summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/core.cpp29
-rw-r--r--src/core/core.h22
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/submission_package.cpp6
-rw-r--r--src/core/file_sys/submission_package.h4
-rw-r--r--src/core/hle/service/am/am.cpp34
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/loader/loader.cpp12
-rw-r--r--src/core/loader/loader.h4
-rw-r--r--src/core/loader/nsp.cpp5
-rw-r--r--src/core/loader/nsp.h3
-rw-r--r--src/core/loader/xci.cpp5
-rw-r--r--src/core/loader/xci.h3
-rwxr-xr-xsrc/input_common/analog_from_button.cpp122
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/main.cpp24
-rw-r--r--src/yuzu/main.h8
19 files changed, 249 insertions, 55 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 1aa477a29..7ca3652af 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -145,7 +145,7 @@ struct System::Impl {
}
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
- LOG_DEBUG(HW_Memory, "initialized OK");
+ LOG_DEBUG(Core, "initialized OK");
device_memory = std::make_unique<Core::DeviceMemory>();
@@ -208,9 +208,11 @@ struct System::Impl {
return ResultStatus::Success;
}
- ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
- const std::string& filepath) {
- app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath));
+ ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
+ std::size_t program_index) {
+ app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
+ program_index);
+
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
@@ -416,6 +418,8 @@ struct System::Impl {
bool is_multicore{};
bool is_async_gpu{};
+ ExecuteProgramCallback execute_program_callback;
+
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
};
@@ -451,8 +455,9 @@ void System::Shutdown() {
impl->Shutdown();
}
-System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
- return impl->Load(*this, emu_window, filepath);
+System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
+ std::size_t program_index) {
+ return impl->Load(*this, emu_window, filepath, program_index);
}
bool System::IsPoweredOn() const {
@@ -789,4 +794,16 @@ bool System::IsMulticore() const {
return impl->is_multicore;
}
+void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
+ impl->execute_program_callback = std::move(callback);
+}
+
+void System::ExecuteProgram(std::size_t program_index) {
+ if (impl->execute_program_callback) {
+ impl->execute_program_callback(program_index);
+ } else {
+ LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend");
+ }
+}
+
} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index cd155625c..f642befc0 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -5,6 +5,7 @@
#pragma once
#include <cstddef>
+#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -173,9 +174,11 @@ public:
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @param filepath String path to the executable application to load on the host file system.
+ * @param program_index Specifies the index within the container of the program to launch.
* @returns ResultStatus code, indicating if the operation succeeded.
*/
- [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath);
+ [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
+ std::size_t program_index = 0);
/**
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@@ -385,6 +388,23 @@ public:
/// Tells if system is running on multicore.
[[nodiscard]] bool IsMulticore() const;
+ /// Type used for the frontend to designate a callback for System to re-launch the application
+ /// using a specified program index.
+ using ExecuteProgramCallback = std::function<void(std::size_t)>;
+
+ /**
+ * Registers a callback from the frontend for System to re-launch the application using a
+ * specified program index.
+ * @param callback Callback from the frontend to relaunch the application.
+ */
+ void RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback);
+
+ /**
+ * Instructs the frontend to re-launch the application using the specified program_index.
+ * @param program_index Specifies the index within the application of the program to launch.
+ */
+ void ExecuteProgram(std::size_t program_index);
+
private:
System();
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 956da68f7..8dee5590b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
"logo",
};
-XCI::XCI(VirtualFile file_)
+XCI::XCI(VirtualFile file_, std::size_t program_index)
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
partitions(partition_names.size()),
partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -62,7 +62,8 @@ XCI::XCI(VirtualFile file_)
}
secure_partition = std::make_shared<NSP>(
- main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
+ main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
+ program_index);
ncas = secure_partition->GetNCAsCollapsed();
program =
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 2d0a0f285..4960e90fe 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
class XCI : public ReadOnlyVfsDirectory {
public:
- explicit XCI(VirtualFile file);
+ explicit XCI(VirtualFile file, std::size_t program_index = 0);
~XCI() override;
Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 90641d23b..c05735ddd 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,8 @@
namespace FileSys {
-NSP::NSP(VirtualFile file_)
- : file(std::move(file_)), status{Loader::ResultStatus::Success},
+NSP::NSP(VirtualFile file_, std::size_t program_index)
+ : file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success},
pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
if (pfs->GetStatus() != Loader::ResultStatus::Success) {
status = pfs->GetStatus();
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
- const auto title_id_iter = ncas.find(title_id);
+ const auto title_id_iter = ncas.find(title_id + program_index);
if (title_id_iter == ncas.end())
return nullptr;
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index c70a11b5b..54581a6f3 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -27,7 +27,7 @@ enum class ContentRecordType : u8;
class NSP : public ReadOnlyVfsDirectory {
public:
- explicit NSP(VirtualFile file);
+ explicit NSP(VirtualFile file, std::size_t program_index = 0);
~NSP() override;
Loader::ResultStatus GetStatus() const;
@@ -69,6 +69,8 @@ private:
VirtualFile file;
+ const std::size_t program_index;
+
bool extracted = false;
Loader::ResultStatus status;
std::map<u64, Loader::ResultStatus> program_status;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 55e428456..703a9b234 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1188,9 +1188,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
{110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
{111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
- {120, nullptr, "ExecuteProgram"},
- {121, nullptr, "ClearUserChannel"},
- {122, nullptr, "UnpopToUserChannel"},
+ {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
+ {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
+ {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
{123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
{124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
@@ -1561,6 +1561,34 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
rb.Push<u32>(0);
}
+void IApplicationFunctions::ExecuteProgram(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::RequestParser rp{ctx};
+ [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
+ [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
+ const auto program_index = rp.Pop<u64>();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+
+ system.ExecuteProgram(program_index);
+}
+
+void IApplicationFunctions::ClearUserChannel(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void IApplicationFunctions::UnpopToUserChannel(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 09c2d05bc..af97c303a 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -287,6 +287,9 @@ private:
void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
+ void ExecuteProgram(Kernel::HLERequestContext& ctx);
+ void ClearUserChannel(Kernel::HLERequestContext& ctx);
+ void UnpopToUserChannel(Kernel::HLERequestContext& ctx);
void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index deffe7379..d91c15561 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -198,10 +198,11 @@ AppLoader::~AppLoader() = default;
* @param system The system context to use.
* @param file The file to retrieve the loader for
* @param type The file type
+ * @param program_index Specifies the index within the container of the program to launch.
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
*/
static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
- FileType type) {
+ FileType type, std::size_t program_index) {
switch (type) {
// Standard ELF file format.
case FileType::ELF:
@@ -222,7 +223,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
// NX XCI (nX Card Image) file format.
case FileType::XCI:
return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
- system.GetContentProvider());
+ system.GetContentProvider(), program_index);
// NX NAX (NintendoAesXts) file format.
case FileType::NAX:
@@ -231,7 +232,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
// NX NSP (Nintendo Submission Package) file format
case FileType::NSP:
return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
- system.GetContentProvider());
+ system.GetContentProvider(), program_index);
// NX KIP (Kernel Internal Process) file format
case FileType::KIP:
@@ -246,7 +247,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
}
}
-std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file) {
+std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
+ std::size_t program_index) {
FileType type = IdentifyFile(file);
const FileType filename_type = GuessFromFilename(file->GetName());
@@ -260,7 +262,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
- return GetFileLoader(system, std::move(file), type);
+ return GetFileLoader(system, std::move(file), type, program_index);
}
} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 8dc2d7615..36e79e71d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -293,9 +293,11 @@ protected:
*
* @param system The system context.
* @param file The bootable file.
+ * @param program_index Specifies the index within the container of the program to launch.
*
* @return the best loader for this file.
*/
-std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file);
+std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
+ std::size_t program_index = 0);
} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index e821937fd..928f64c8c 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -23,8 +23,9 @@ namespace Loader {
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
- const FileSys::ContentProvider& content_provider)
- : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
+ const FileSys::ContentProvider& content_provider,
+ std::size_t program_index)
+ : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)),
title_id(nsp->GetProgramTitleID()) {
if (nsp->GetStatus() != ResultStatus::Success) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 36e8e3533..f0518ac47 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -28,7 +28,8 @@ class AppLoader_NSP final : public AppLoader {
public:
explicit AppLoader_NSP(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
- const FileSys::ContentProvider& content_provider);
+ const FileSys::ContentProvider& content_provider,
+ std::size_t program_index);
~AppLoader_NSP() override;
/**
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 536e721fc..aaa250cea 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -22,8 +22,9 @@ namespace Loader {
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
- const FileSys::ContentProvider& content_provider)
- : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
+ const FileSys::ContentProvider& content_provider,
+ std::size_t program_index)
+ : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, program_index)),
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
if (xci->GetStatus() != ResultStatus::Success) {
return;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 6dc1f9243..764dc8328 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -28,7 +28,8 @@ class AppLoader_XCI final : public AppLoader {
public:
explicit AppLoader_XCI(FileSys::VirtualFile file,
const Service::FileSystem::FileSystemController& fsc,
- const FileSys::ContentProvider& content_provider);
+ const FileSys::ContentProvider& content_provider,
+ std::size_t program_index);
~AppLoader_XCI() override;
/**
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index 74744d7f3..d748c1c04 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -2,6 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <chrono>
+#include <cmath>
+#include <thread>
+#include "common/math_util.h"
#include "input_common/analog_from_button.h"
namespace InputCommon {
@@ -11,31 +15,104 @@ public:
using Button = std::unique_ptr<Input::ButtonDevice>;
Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
- float modifier_scale_)
+ float modifier_scale_, float modifier_angle_)
: up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
- right(std::move(right_)), modifier(std::move(modifier_)),
- modifier_scale(modifier_scale_) {}
-
- std::tuple<float, float> GetStatus() const override {
- constexpr float SQRT_HALF = 0.707106781f;
- int x = 0, y = 0;
+ right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
+ modifier_angle(modifier_angle_) {
+ update_thread = std::thread(&Analog::UpdateStatus, this);
+ }
- if (right->GetStatus()) {
- ++x;
+ ~Analog() override {
+ update_thread_running = false;
+ if (update_thread.joinable()) {
+ update_thread.join();
}
- if (left->GetStatus()) {
- --x;
+ }
+
+ void MoveToDirection(bool enable, float to_angle) {
+ if (!enable) {
+ return;
}
- if (up->GetStatus()) {
- ++y;
+ constexpr float TAU = Common::PI * 2.0f;
+ // Use wider angle to ease the transition.
+ constexpr float aperture = TAU * 0.15f;
+ const float top_limit = to_angle + aperture;
+ const float bottom_limit = to_angle - aperture;
+
+ if ((angle > to_angle && angle <= top_limit) ||
+ (angle + TAU > to_angle && angle + TAU <= top_limit)) {
+ angle -= modifier_angle;
+ if (angle < 0) {
+ angle += TAU;
+ }
+ } else if ((angle >= bottom_limit && angle < to_angle) ||
+ (angle - TAU >= bottom_limit && angle - TAU < to_angle)) {
+ angle += modifier_angle;
+ if (angle >= TAU) {
+ angle -= TAU;
+ }
+ } else {
+ angle = to_angle;
}
- if (down->GetStatus()) {
- --y;
+ }
+
+ void UpdateStatus() {
+ while (update_thread_running) {
+ const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
+
+ bool r = right->GetStatus();
+ bool l = left->GetStatus();
+ bool u = up->GetStatus();
+ bool d = down->GetStatus();
+
+ // Eliminate contradictory movements
+ if (r && l) {
+ r = false;
+ l = false;
+ }
+ if (u && d) {
+ u = false;
+ d = false;
+ }
+
+ // Move to the right
+ MoveToDirection(r && !u && !d, 0.0f);
+
+ // Move to the upper right
+ MoveToDirection(r && u && !d, Common::PI * 0.25f);
+
+ // Move up
+ MoveToDirection(u && !l && !r, Common::PI * 0.5f);
+
+ // Move to the upper left
+ MoveToDirection(l && u && !d, Common::PI * 0.75f);
+
+ // Move to the left
+ MoveToDirection(l && !u && !d, Common::PI);
+
+ // Move to the bottom left
+ MoveToDirection(l && !u && d, Common::PI * 1.25f);
+
+ // Move down
+ MoveToDirection(d && !l && !r, Common::PI * 1.5f);
+
+ // Move to the bottom right
+ MoveToDirection(r && !u && d, Common::PI * 1.75f);
+
+ // Move if a key is pressed
+ if (r || l || u || d) {
+ amplitude = coef;
+ } else {
+ amplitude = 0;
+ }
+
+ // Delay the update rate to 100hz
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
+ }
- const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
- return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
- static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
+ std::tuple<float, float> GetStatus() const override {
+ return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude);
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
@@ -59,6 +136,11 @@ private:
Button right;
Button modifier;
float modifier_scale;
+ float modifier_angle;
+ float angle{};
+ float amplitude{};
+ std::thread update_thread;
+ bool update_thread_running{true};
};
std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
@@ -69,8 +151,10 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para
auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
auto modifier_scale = params.Get("modifier_scale", 0.5f);
+ auto modifier_angle = params.Get("modifier_angle", 0.035f);
return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
- std::move(right), std::move(modifier), modifier_scale);
+ std::move(right), std::move(modifier), modifier_scale,
+ modifier_angle);
}
} // namespace InputCommon
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d62b0efc2..f0338cf7a 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -302,6 +302,12 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
this->setMouseTracking(true);
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
+ connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
+ Qt::QueuedConnection);
+}
+
+void GRenderWindow::ExecuteProgram(std::size_t program_index) {
+ emit ExecuteProgramSignal(program_index);
}
GRenderWindow::~GRenderWindow() {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index ca35cf831..503b4f89e 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -166,6 +166,12 @@ public:
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
+ /**
+ * Instructs the window to re-launch the application using the specified program_index.
+ * @param program_index Specifies the index within the application of the program to launch.
+ */
+ void ExecuteProgram(std::size_t program_index);
+
public slots:
void OnEmulationStarting(EmuThread* emu_thread);
void OnEmulationStopping();
@@ -175,6 +181,7 @@ signals:
/// Emitted when the window is closed
void Closed();
void FirstFrameDisplayed();
+ void ExecuteProgramSignal(std::size_t program_index);
private:
void TouchBeginEvent(const QTouchEvent* event);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e704cc656..805619ccf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -978,7 +978,7 @@ void GMainWindow::AllowOSSleep() {
#endif
}
-bool GMainWindow::LoadROM(const QString& filename) {
+bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
@@ -1003,7 +1003,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
system.RegisterHostThread();
- const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
+ const Core::System::ResultStatus result{
+ system.Load(*render_window, filename.toStdString(), program_index)};
const auto drd_callout =
(UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1085,14 +1086,18 @@ void GMainWindow::SelectAndSetCurrentUser() {
Settings::values.current_user = dialog.GetIndex();
}
-void GMainWindow::BootGame(const QString& filename) {
+void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
LOG_INFO(Frontend, "yuzu starting...");
StoreRecentFile(filename); // Put the filename on top of the list
u64 title_id{0};
+
+ last_filename_booted = filename;
+
auto& system = Core::System::GetInstance();
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
- const auto loader = Loader::GetLoader(system, v_file);
+ const auto loader = Loader::GetLoader(system, v_file, program_index);
+
if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
// Load per game settings
Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
@@ -1106,7 +1111,7 @@ void GMainWindow::BootGame(const QString& filename) {
SelectAndSetCurrentUser();
}
- if (!LoadROM(filename))
+ if (!LoadROM(filename, program_index))
return;
// Create and start the emulation thread
@@ -1114,6 +1119,10 @@ void GMainWindow::BootGame(const QString& filename) {
emit EmulationStarting(emu_thread.get());
emu_thread->start();
+ // Register an ExecuteProgram callback such that Core can execute a sub-program
+ system.RegisterExecuteProgramCallback(
+ [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
+
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
// before the CPU continues
@@ -2136,6 +2145,11 @@ void GMainWindow::OnLoadComplete() {
loading_screen->OnLoadComplete();
}
+void GMainWindow::OnExecuteProgram(std::size_t program_index) {
+ ShutdownGame();
+ BootGame(last_filename_booted, program_index);
+}
+
void GMainWindow::ErrorDisplayDisplayError(QString body) {
QMessageBox::critical(this, tr("Error Display"), body);
emit ErrorDisplayFinished();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b380a66f3..6242341d1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -131,6 +131,7 @@ signals:
public slots:
void OnLoadComplete();
+ void OnExecuteProgram(std::size_t program_index);
void ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters);
void ErrorDisplayDisplayError(QString body);
@@ -154,8 +155,8 @@ private:
void PreventOSSleep();
void AllowOSSleep();
- bool LoadROM(const QString& filename);
- void BootGame(const QString& filename);
+ bool LoadROM(const QString& filename, std::size_t program_index);
+ void BootGame(const QString& filename, std::size_t program_index = 0);
void ShutdownGame();
void ShowTelemetryCallout();
@@ -317,6 +318,9 @@ private:
// Install progress dialog
QProgressDialog* install_progress;
+ // Last game booted, used for multi-process apps
+ QString last_filename_booted;
+
protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;