|
|
@@ -33,7 +33,6 @@
|
|
|
#include "core/crypto/key_manager.h"
|
|
|
#include "core/file_sys/card_image.h"
|
|
|
#include "core/file_sys/content_archive.h"
|
|
|
-#include "core/file_sys/registered_cache.h"
|
|
|
#include "core/file_sys/submission_package.h"
|
|
|
#include "core/file_sys/vfs.h"
|
|
|
#include "core/file_sys/vfs_real.h"
|
|
|
@@ -48,514 +47,416 @@
|
|
|
#include "core/hid/emulated_controller.h"
|
|
|
#include "core/hid/hid_core.h"
|
|
|
#include "core/hid/hid_types.h"
|
|
|
-#include "core/hle/service/acc/profile_manager.h"
|
|
|
#include "core/hle/service/am/applet_ae.h"
|
|
|
#include "core/hle/service/am/applet_oe.h"
|
|
|
#include "core/hle/service/am/applets/applets.h"
|
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
|
#include "core/loader/loader.h"
|
|
|
-#include "core/perf_stats.h"
|
|
|
#include "jni/android_common/android_common.h"
|
|
|
-#include "jni/applets/software_keyboard.h"
|
|
|
#include "jni/config.h"
|
|
|
-#include "jni/emu_window/emu_window.h"
|
|
|
#include "jni/id_cache.h"
|
|
|
-#include "video_core/rasterizer_interface.h"
|
|
|
+#include "jni/native.h"
|
|
|
#include "video_core/renderer_base.h"
|
|
|
|
|
|
#define jconst [[maybe_unused]] const auto
|
|
|
#define jauto [[maybe_unused]] auto
|
|
|
|
|
|
-namespace {
|
|
|
+static EmulationSession s_instance;
|
|
|
|
|
|
-class EmulationSession final {
|
|
|
-public:
|
|
|
- EmulationSession() {
|
|
|
- m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
|
|
|
- }
|
|
|
-
|
|
|
- ~EmulationSession() = default;
|
|
|
-
|
|
|
- static EmulationSession& GetInstance() {
|
|
|
- return s_instance;
|
|
|
- }
|
|
|
-
|
|
|
- const Core::System& System() const {
|
|
|
- return m_system;
|
|
|
- }
|
|
|
+EmulationSession::EmulationSession() {
|
|
|
+ m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
|
|
|
+}
|
|
|
|
|
|
- Core::System& System() {
|
|
|
- return m_system;
|
|
|
- }
|
|
|
+EmulationSession& EmulationSession::GetInstance() {
|
|
|
+ return s_instance;
|
|
|
+}
|
|
|
|
|
|
- const EmuWindow_Android& Window() const {
|
|
|
- return *m_window;
|
|
|
- }
|
|
|
+const Core::System& EmulationSession::System() const {
|
|
|
+ return m_system;
|
|
|
+}
|
|
|
|
|
|
- EmuWindow_Android& Window() {
|
|
|
- return *m_window;
|
|
|
- }
|
|
|
+Core::System& EmulationSession::System() {
|
|
|
+ return m_system;
|
|
|
+}
|
|
|
|
|
|
- ANativeWindow* NativeWindow() const {
|
|
|
- return m_native_window;
|
|
|
- }
|
|
|
+const EmuWindow_Android& EmulationSession::Window() const {
|
|
|
+ return *m_window;
|
|
|
+}
|
|
|
|
|
|
- void SetNativeWindow(ANativeWindow* native_window) {
|
|
|
- m_native_window = native_window;
|
|
|
- }
|
|
|
+EmuWindow_Android& EmulationSession::Window() {
|
|
|
+ return *m_window;
|
|
|
+}
|
|
|
|
|
|
- int InstallFileToNand(std::string filename, std::string file_extension) {
|
|
|
- jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
|
|
- std::size_t block_size) {
|
|
|
- if (src == nullptr || dest == nullptr) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (!dest->Resize(src->GetSize())) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ANativeWindow* EmulationSession::NativeWindow() const {
|
|
|
+ return m_native_window;
|
|
|
+}
|
|
|
|
|
|
- using namespace Common::Literals;
|
|
|
- [[maybe_unused]] std::vector<u8> buffer(1_MiB);
|
|
|
+void EmulationSession::SetNativeWindow(ANativeWindow* native_window) {
|
|
|
+ m_native_window = native_window;
|
|
|
+}
|
|
|
|
|
|
- for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
|
|
- jconst read = src->Read(buffer.data(), buffer.size(), i);
|
|
|
- dest->Write(buffer.data(), read, i);
|
|
|
- }
|
|
|
- return true;
|
|
|
- };
|
|
|
-
|
|
|
- enum InstallResult {
|
|
|
- Success = 0,
|
|
|
- SuccessFileOverwritten = 1,
|
|
|
- InstallError = 2,
|
|
|
- ErrorBaseGame = 3,
|
|
|
- ErrorFilenameExtension = 4,
|
|
|
- };
|
|
|
-
|
|
|
- m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
|
|
- m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
|
|
-
|
|
|
- [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
|
|
|
- if (file_extension == "nsp") {
|
|
|
- nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
|
|
- if (nsp->IsExtractedType()) {
|
|
|
- return InstallError;
|
|
|
- }
|
|
|
- } else {
|
|
|
- return ErrorFilenameExtension;
|
|
|
+int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) {
|
|
|
+ jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
|
|
+ std::size_t block_size) {
|
|
|
+ if (src == nullptr || dest == nullptr) {
|
|
|
+ return false;
|
|
|
}
|
|
|
-
|
|
|
- if (!nsp) {
|
|
|
- return InstallError;
|
|
|
+ if (!dest->Resize(src->GetSize())) {
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- if (nsp->GetStatus() != Loader::ResultStatus::Success) {
|
|
|
- return InstallError;
|
|
|
- }
|
|
|
+ using namespace Common::Literals;
|
|
|
+ [[maybe_unused]] std::vector<u8> buffer(1_MiB);
|
|
|
|
|
|
- jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
|
|
|
- *nsp, true, copy_func);
|
|
|
-
|
|
|
- switch (res) {
|
|
|
- case FileSys::InstallResult::Success:
|
|
|
- return Success;
|
|
|
- case FileSys::InstallResult::OverwriteExisting:
|
|
|
- return SuccessFileOverwritten;
|
|
|
- case FileSys::InstallResult::ErrorBaseInstall:
|
|
|
- return ErrorBaseGame;
|
|
|
- default:
|
|
|
- return InstallError;
|
|
|
+ for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
|
|
+ jconst read = src->Read(buffer.data(), buffer.size(), i);
|
|
|
+ dest->Write(buffer.data(), read, i);
|
|
|
}
|
|
|
- }
|
|
|
+ return true;
|
|
|
+ };
|
|
|
|
|
|
- void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
|
|
- const std::string& custom_driver_name,
|
|
|
- const std::string& file_redirect_dir) {
|
|
|
-#ifdef ARCHITECTURE_arm64
|
|
|
- void* handle{};
|
|
|
- const char* file_redirect_dir_{};
|
|
|
- int featureFlags{};
|
|
|
-
|
|
|
- // Enable driver file redirection when renderer debugging is enabled.
|
|
|
- if (Settings::values.renderer_debug && file_redirect_dir.size()) {
|
|
|
- featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
|
|
|
- file_redirect_dir_ = file_redirect_dir.c_str();
|
|
|
- }
|
|
|
+ enum InstallResult {
|
|
|
+ Success = 0,
|
|
|
+ SuccessFileOverwritten = 1,
|
|
|
+ InstallError = 2,
|
|
|
+ ErrorBaseGame = 3,
|
|
|
+ ErrorFilenameExtension = 4,
|
|
|
+ };
|
|
|
|
|
|
- // Try to load a custom driver.
|
|
|
- if (custom_driver_name.size()) {
|
|
|
- handle = adrenotools_open_libvulkan(
|
|
|
- RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
|
|
|
- custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
|
|
|
- }
|
|
|
+ m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
|
|
+ m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
|
|
|
|
|
- // Try to load the system driver.
|
|
|
- if (!handle) {
|
|
|
- handle =
|
|
|
- adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
|
|
- nullptr, nullptr, file_redirect_dir_, nullptr);
|
|
|
+ [[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
|
|
|
+ if (file_extension == "nsp") {
|
|
|
+ nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
|
|
+ if (nsp->IsExtractedType()) {
|
|
|
+ return InstallError;
|
|
|
}
|
|
|
-
|
|
|
- m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
|
|
|
-#endif
|
|
|
+ } else {
|
|
|
+ return ErrorFilenameExtension;
|
|
|
}
|
|
|
|
|
|
- bool IsRunning() const {
|
|
|
- return m_is_running;
|
|
|
+ if (!nsp) {
|
|
|
+ return InstallError;
|
|
|
}
|
|
|
|
|
|
- bool IsPaused() const {
|
|
|
- return m_is_running && m_is_paused;
|
|
|
+ if (nsp->GetStatus() != Loader::ResultStatus::Success) {
|
|
|
+ return InstallError;
|
|
|
}
|
|
|
|
|
|
- const Core::PerfStatsResults& PerfStats() const {
|
|
|
- std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
|
|
- return m_perf_stats;
|
|
|
- }
|
|
|
+ jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true,
|
|
|
+ copy_func);
|
|
|
|
|
|
- void SurfaceChanged() {
|
|
|
- if (!IsRunning()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- m_window->OnSurfaceChanged(m_native_window);
|
|
|
+ switch (res) {
|
|
|
+ case FileSys::InstallResult::Success:
|
|
|
+ return Success;
|
|
|
+ case FileSys::InstallResult::OverwriteExisting:
|
|
|
+ return SuccessFileOverwritten;
|
|
|
+ case FileSys::InstallResult::ErrorBaseInstall:
|
|
|
+ return ErrorBaseGame;
|
|
|
+ default:
|
|
|
+ return InstallError;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- void ConfigureFilesystemProvider(const std::string& filepath) {
|
|
|
- const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
|
|
- if (!file) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- auto loader = Loader::GetLoader(m_system, file);
|
|
|
- if (!loader) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const auto file_type = loader->GetFileType();
|
|
|
- if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
|
|
- return;
|
|
|
- }
|
|
|
+void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
|
|
|
+ const std::string& custom_driver_dir,
|
|
|
+ const std::string& custom_driver_name,
|
|
|
+ const std::string& file_redirect_dir) {
|
|
|
+#ifdef ARCHITECTURE_arm64
|
|
|
+ void* handle{};
|
|
|
+ const char* file_redirect_dir_{};
|
|
|
+ int featureFlags{};
|
|
|
|
|
|
- u64 program_id = 0;
|
|
|
- const auto res2 = loader->ReadProgramId(program_id);
|
|
|
- if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
|
|
- m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
|
|
- FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
|
|
- program_id, file);
|
|
|
- } else if (res2 == Loader::ResultStatus::Success &&
|
|
|
- (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
|
|
- const auto nsp = file_type == Loader::FileType::NSP
|
|
|
- ? std::make_shared<FileSys::NSP>(file)
|
|
|
- : FileSys::XCI{file}.GetSecurePartitionNSP();
|
|
|
- for (const auto& title : nsp->GetNCAs()) {
|
|
|
- for (const auto& entry : title.second) {
|
|
|
- m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
|
|
- entry.second->GetBaseFile());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // Enable driver file redirection when renderer debugging is enabled.
|
|
|
+ if (Settings::values.renderer_debug && file_redirect_dir.size()) {
|
|
|
+ featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
|
|
|
+ file_redirect_dir_ = file_redirect_dir.c_str();
|
|
|
}
|
|
|
|
|
|
- Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
|
|
|
- std::scoped_lock lock(m_mutex);
|
|
|
-
|
|
|
- // Create the render window.
|
|
|
- m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
|
|
|
- m_vulkan_library);
|
|
|
-
|
|
|
- m_system.SetFilesystem(m_vfs);
|
|
|
- m_system.GetUserChannel().clear();
|
|
|
-
|
|
|
- // Initialize system.
|
|
|
- jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
|
|
|
- m_software_keyboard = android_keyboard.get();
|
|
|
- m_system.SetShuttingDown(false);
|
|
|
- m_system.ApplySettings();
|
|
|
- Settings::LogSettings();
|
|
|
- m_system.HIDCore().ReloadInputDevices();
|
|
|
- m_system.SetAppletFrontendSet({
|
|
|
- nullptr, // Amiibo Settings
|
|
|
- nullptr, // Controller Selector
|
|
|
- nullptr, // Error Display
|
|
|
- nullptr, // Mii Editor
|
|
|
- nullptr, // Parental Controls
|
|
|
- nullptr, // Photo Viewer
|
|
|
- nullptr, // Profile Selector
|
|
|
- std::move(android_keyboard), // Software Keyboard
|
|
|
- nullptr, // Web Browser
|
|
|
- });
|
|
|
-
|
|
|
- // Initialize filesystem.
|
|
|
- m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
|
|
|
- m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
|
|
- m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
|
|
- m_manual_provider.get());
|
|
|
- m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
|
|
- ConfigureFilesystemProvider(filepath);
|
|
|
-
|
|
|
- // Initialize account manager
|
|
|
- m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
|
|
-
|
|
|
- // Load the ROM.
|
|
|
- m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
|
|
|
- if (m_load_result != Core::SystemResultStatus::Success) {
|
|
|
- return m_load_result;
|
|
|
- }
|
|
|
-
|
|
|
- // Complete initialization.
|
|
|
- m_system.GPU().Start();
|
|
|
- m_system.GetCpuManager().OnGpuReady();
|
|
|
- m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
|
|
+ // Try to load a custom driver.
|
|
|
+ if (custom_driver_name.size()) {
|
|
|
+ handle = adrenotools_open_libvulkan(
|
|
|
+ RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
|
|
|
+ custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
|
|
|
+ }
|
|
|
|
|
|
- return Core::SystemResultStatus::Success;
|
|
|
+ // Try to load the system driver.
|
|
|
+ if (!handle) {
|
|
|
+ handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
|
|
+ nullptr, nullptr, file_redirect_dir_, nullptr);
|
|
|
}
|
|
|
|
|
|
- void ShutdownEmulation() {
|
|
|
- std::scoped_lock lock(m_mutex);
|
|
|
+ m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
|
|
|
+#endif
|
|
|
+}
|
|
|
|
|
|
- m_is_running = false;
|
|
|
+bool EmulationSession::IsRunning() const {
|
|
|
+ return m_is_running;
|
|
|
+}
|
|
|
|
|
|
- // Unload user input.
|
|
|
- m_system.HIDCore().UnloadInputDevices();
|
|
|
+bool EmulationSession::IsPaused() const {
|
|
|
+ return m_is_running && m_is_paused;
|
|
|
+}
|
|
|
|
|
|
- // Shutdown the main emulated process
|
|
|
- if (m_load_result == Core::SystemResultStatus::Success) {
|
|
|
- m_system.DetachDebugger();
|
|
|
- m_system.ShutdownMainProcess();
|
|
|
- m_detached_tasks.WaitForAllTasks();
|
|
|
- m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
|
|
|
- m_window.reset();
|
|
|
- OnEmulationStopped(Core::SystemResultStatus::Success);
|
|
|
- return;
|
|
|
- }
|
|
|
+const Core::PerfStatsResults& EmulationSession::PerfStats() const {
|
|
|
+ std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
|
|
+ return m_perf_stats;
|
|
|
+}
|
|
|
|
|
|
- // Tear down the render window.
|
|
|
- m_window.reset();
|
|
|
+void EmulationSession::SurfaceChanged() {
|
|
|
+ if (!IsRunning()) {
|
|
|
+ return;
|
|
|
}
|
|
|
+ m_window->OnSurfaceChanged(m_native_window);
|
|
|
+}
|
|
|
|
|
|
- void PauseEmulation() {
|
|
|
- std::scoped_lock lock(m_mutex);
|
|
|
- m_system.Pause();
|
|
|
- m_is_paused = true;
|
|
|
+void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
|
|
|
+ const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
|
|
+ if (!file) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- void UnPauseEmulation() {
|
|
|
- std::scoped_lock lock(m_mutex);
|
|
|
- m_system.Run();
|
|
|
- m_is_paused = false;
|
|
|
+ auto loader = Loader::GetLoader(m_system, file);
|
|
|
+ if (!loader) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- void HaltEmulation() {
|
|
|
- std::scoped_lock lock(m_mutex);
|
|
|
- m_is_running = false;
|
|
|
- m_cv.notify_one();
|
|
|
+ const auto file_type = loader->GetFileType();
|
|
|
+ if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- void RunEmulation() {
|
|
|
- {
|
|
|
- std::scoped_lock lock(m_mutex);
|
|
|
- m_is_running = true;
|
|
|
- }
|
|
|
-
|
|
|
- // Load the disk shader cache.
|
|
|
- if (Settings::values.use_disk_shader_cache.GetValue()) {
|
|
|
- LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
|
|
- m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
|
|
- m_system.GetApplicationProcessProgramID(), std::stop_token{},
|
|
|
- LoadDiskCacheProgress);
|
|
|
- LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
|
|
+ u64 program_id = 0;
|
|
|
+ const auto res2 = loader->ReadProgramId(program_id);
|
|
|
+ if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
|
|
+ m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
|
|
+ FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
|
|
+ program_id, file);
|
|
|
+ } else if (res2 == Loader::ResultStatus::Success &&
|
|
|
+ (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
|
|
+ const auto nsp = file_type == Loader::FileType::NSP
|
|
|
+ ? std::make_shared<FileSys::NSP>(file)
|
|
|
+ : FileSys::XCI{file}.GetSecurePartitionNSP();
|
|
|
+ for (const auto& title : nsp->GetNCAs()) {
|
|
|
+ for (const auto& entry : title.second) {
|
|
|
+ m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
|
|
+ entry.second->GetBaseFile());
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- void(m_system.Run());
|
|
|
-
|
|
|
- if (m_system.DebuggerEnabled()) {
|
|
|
- m_system.InitializeDebugger();
|
|
|
- }
|
|
|
+Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) {
|
|
|
+ std::scoped_lock lock(m_mutex);
|
|
|
+
|
|
|
+ // Create the render window.
|
|
|
+ m_window =
|
|
|
+ std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
|
|
|
+
|
|
|
+ m_system.SetFilesystem(m_vfs);
|
|
|
+ m_system.GetUserChannel().clear();
|
|
|
+
|
|
|
+ // Initialize system.
|
|
|
+ jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
|
|
|
+ m_software_keyboard = android_keyboard.get();
|
|
|
+ m_system.SetShuttingDown(false);
|
|
|
+ m_system.ApplySettings();
|
|
|
+ Settings::LogSettings();
|
|
|
+ m_system.HIDCore().ReloadInputDevices();
|
|
|
+ m_system.SetAppletFrontendSet({
|
|
|
+ nullptr, // Amiibo Settings
|
|
|
+ nullptr, // Controller Selector
|
|
|
+ nullptr, // Error Display
|
|
|
+ nullptr, // Mii Editor
|
|
|
+ nullptr, // Parental Controls
|
|
|
+ nullptr, // Photo Viewer
|
|
|
+ nullptr, // Profile Selector
|
|
|
+ std::move(android_keyboard), // Software Keyboard
|
|
|
+ nullptr, // Web Browser
|
|
|
+ });
|
|
|
+
|
|
|
+ // Initialize filesystem.
|
|
|
+ m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
|
|
|
+ m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
|
|
+ m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
|
|
+ m_manual_provider.get());
|
|
|
+ m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
|
|
+ ConfigureFilesystemProvider(filepath);
|
|
|
+
|
|
|
+ // Initialize account manager
|
|
|
+ m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
|
|
+
|
|
|
+ // Load the ROM.
|
|
|
+ m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
|
|
|
+ if (m_load_result != Core::SystemResultStatus::Success) {
|
|
|
+ return m_load_result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Complete initialization.
|
|
|
+ m_system.GPU().Start();
|
|
|
+ m_system.GetCpuManager().OnGpuReady();
|
|
|
+ m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
|
|
|
|
|
- OnEmulationStarted();
|
|
|
+ return Core::SystemResultStatus::Success;
|
|
|
+}
|
|
|
|
|
|
- while (true) {
|
|
|
- {
|
|
|
- [[maybe_unused]] std::unique_lock lock(m_mutex);
|
|
|
- if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
|
|
- [&]() { return !m_is_running; })) {
|
|
|
- // Emulation halted.
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- {
|
|
|
- // Refresh performance stats.
|
|
|
- std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
|
|
- m_perf_stats = m_system.GetAndResetPerfStats();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+void EmulationSession::ShutdownEmulation() {
|
|
|
+ std::scoped_lock lock(m_mutex);
|
|
|
|
|
|
- std::string GetRomTitle(const std::string& path) {
|
|
|
- return GetRomMetadata(path).title;
|
|
|
- }
|
|
|
+ m_is_running = false;
|
|
|
|
|
|
- std::vector<u8> GetRomIcon(const std::string& path) {
|
|
|
- return GetRomMetadata(path).icon;
|
|
|
- }
|
|
|
+ // Unload user input.
|
|
|
+ m_system.HIDCore().UnloadInputDevices();
|
|
|
|
|
|
- bool GetIsHomebrew(const std::string& path) {
|
|
|
- return GetRomMetadata(path).isHomebrew;
|
|
|
+ // Shutdown the main emulated process
|
|
|
+ if (m_load_result == Core::SystemResultStatus::Success) {
|
|
|
+ m_system.DetachDebugger();
|
|
|
+ m_system.ShutdownMainProcess();
|
|
|
+ m_detached_tasks.WaitForAllTasks();
|
|
|
+ m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
|
|
|
+ m_window.reset();
|
|
|
+ OnEmulationStopped(Core::SystemResultStatus::Success);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- void ResetRomMetadata() {
|
|
|
- m_rom_metadata_cache.clear();
|
|
|
- }
|
|
|
+ // Tear down the render window.
|
|
|
+ m_window.reset();
|
|
|
+}
|
|
|
|
|
|
- bool IsHandheldOnly() {
|
|
|
- jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
|
|
+void EmulationSession::PauseEmulation() {
|
|
|
+ std::scoped_lock lock(m_mutex);
|
|
|
+ m_system.Pause();
|
|
|
+ m_is_paused = true;
|
|
|
+}
|
|
|
|
|
|
- if (npad_style_set.fullkey == 1) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+void EmulationSession::UnPauseEmulation() {
|
|
|
+ std::scoped_lock lock(m_mutex);
|
|
|
+ m_system.Run();
|
|
|
+ m_is_paused = false;
|
|
|
+}
|
|
|
|
|
|
- if (npad_style_set.handheld == 0) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+void EmulationSession::HaltEmulation() {
|
|
|
+ std::scoped_lock lock(m_mutex);
|
|
|
+ m_is_running = false;
|
|
|
+ m_cv.notify_one();
|
|
|
+}
|
|
|
|
|
|
- return !Settings::IsDockedMode();
|
|
|
+void EmulationSession::RunEmulation() {
|
|
|
+ {
|
|
|
+ std::scoped_lock lock(m_mutex);
|
|
|
+ m_is_running = true;
|
|
|
}
|
|
|
|
|
|
- void SetDeviceType([[maybe_unused]] int index, int type) {
|
|
|
- jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
|
- controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
|
|
+ // Load the disk shader cache.
|
|
|
+ if (Settings::values.use_disk_shader_cache.GetValue()) {
|
|
|
+ LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
|
|
+ m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
|
|
+ m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress);
|
|
|
+ LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
|
|
}
|
|
|
|
|
|
- void OnGamepadConnectEvent([[maybe_unused]] int index) {
|
|
|
- jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
|
-
|
|
|
- // Ensure that player1 is configured correctly and handheld disconnected
|
|
|
- if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
|
|
- jauto handheld =
|
|
|
- m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
|
+ void(m_system.Run());
|
|
|
|
|
|
- if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
|
|
- handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
|
|
- controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
|
|
- handheld->Disconnect();
|
|
|
- }
|
|
|
- }
|
|
|
+ if (m_system.DebuggerEnabled()) {
|
|
|
+ m_system.InitializeDebugger();
|
|
|
+ }
|
|
|
|
|
|
- // Ensure that handheld is configured correctly and player 1 disconnected
|
|
|
- if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
|
|
- jauto player1 =
|
|
|
- m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
|
|
+ OnEmulationStarted();
|
|
|
|
|
|
- if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
|
|
- player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
|
|
- controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
|
|
- player1->Disconnect();
|
|
|
+ while (true) {
|
|
|
+ {
|
|
|
+ [[maybe_unused]] std::unique_lock lock(m_mutex);
|
|
|
+ if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
|
|
+ [&]() { return !m_is_running; })) {
|
|
|
+ // Emulation halted.
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- if (!controller->IsConnected()) {
|
|
|
- controller->Connect();
|
|
|
+ {
|
|
|
+ // Refresh performance stats.
|
|
|
+ std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
|
|
+ m_perf_stats = m_system.GetAndResetPerfStats();
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+bool EmulationSession::IsHandheldOnly() {
|
|
|
+ jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
|
|
|
|
|
- void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
|
|
- jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
|
- controller->Disconnect();
|
|
|
+ if (npad_style_set.fullkey == 1) {
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() {
|
|
|
- return m_software_keyboard;
|
|
|
+ if (npad_style_set.handheld == 0) {
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
-private:
|
|
|
- struct RomMetadata {
|
|
|
- std::string title;
|
|
|
- std::vector<u8> icon;
|
|
|
- bool isHomebrew;
|
|
|
- };
|
|
|
+ return !Settings::IsDockedMode();
|
|
|
+}
|
|
|
|
|
|
- RomMetadata GetRomMetadata(const std::string& path) {
|
|
|
- if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
|
|
- return search->second;
|
|
|
- }
|
|
|
+void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) {
|
|
|
+ jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
|
+ controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
|
|
+}
|
|
|
|
|
|
- return CacheRomMetadata(path);
|
|
|
- }
|
|
|
+void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
|
|
|
+ jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
|
|
|
|
- RomMetadata CacheRomMetadata(const std::string& path) {
|
|
|
- jconst file = Core::GetGameFileFromPath(m_vfs, path);
|
|
|
- jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
|
|
+ // Ensure that player1 is configured correctly and handheld disconnected
|
|
|
+ if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
|
|
+ jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
|
|
|
|
- RomMetadata entry;
|
|
|
- loader->ReadTitle(entry.title);
|
|
|
- loader->ReadIcon(entry.icon);
|
|
|
- if (loader->GetFileType() == Loader::FileType::NRO) {
|
|
|
- jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
|
|
- entry.isHomebrew = loader_nro->IsHomebrew();
|
|
|
- } else {
|
|
|
- entry.isHomebrew = false;
|
|
|
+ if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
|
|
+ handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
|
|
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
|
|
+ handheld->Disconnect();
|
|
|
}
|
|
|
-
|
|
|
- m_rom_metadata_cache[path] = entry;
|
|
|
-
|
|
|
- return entry;
|
|
|
}
|
|
|
|
|
|
-private:
|
|
|
- static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) {
|
|
|
- JNIEnv* env = IDCache::GetEnvForThread();
|
|
|
- env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
|
|
- IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
|
|
- static_cast<jint>(progress), static_cast<jint>(max));
|
|
|
- }
|
|
|
+ // Ensure that handheld is configured correctly and player 1 disconnected
|
|
|
+ if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
|
|
+ jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
|
|
|
|
|
- static void OnEmulationStarted() {
|
|
|
- JNIEnv* env = IDCache::GetEnvForThread();
|
|
|
- env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
|
|
- IDCache::GetOnEmulationStarted());
|
|
|
+ if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
|
|
+ player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
|
|
+ controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
|
|
+ player1->Disconnect();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- static void OnEmulationStopped(Core::SystemResultStatus result) {
|
|
|
- JNIEnv* env = IDCache::GetEnvForThread();
|
|
|
- env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
|
|
- IDCache::GetOnEmulationStopped(), static_cast<jint>(result));
|
|
|
+ if (!controller->IsConnected()) {
|
|
|
+ controller->Connect();
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
-private:
|
|
|
- static EmulationSession s_instance;
|
|
|
-
|
|
|
- // Frontend management
|
|
|
- std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
|
|
-
|
|
|
- // Window management
|
|
|
- std::unique_ptr<EmuWindow_Android> m_window;
|
|
|
- ANativeWindow* m_native_window{};
|
|
|
-
|
|
|
- // Core emulation
|
|
|
- Core::System m_system;
|
|
|
- InputCommon::InputSubsystem m_input_subsystem;
|
|
|
- Common::DetachedTasks m_detached_tasks;
|
|
|
- Core::PerfStatsResults m_perf_stats{};
|
|
|
- std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
|
|
|
- Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
|
|
|
- std::atomic<bool> m_is_running = false;
|
|
|
- std::atomic<bool> m_is_paused = false;
|
|
|
- SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
|
|
- std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
|
|
- std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
|
|
+void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
|
|
+ jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
|
|
+ controller->Disconnect();
|
|
|
+}
|
|
|
|
|
|
- // GPU driver parameters
|
|
|
- std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
|
|
+SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
|
|
+ return m_software_keyboard;
|
|
|
+}
|
|
|
|
|
|
- // Synchronization
|
|
|
- std::condition_variable_any m_cv;
|
|
|
- mutable std::mutex m_perf_stats_mutex;
|
|
|
- mutable std::mutex m_mutex;
|
|
|
-};
|
|
|
+void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
|
|
|
+ int max) {
|
|
|
+ JNIEnv* env = IDCache::GetEnvForThread();
|
|
|
+ env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
|
|
+ IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
|
|
+ static_cast<jint>(progress), static_cast<jint>(max));
|
|
|
+}
|
|
|
|
|
|
-/*static*/ EmulationSession EmulationSession::s_instance;
|
|
|
+void EmulationSession::OnEmulationStarted() {
|
|
|
+ JNIEnv* env = IDCache::GetEnvForThread();
|
|
|
+ env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted());
|
|
|
+}
|
|
|
|
|
|
-} // Anonymous namespace
|
|
|
+void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
|
|
|
+ JNIEnv* env = IDCache::GetEnvForThread();
|
|
|
+ env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(),
|
|
|
+ static_cast<jint>(result));
|
|
|
+}
|
|
|
|
|
|
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
|
|
Common::Log::Initialize();
|
|
|
@@ -657,10 +558,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass cla
|
|
|
EmulationSession::GetInstance().HaltEmulation();
|
|
|
}
|
|
|
|
|
|
-void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
|
|
|
- EmulationSession::GetInstance().ResetRomMetadata();
|
|
|
-}
|
|
|
-
|
|
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
|
|
|
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
|
|
|
}
|
|
|
@@ -766,46 +663,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
|
|
|
- jstring j_filename) {
|
|
|
- jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
|
|
|
- jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
|
|
- env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
|
|
- reinterpret_cast<jbyte*>(icon_data.data()));
|
|
|
- return icon;
|
|
|
-}
|
|
|
-
|
|
|
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
|
|
|
- jstring j_filename) {
|
|
|
- jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
|
|
|
- return env->NewStringUTF(title.c_str());
|
|
|
-}
|
|
|
-
|
|
|
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
|
|
|
- jstring j_filename) {
|
|
|
- return j_filename;
|
|
|
-}
|
|
|
-
|
|
|
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
|
|
|
- jstring j_filename) {
|
|
|
- return j_filename;
|
|
|
-}
|
|
|
-
|
|
|
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
|
|
|
- jstring j_filename) {
|
|
|
- return env->NewStringUTF("");
|
|
|
-}
|
|
|
-
|
|
|
-jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
|
|
|
- jstring j_filename) {
|
|
|
- return env->NewStringUTF("");
|
|
|
-}
|
|
|
-
|
|
|
-jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
|
|
|
- jstring j_filename) {
|
|
|
- return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
|
|
-}
|
|
|
-
|
|
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
|
|
|
// Create the default config.ini.
|
|
|
Config{};
|