early-access version 3837
This commit is contained in:
parent
d07529eada
commit
c807f0cfc8
17 changed files with 252 additions and 94 deletions
|
@ -1,7 +1,7 @@
|
|||
yuzu emulator early access
|
||||
=============
|
||||
|
||||
This is the source code for early-access 3836.
|
||||
This is the source code for early-access 3837.
|
||||
|
||||
## Legal Notice
|
||||
|
||||
|
|
5
dist/qt_themes/default/style.qss
vendored
5
dist/qt_themes/default/style.qss
vendored
|
@ -78,6 +78,11 @@ QPushButton#buttonRefreshDevices {
|
|||
max-height: 21px;
|
||||
}
|
||||
|
||||
QPushButton#button_reset_defaults {
|
||||
min-width: 57px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
QWidget#bottomPerGameInput,
|
||||
QWidget#topControllerApplet,
|
||||
QWidget#bottomControllerApplet,
|
||||
|
|
|
@ -2228,6 +2228,10 @@ QPushButton#buttonRefreshDevices {
|
|||
padding: 0px 0px;
|
||||
}
|
||||
|
||||
QPushButton#button_reset_defaults {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
QSpinBox#spinboxLStickRange,
|
||||
QSpinBox#spinboxRStickRange,
|
||||
QSpinBox#vibrationSpinPlayer1,
|
||||
|
|
14
externals/CMakeLists.txt
vendored
14
externals/CMakeLists.txt
vendored
|
@ -42,6 +42,11 @@ endif()
|
|||
# mbedtls
|
||||
add_subdirectory(mbedtls)
|
||||
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
|
||||
add_library(microprofile INTERFACE)
|
||||
|
@ -94,6 +99,12 @@ if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb)
|
|||
set(BUILD_TOOLS OFF)
|
||||
add_subdirectory(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()
|
||||
|
||||
# DiscordRPC
|
||||
|
@ -151,6 +162,9 @@ endif()
|
|||
if (NOT TARGET LLVM::Demangle)
|
||||
add_library(demangle demangle/ItaniumDemangle.cpp)
|
||||
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)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -114,16 +114,19 @@ else()
|
|||
-Wno-attributes
|
||||
-Wno-invalid-offsetof
|
||||
-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)
|
||||
add_compile_options("-mcx16")
|
||||
add_compile_options("-fwrapv")
|
||||
|
|
|
@ -460,11 +460,6 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
|
|||
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
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator<(const S& p, const swap_struct_t<T, F> v) {
|
||||
|
|
|
@ -273,7 +273,8 @@ struct System::Impl {
|
|||
time_manager.Initialize();
|
||||
|
||||
is_powered_on = true;
|
||||
exit_lock = false;
|
||||
exit_locked = false;
|
||||
exit_requested = false;
|
||||
|
||||
microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0);
|
||||
microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1);
|
||||
|
@ -398,12 +399,14 @@ struct System::Impl {
|
|||
}
|
||||
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
exit_locked = false;
|
||||
exit_requested = false;
|
||||
|
||||
if (gpu_core != nullptr) {
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
Network::CancelPendingSocketOperations();
|
||||
kernel.SuspendApplication(true);
|
||||
if (services) {
|
||||
services->KillNVNFlinger();
|
||||
|
@ -425,6 +428,7 @@ struct System::Impl {
|
|||
debugger.reset();
|
||||
kernel.Shutdown();
|
||||
memory.Reset();
|
||||
Network::RestartSocketOperations();
|
||||
|
||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||
Network::GameInfo game_info{};
|
||||
|
@ -507,7 +511,8 @@ struct System::Impl {
|
|||
|
||||
CpuManager cpu_manager;
|
||||
std::atomic_bool is_powered_on{};
|
||||
bool exit_lock = false;
|
||||
bool exit_locked = false;
|
||||
bool exit_requested = false;
|
||||
|
||||
bool nvdec_active{};
|
||||
|
||||
|
@ -943,12 +948,20 @@ const Service::Time::TimeManager& System::GetTimeManager() const {
|
|||
return impl->time_manager;
|
||||
}
|
||||
|
||||
void System::SetExitLock(bool locked) {
|
||||
impl->exit_lock = locked;
|
||||
void System::SetExitLocked(bool locked) {
|
||||
impl->exit_locked = locked;
|
||||
}
|
||||
|
||||
bool System::GetExitLock() const {
|
||||
return impl->exit_lock;
|
||||
bool System::GetExitLocked() const {
|
||||
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) {
|
||||
|
|
|
@ -412,8 +412,11 @@ public:
|
|||
/// Gets an immutable reference to the Room Network.
|
||||
[[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
|
||||
|
||||
void SetExitLock(bool locked);
|
||||
[[nodiscard]] bool GetExitLock() const;
|
||||
void SetExitLocked(bool locked);
|
||||
bool GetExitLocked() const;
|
||||
|
||||
void SetExitRequested(bool requested);
|
||||
bool GetExitRequested() const;
|
||||
|
||||
void SetApplicationProcessBuildID(const CurrentBuildProcessID& id);
|
||||
[[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const;
|
||||
|
|
|
@ -341,7 +341,7 @@ void ISelfController::Exit(HLERequestContext& ctx) {
|
|||
void ISelfController::LockExit(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
system.SetExitLock(true);
|
||||
system.SetExitLocked(true);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -350,10 +350,14 @@ void ISelfController::LockExit(HLERequestContext& ctx) {
|
|||
void ISelfController::UnlockExit(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
system.SetExitLock(false);
|
||||
system.SetExitLocked(false);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
if (system.GetExitRequested()) {
|
||||
system.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
void ISelfController::EnterFatalSection(HLERequestContext& ctx) {
|
||||
|
|
|
@ -48,15 +48,32 @@ enum class CallType {
|
|||
|
||||
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() {
|
||||
WSADATA wsa_data;
|
||||
(void)WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
|
||||
AcknowledgeInterrupt();
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
InterruptSocketOperations();
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
SOCKET GetInterruptSocket() {
|
||||
return interrupt_socket;
|
||||
}
|
||||
|
||||
sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
|
||||
sockaddr_in result;
|
||||
|
||||
|
@ -157,9 +174,39 @@ constexpr int SD_RECEIVE = SHUT_RD;
|
|||
constexpr int SD_SEND = SHUT_WR;
|
||||
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_in result;
|
||||
|
@ -490,6 +537,14 @@ NetworkInstance::~NetworkInstance() {
|
|||
Finalize();
|
||||
}
|
||||
|
||||
void CancelPendingSocketOperations() {
|
||||
InterruptSocketOperations();
|
||||
}
|
||||
|
||||
void RestartSocketOperations() {
|
||||
AcknowledgeInterrupt();
|
||||
}
|
||||
|
||||
std::optional<IPv4Address> GetHostIPv4Address() {
|
||||
const auto network_interface = Network::GetSelectedNetworkInterface();
|
||||
if (!network_interface.has_value()) {
|
||||
|
@ -560,7 +615,14 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
|
|||
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) {
|
||||
ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(),
|
||||
[](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() {
|
||||
sockaddr_in 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);
|
||||
|
||||
if (new_socket == INVALID_SOCKET) {
|
||||
|
|
|
@ -96,6 +96,9 @@ public:
|
|||
~NetworkInstance();
|
||||
};
|
||||
|
||||
void CancelPendingSocketOperations();
|
||||
void RestartSocketOperations();
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr IPv4Address TranslateIPv4(in_addr addr) {
|
||||
auto& bytes = addr.S_un.S_un_b;
|
||||
|
|
|
@ -835,15 +835,15 @@ public:
|
|||
return input_engine->SupportsNfc(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState StartNfcPolling() {
|
||||
Common::Input::NfcState StartNfcPolling() override {
|
||||
return input_engine->StartNfcPolling(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState StopNfcPolling() {
|
||||
Common::Input::NfcState StopNfcPolling() override {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -852,11 +852,11 @@ public:
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,14 +27,24 @@ MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192))
|
|||
|
||||
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 macro_dir{base_dir / "macros"};
|
||||
if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
|
||||
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);
|
||||
if (!macro_file) {
|
||||
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()) {
|
||||
cache_info.lle_program = Compile(macro_code->second);
|
||||
cache_info.hash = Common::HashValue(macro_code->second);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second);
|
||||
}
|
||||
} else {
|
||||
const auto& macro_cached = uploaded_macro_code[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));
|
||||
cache_info.hash = Common::HashValue(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);
|
||||
|
@ -117,6 +121,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
|||
MICROPROFILE_SCOPE(MacroHLE);
|
||||
cache_info.hle_program->Execute(parameters, method);
|
||||
}
|
||||
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second, cache_info.has_hle_program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -611,9 +611,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
|||
|
||||
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);
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(hash, key.unique_hashes[index]);
|
||||
}
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
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);
|
||||
}
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(hash, key.unique_hashes[index]);
|
||||
}
|
||||
|
||||
if (programs[index].info.requires_layer_emulation) {
|
||||
layer_source_program = &programs[index];
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // for deprecated OpenSSL functions
|
||||
#endif
|
||||
#include <jwt/jwt.hpp>
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
|
|
|
@ -2010,8 +2010,16 @@ bool GMainWindow::OnShutdownBegin() {
|
|||
|
||||
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.start(system->DebuggerEnabled() ? 0 : 5000);
|
||||
shutdown_timer.start(shutdown_time);
|
||||
connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
|
||||
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;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile base_romfs;
|
||||
if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) {
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
FileSys::VirtualFile packed_update_raw{};
|
||||
loader->ReadUpdateRaw(packed_update_raw);
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program
|
||||
: FileSys::ContentRecordType::Data;
|
||||
const auto base_nca = installed.GetEntry(*romfs_title_id, type);
|
||||
const auto type = static_cast<FileSys::ContentRecordType>(raw_type);
|
||||
const auto base_nca = installed.GetEntry(title_id, type);
|
||||
if (!base_nca) {
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto base_romfs = base_nca->GetRomFS();
|
||||
if (!base_romfs) {
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto dump_dir =
|
||||
target == DumpRomFSTarget::Normal
|
||||
? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)
|
||||
: 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);
|
||||
|
||||
FileSys::VirtualFile romfs;
|
||||
|
||||
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 FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
|
||||
auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
|
||||
if (extracted == nullptr) {
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto full = res == selections.constFirst();
|
||||
const auto entry_size = CalculateRomFSEntrySize(extracted, full);
|
||||
|
||||
|
@ -3261,7 +3266,7 @@ void GMainWindow::OnPauseContinueGame() {
|
|||
}
|
||||
|
||||
void GMainWindow::OnStopGame() {
|
||||
if (system->GetExitLock() && !ConfirmForceLockedExit()) {
|
||||
if (system->GetExitLocked() && !ConfirmForceLockedExit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4350,28 +4355,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
|
|||
return mii_nca->GetRomFS().get() != nullptr;
|
||||
}
|
||||
|
||||
std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed,
|
||||
u64 program_id) {
|
||||
const auto dlc_entries =
|
||||
installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
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;
|
||||
});
|
||||
bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
|
||||
u64* selected_title_id, u8* selected_content_record_type) {
|
||||
using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>;
|
||||
boost::container::flat_map<u64, ContentInfo> available_title_ids;
|
||||
|
||||
std::vector<u64> romfs_tids;
|
||||
romfs_tids.push_back(program_id);
|
||||
for (const auto& entry : dlc_match) {
|
||||
romfs_tids.push_back(entry.title_id);
|
||||
const auto RetrieveEntries = [&](FileSys::TitleType title_type,
|
||||
FileSys::ContentRecordType record_type) {
|
||||
const auto entries = installed.ListEntriesFilter(title_type, record_type);
|
||||
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) {
|
||||
QStringList list{QStringLiteral("Base")};
|
||||
for (std::size_t i = 1; i < romfs_tids.size(); ++i) {
|
||||
list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
|
||||
size_t title_index = 0;
|
||||
|
||||
if (available_title_ids.size() > 1) {
|
||||
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;
|
||||
|
@ -4379,13 +4397,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv
|
|||
this, tr("Select RomFS Dump Target"),
|
||||
tr("Please select which RomFS you would like to dump."), list, 0, false, &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() {
|
||||
|
@ -4515,6 +4536,8 @@ void GMainWindow::RequestGameExit() {
|
|||
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||
bool has_signalled = false;
|
||||
|
||||
system->SetExitRequested(true);
|
||||
|
||||
if (applet_oe != nullptr) {
|
||||
applet_oe->GetMessageQueue()->RequestExit();
|
||||
has_signalled = true;
|
||||
|
|
|
@ -375,7 +375,8 @@ private:
|
|||
void RemoveAllTransferableShaderCaches(u64 program_id);
|
||||
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
|
||||
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 InstallNCA(const QString& filename);
|
||||
void MigrateConfigFiles();
|
||||
|
|
Loading…
Reference in a new issue