summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp2
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp8
-rw-r--r--src/core/arm/skyeye_common/armstate.h2
-rw-r--r--src/core/core.cpp5
-rw-r--r--src/core/core.h9
-rw-r--r--src/core/file_sys/archive_backend.cpp2
-rw-r--r--src/core/frontend/emu_window.cpp97
-rw-r--r--src/core/frontend/emu_window.h114
-rw-r--r--src/core/frontend/framebuffer_layout.cpp36
-rw-r--r--src/core/frontend/framebuffer_layout.h11
-rw-r--r--src/core/frontend/input.h25
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
-rw-r--r--src/core/hle/applets/erreula.cpp4
-rw-r--r--src/core/hle/applets/mii_selector.cpp4
-rw-r--r--src/core/hle/applets/mint.cpp4
-rw-r--r--src/core/hle/applets/swkbd.cpp4
-rw-r--r--src/core/hle/kernel/kernel.h5
-rw-r--r--src/core/hle/kernel/thread.cpp6
-rw-r--r--src/core/hle/lock.cpp11
-rw-r--r--src/core/hle/lock.h18
-rw-r--r--src/core/hle/service/apt/apt.cpp233
-rw-r--r--src/core/hle/service/apt/apt.h6
-rw-r--r--src/core/hle/service/cfg/cfg.cpp2
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp21
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp18
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp9
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/hle/service/hid/hid.cpp44
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp2
-rw-r--r--src/core/hle/svc.cpp8
-rw-r--r--src/core/hw/gpu.cpp2
-rw-r--r--src/core/hw/gpu.h4
-rw-r--r--src/core/loader/loader.h9
-rw-r--r--src/core/loader/ncch.cpp28
-rw-r--r--src/core/loader/ncch.h14
-rw-r--r--src/core/memory.cpp9
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h10
-rw-r--r--src/core/telemetry_session.cpp74
-rw-r--r--src/core/telemetry_session.h12
43 files changed, 640 insertions, 390 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 360f407f3..662030782 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,7 +33,6 @@ set(SRCS
frontend/camera/interface.cpp
frontend/emu_window.cpp
frontend/framebuffer_layout.cpp
- frontend/motion_emu.cpp
gdbstub/gdbstub.cpp
hle/config_mem.cpp
hle/applets/applet.cpp
@@ -60,6 +59,7 @@ set(SRCS
hle/kernel/timer.cpp
hle/kernel/vm_manager.cpp
hle/kernel/wait_object.cpp
+ hle/lock.cpp
hle/romfs.cpp
hle/service/ac/ac.cpp
hle/service/ac/ac_i.cpp
@@ -226,7 +226,6 @@ set(HEADERS
frontend/emu_window.h
frontend/framebuffer_layout.h
frontend/input.h
- frontend/motion_emu.h
gdbstub/gdbstub.h
hle/config_mem.h
hle/function_wrappers.h
@@ -258,6 +257,7 @@ set(HEADERS
hle/kernel/timer.h
hle/kernel/vm_manager.h
hle/kernel/wait_object.h
+ hle/lock.h
hle/result.h
hle/romfs.h
hle/service/ac/ac.h
@@ -388,7 +388,7 @@ set(HEADERS
create_directory_groups(${SRCS} ${HEADERS})
add_library(core STATIC ${SRCS} ${HEADERS})
-target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
+target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
if (ENABLE_WEB_SERVICE)
target_link_libraries(core PUBLIC json-headers web_service)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 7d2790b08..0a0b91590 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -136,7 +136,7 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit);
- unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
+ std::size_t ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
AddTicks(ticks_executed);
}
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index f4fbb8d04..3522d1e82 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -759,7 +759,7 @@ static ThumbDecodeStatus DecodeThumbInstruction(u32 inst, u32 addr, u32* arm_ins
ThumbDecodeStatus ret = TranslateThumbInstruction(addr, inst, arm_inst, inst_size);
if (ret == ThumbDecodeStatus::BRANCH) {
int inst_index;
- int table_length = arm_instruction_trans_len;
+ int table_length = static_cast<int>(arm_instruction_trans_len);
u32 tinstr = GetThumbInstruction(inst, addr);
switch ((tinstr & 0xF800) >> 11) {
@@ -838,7 +838,7 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons
return inst_size;
}
-static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) {
+static int InterpreterTranslateBlock(ARMul_State* cpu, std::size_t& bb_start, u32 addr) {
MICROPROFILE_SCOPE(DynCom_Decode);
// Decode instruction, get index
@@ -871,7 +871,7 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr)
return KEEP_GOING;
}
-static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) {
+static int InterpreterTranslateSingle(ARMul_State* cpu, std::size_t& bb_start, u32 addr) {
MICROPROFILE_SCOPE(DynCom_Decode);
ARM_INST_PTR inst_base = nullptr;
@@ -1620,7 +1620,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
unsigned int addr;
unsigned int num_instrs = 0;
- int ptr;
+ std::size_t ptr;
LOAD_NZCVT;
DISPATCH : {
diff --git a/src/core/arm/skyeye_common/armstate.h b/src/core/arm/skyeye_common/armstate.h
index 1a707ff7e..893877797 100644
--- a/src/core/arm/skyeye_common/armstate.h
+++ b/src/core/arm/skyeye_common/armstate.h
@@ -230,7 +230,7 @@ public:
// TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per
// process for our purposes), not per ARMul_State (which tracks CPU core state).
- std::unordered_map<u32, int> instruction_cache;
+ std::unordered_map<u32, std::size_t> instruction_cache;
private:
void ResetMPCoreCP15Registers();
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d08f18623..5332318cf 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -19,6 +19,7 @@
#include "core/loader/loader.h"
#include "core/memory_setup.h"
#include "core/settings.h"
+#include "network/network.h"
#include "video_core/video_core.h"
namespace Core {
@@ -188,6 +189,10 @@ void System::Shutdown() {
cpu_core = nullptr;
app_loader = nullptr;
telemetry_session = nullptr;
+ if (auto room_member = Network::GetRoomMember().lock()) {
+ Network::GameInfo game_info{};
+ room_member->SendGameInfo(game_info);
+ }
LOG_DEBUG(Core, "Shutdown OK");
}
diff --git a/src/core/core.h b/src/core/core.h
index 4e3b6b409..9805cc694 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
+#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
@@ -14,10 +15,6 @@
class EmuWindow;
class ARM_Interface;
-namespace Loader {
-class AppLoader;
-}
-
namespace Core {
class System {
@@ -119,6 +116,10 @@ public:
return status_details;
}
+ Loader::AppLoader& GetAppLoader() const {
+ return *app_loader;
+ }
+
private:
/**
* Initialize the emulated system.
diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp
index 1fae0ede0..87a240d7a 100644
--- a/src/core/file_sys/archive_backend.cpp
+++ b/src/core/file_sys/archive_backend.cpp
@@ -90,6 +90,8 @@ std::u16string Path::AsU16Str() const {
LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
return {};
}
+
+ UNREACHABLE();
}
std::vector<u8> Path::AsBinary() const {
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 4f7d54a33..e67394177 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -2,14 +2,55 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
#include <cmath>
-#include "common/assert.h"
-#include "core/3ds.h"
-#include "core/core.h"
+#include <mutex>
#include "core/frontend/emu_window.h"
+#include "core/frontend/input.h"
#include "core/settings.h"
+class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
+ public std::enable_shared_from_this<TouchState> {
+public:
+ std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
+ return std::make_unique<Device>(shared_from_this());
+ }
+
+ std::mutex mutex;
+
+ bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
+
+ float touch_x = 0.0f; ///< Touchpad X-position
+ float touch_y = 0.0f; ///< Touchpad Y-position
+
+private:
+ class Device : public Input::TouchDevice {
+ public:
+ explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
+ std::tuple<float, float, bool> GetStatus() const override {
+ if (auto state = touch_state.lock()) {
+ std::lock_guard<std::mutex> guard(state->mutex);
+ return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
+ }
+ return std::make_tuple(0.0f, 0.0f, false);
+ }
+
+ private:
+ std::weak_ptr<TouchState> touch_state;
+ };
+};
+
+EmuWindow::EmuWindow() {
+ // TODO: Find a better place to set this.
+ config.min_client_area_size = std::make_pair(400u, 480u);
+ active_config = config;
+ touch_state = std::make_shared<TouchState>();
+ Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
+}
+
+EmuWindow::~EmuWindow() {
+ Input::UnregisterFactory<Input::TouchDevice>("emu_window");
+}
+
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -38,22 +79,26 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
- touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
- (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
- touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
- (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
+ std::lock_guard<std::mutex> guard(touch_state->mutex);
+ touch_state->touch_x =
+ static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
+ (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
+ touch_state->touch_y =
+ static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
+ (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
- touch_pressed = true;
+ touch_state->touch_pressed = true;
}
void EmuWindow::TouchReleased() {
- touch_pressed = false;
- touch_x = 0;
- touch_y = 0;
+ std::lock_guard<std::mutex> guard(touch_state->mutex);
+ touch_state->touch_pressed = false;
+ touch_state->touch_x = 0;
+ touch_state->touch_y = 0;
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
- if (!touch_pressed)
+ if (!touch_state->touch_pressed)
return;
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
@@ -62,29 +107,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
TouchPressed(framebuffer_x, framebuffer_y);
}
-void EmuWindow::AccelerometerChanged(float x, float y, float z) {
- constexpr float coef = 512;
-
- std::lock_guard<std::mutex> lock(accel_mutex);
-
- // TODO(wwylele): do a time stretch as it in GyroscopeChanged
- // The time stretch formula should be like
- // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
- accel_x = static_cast<s16>(x * coef);
- accel_y = static_cast<s16>(y * coef);
- accel_z = static_cast<s16>(z * coef);
-}
-
-void EmuWindow::GyroscopeChanged(float x, float y, float z) {
- constexpr float FULL_FPS = 60;
- float coef = GetGyroscopeRawToDpsCoefficient();
- float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
- std::lock_guard<std::mutex> lock(gyro_mutex);
- gyro_x = static_cast<s16>(x * coef * stretch);
- gyro_y = static_cast<s16>(y * coef * stretch);
- gyro_z = static_cast<s16>(z * coef * stretch);
-}
-
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
Layout::FramebufferLayout layout;
if (Settings::values.custom_layout == true) {
@@ -97,6 +119,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height)
case Settings::LayoutOption::LargeScreen:
layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
break;
+ case Settings::LayoutOption::SideScreen:
+ layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen);
+ break;
case Settings::LayoutOption::Default:
default:
layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen);
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 9414123a4..c10dee51b 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -4,11 +4,10 @@
#pragma once
-#include <mutex>
+#include <memory>
#include <tuple>
#include <utility>
#include "common/common_types.h"
-#include "common/math_util.h"
#include "core/frontend/framebuffer_layout.h"
/**
@@ -69,84 +68,6 @@ public:
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/**
- * Signal accelerometer state has changed.
- * @param x X-axis accelerometer value
- * @param y Y-axis accelerometer value
- * @param z Z-axis accelerometer value
- * @note all values are in unit of g (gravitational acceleration).
- * e.g. x = 1.0 means 9.8m/s^2 in x direction.
- * @see GetAccelerometerState for axis explanation.
- */
- void AccelerometerChanged(float x, float y, float z);
-
- /**
- * Signal gyroscope state has changed.
- * @param x X-axis accelerometer value
- * @param y Y-axis accelerometer value
- * @param z Z-axis accelerometer value
- * @note all values are in deg/sec.
- * @see GetGyroscopeState for axis explanation.
- */
- void GyroscopeChanged(float x, float y, float z);
-
- /**
- * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @todo Fix this function to be thread-safe.
- * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
- * `pressed` is true if the touch screen is currently being pressed
- */
- std::tuple<u16, u16, bool> GetTouchState() const {
- return std::make_tuple(touch_x, touch_y, touch_pressed);
- }
-
- /**
- * Gets the current accelerometer state (acceleration along each three axis).
- * Axis explained:
- * +x is the same direction as LEFT on D-pad.
- * +y is normal to the touch screen, pointing outward.
- * +z is the same direction as UP on D-pad.
- * Units:
- * 1 unit of return value = 1/512 g (measured by hw test),
- * where g is the gravitational acceleration (9.8 m/sec2).
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @return std::tuple of (x, y, z)
- */
- std::tuple<s16, s16, s16> GetAccelerometerState() {
- std::lock_guard<std::mutex> lock(accel_mutex);
- return std::make_tuple(accel_x, accel_y, accel_z);
- }
-
- /**
- * Gets the current gyroscope state (angular rates about each three axis).
- * Axis explained:
- * +x is the same direction as LEFT on D-pad.
- * +y is normal to the touch screen, pointing outward.
- * +z is the same direction as UP on D-pad.
- * Orientation is determined by right-hand rule.
- * Units:
- * 1 unit of return value = (1/coef) deg/sec,
- * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @return std::tuple of (x, y, z)
- */
- std::tuple<s16, s16, s16> GetGyroscopeState() {
- std::lock_guard<std::mutex> lock(gyro_mutex);
- return std::make_tuple(gyro_x, gyro_y, gyro_z);
- }
-
- /**
- * Gets the coefficient for units conversion of gyroscope state.
- * The conversion formula is r = coefficient * v,
- * where v is angular rate in deg/sec,
- * and r is the gyroscope state.
- * @return float-type coefficient
- */
- f32 GetGyroscopeRawToDpsCoefficient() const {
- return 14.375f; // taken from hw test, and gyroscope's document
- }
-
- /**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
@@ -180,21 +101,8 @@ public:
void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
protected:
- EmuWindow() {
- // TODO: Find a better place to set this.
- config.min_client_area_size = std::make_pair(400u, 480u);
- active_config = config;
- touch_x = 0;
- touch_y = 0;
- touch_pressed = false;
- accel_x = 0;
- accel_y = -512;
- accel_z = 0;
- gyro_x = 0;
- gyro_y = 0;
- gyro_z = 0;
- }
- virtual ~EmuWindow() {}
+ EmuWindow();
+ virtual ~EmuWindow();
/**
* Processes any pending configuration changes from the last SetConfig call.
@@ -250,20 +158,8 @@ private:
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
- bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
-
- u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
- u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
-
- std::mutex accel_mutex;
- s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
- s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
- s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
-
- std::mutex gyro_mutex;
- s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
- s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
- s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
+ class TouchState;
+ std::shared_ptr<TouchState> touch_state;
/**
* Clip the provided coordinates to be inside the touchscreen area.
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index d2d02f9ff..e9f778fcb 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -141,6 +141,40 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped
return res;
}
+FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool swapped) {
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+
+ FramebufferLayout res{width, height, true, true, {}, {}};
+ // Aspect ratio of both screens side by side
+ const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) /
+ (Core::kScreenTopWidth + Core::kScreenBottomWidth);
+ float window_aspect_ratio = static_cast<float>(height) / width;
+ MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height};
+ // Find largest Rectangle that can fit in the window size with the given aspect ratio
+ MathUtil::Rectangle<unsigned> screen_rect =
+ maxRectangle(screen_window_area, emulation_aspect_ratio);
+ // Find sizes of top and bottom screen
+ MathUtil::Rectangle<unsigned> top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
+ MathUtil::Rectangle<unsigned> bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
+
+ if (window_aspect_ratio < emulation_aspect_ratio) {
+ // Apply borders to the left and right sides of the window.
+ u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2;
+ top_screen = top_screen.TranslateX(shift_horizontal);
+ bot_screen = bot_screen.TranslateX(shift_horizontal);
+ } else {
+ // Window is narrower than the emulation content => apply borders to the top and bottom
+ u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2;
+ top_screen = top_screen.TranslateY(shift_vertical);
+ bot_screen = bot_screen.TranslateY(shift_vertical);
+ }
+ // Move the top screen to the right if we are swapped.
+ res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
+ res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
+ return res;
+}
+
FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
ASSERT(width > 0);
ASSERT(height > 0);
@@ -158,4 +192,4 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
res.bottom_screen = bot_screen;
return res;
}
-}
+} // namespace Layout
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 9a7738969..4983cf103 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -54,6 +54,17 @@ FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swa
FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped);
/**
+* Factory method for constructing a Frame with the Top screen and bottom
+* screen side by side
+* This is useful for devices with small screens, like the GPDWin
+* @param width Window framebuffer width in pixels
+* @param height Window framebuffer height in pixels
+* @param is_swapped if true, the bottom screen will be the left display
+* @return Newly created FramebufferLayout object with default screen regions initialized
+*/
+FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapped);
+
+/**
* Factory method for constructing a custom FramebufferLayout
* @param width Window framebuffer width in pixels
* @param height Window framebuffer height in pixels
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 0a5713dc0..8c256beb5 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -11,6 +11,7 @@
#include <utility>
#include "common/logging/log.h"
#include "common/param_package.h"
+#include "common/vector_math.h"
namespace Input {
@@ -107,4 +108,28 @@ using ButtonDevice = InputDevice<bool>;
*/
using AnalogDevice = InputDevice<std::tuple<float, float>>;
+/**
+ * A motion device is an input device that returns a tuple of accelerometer state vector and
+ * gyroscope state vector.
+ *
+ * For both vectors:
+ * x+ is the same direction as LEFT on D-pad.
+ * y+ is normal to the touch screen, pointing outward.
+ * z+ is the same direction as UP on D-pad.
+ *
+ * For accelerometer state vector
+ * Units: g (gravitational acceleration)
+ *
+ * For gyroscope state vector:
+ * Orientation is determined by right-hand rule.
+ * Units: deg/sec
+ */
+using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
+
+/**
+ * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
+ * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
+ */
+using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
+
} // namespace Input
diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp
deleted file mode 100644
index 9a5b3185d..000000000
--- a/src/core/frontend/motion_emu.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/math_util.h"
-#include "common/quaternion.h"
-#include "core/frontend/emu_window.h"
-#include "core/frontend/motion_emu.h"
-
-namespace Motion {
-
-static constexpr int update_millisecond = 100;
-static constexpr auto update_duration =
- std::chrono::duration_cast<std::chrono::steady_clock::duration>(
- std::chrono::milliseconds(update_millisecond));
-
-MotionEmu::MotionEmu(EmuWindow& emu_window)
- : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
-
-MotionEmu::~MotionEmu() {
- if (motion_emu_thread.joinable()) {
- shutdown_event.Set();
- motion_emu_thread.join();
- }
-}
-
-void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
- auto update_time = std::chrono::steady_clock::now();
- Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
- Math::Quaternion<float> old_q;
-
- while (!shutdown_event.WaitUntil(update_time)) {
- update_time += update_duration;
- old_q = q;
-
- {
- std::lock_guard<std::mutex> guard(tilt_mutex);
-
- // Find the quaternion describing current 3DS tilting
- q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
- tilt_angle);
- }
-
- auto inv_q = q.Inverse();
-
- // Set the gravity vector in world space
- auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
-
- // Find the angular rate vector in world space
- auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
- angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
-
- // Transform the two vectors from world space to 3DS space
- gravity = QuaternionRotate(inv_q, gravity);
- angular_rate = QuaternionRotate(inv_q, angular_rate);
-
- // Update the sensor state
- emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
- emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
- }
-}
-
-void MotionEmu::BeginTilt(int x, int y) {
- mouse_origin = Math::MakeVec(x, y);
- is_tilting = true;
-}
-
-void MotionEmu::Tilt(int x, int y) {
- constexpr float SENSITIVITY = 0.01f;
- auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
- if (is_tilting) {
- std::lock_guard<std::mutex> guard(tilt_mutex);
- if (mouse_move.x == 0 && mouse_move.y == 0) {
- tilt_angle = 0;
- } else {
- tilt_direction = mouse_move.Cast<float>();
- tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
- MathUtil::PI * 0.5f);
- }
- }
-}
-
-void MotionEmu::EndTilt() {
- std::lock_guard<std::mutex> guard(tilt_mutex);
- tilt_angle = 0;
- is_tilting = false;
-}
-
-} // namespace Motion
diff --git a/src/core/frontend/motion_emu.h b/src/core/frontend/motion_emu.h
deleted file mode 100644
index 99d41a726..000000000
--- a/src/core/frontend/motion_emu.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-#include "common/thread.h"
-#include "common/vector_math.h"
-
-class EmuWindow;
-
-namespace Motion {
-
-class MotionEmu final {
-public:
- MotionEmu(EmuWindow& emu_window);
- ~MotionEmu();
-
- /**
- * Signals that a motion sensor tilt has begun.
- * @param x the x-coordinate of the cursor
- * @param y the y-coordinate of the cursor
- */
- void BeginTilt(int x, int y);
-
- /**
- * Signals that a motion sensor tilt is occurring.
- * @param x the x-coordinate of the cursor
- * @param y the y-coordinate of the cursor
- */
- void Tilt(int x, int y);
-
- /**
- * Signals that a motion sensor tilt has ended.
- */
- void EndTilt();
-
-private:
- Math::Vec2<int> mouse_origin;
-
- std::mutex tilt_mutex;
- Math::Vec2<float> tilt_direction;
- float tilt_angle = 0;
-
- bool is_tilting = false;
-
- Common::Event shutdown_event;
- std::thread motion_emu_thread;
-
- void MotionEmuThread(EmuWindow& emu_window);
-};
-
-} // namespace Motion
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
index 75d7fd9fc..518f371f5 100644
--- a/src/core/hle/applets/erreula.cpp
+++ b/src/core/hle/applets/erreula.cpp
@@ -31,8 +31,8 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
- heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite,
- MemoryPermission::ReadWrite, "ErrEula Memory");
+ heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "ErrEula Memory");
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 89f08daa2..705859f1e 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -38,8 +38,8 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
- heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite,
- MemoryPermission::ReadWrite, "MiiSelector Memory");
+ heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "MiiSelector Memory");
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp
index 31a79ea17..50d79190b 100644
--- a/src/core/hle/applets/mint.cpp
+++ b/src/core/hle/applets/mint.cpp
@@ -31,8 +31,8 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
- heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite,
- MemoryPermission::ReadWrite, "Mint Memory");
+ heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "Mint Memory");
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index fdf8807b0..0bc471a3a 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -41,8 +41,8 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
// Create a SharedMemory that directly points to this heap block.
framebuffer_memory = Kernel::SharedMemory::CreateForApplet(
- heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite,
- MemoryPermission::ReadWrite, "SoftwareKeyboard Memory");
+ heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "SoftwareKeyboard Memory");
// Send the response message with the newly created SharedMemory
Service::APT::MessageParameter result;
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 9cf288b08..73fab3981 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -8,6 +8,7 @@
#include <string>
#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
+#include "common/assert.h"
#include "common/common_types.h"
namespace Kernel {
@@ -84,6 +85,8 @@ public:
case HandleType::ClientSession:
return false;
}
+
+ UNREACHABLE();
}
public:
@@ -129,4 +132,4 @@ void Init(u32 system_mode);
/// Shutdown the kernel
void Shutdown();
-} // namespace
+} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f5f2eb2f7..b957c45dd 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -478,8 +478,6 @@ void Thread::BoostPriority(s32 priority) {
}
SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
- DEBUG_ASSERT(!GetCurrentThread());
-
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::HEAP_VADDR_END);
@@ -489,9 +487,7 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
thread->context.fpscr =
FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
- // Run new "main" thread
- SwitchContext(thread.get());
-
+ // Note: The newly created thread will be run when the scheduler fires.
return thread;
}
diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp
new file mode 100644
index 000000000..1c24c7ce9
--- /dev/null
+++ b/src/core/hle/lock.cpp
@@ -0,0 +1,11 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <core/hle/lock.h>
+
+namespace HLE {
+std::recursive_mutex g_hle_lock;
+}
diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h
new file mode 100644
index 000000000..5c99fe996
--- /dev/null
+++ b/src/core/hle/lock.h
@@ -0,0 +1,18 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <mutex>
+
+namespace HLE {
+/*
+ * Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
+ * application thread performs a syscall. It should be acquired by any host threads that read or
+ * modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
+ * to the emulated memory is not protected by this mutex, and should be avoided in any threads other
+ * than the CPU thread.
+ */
+extern std::recursive_mutex g_hle_lock;
+} // namespace HLE
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 4e6b7b6f5..58d94768c 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -34,8 +34,6 @@ static bool shared_font_loaded = false;
static bool shared_font_relocated = false;
static Kernel::SharedPtr<Kernel::Mutex> lock;
-static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
-static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event
static u32 cpu_percent; ///< CPU time available to the running application
@@ -44,37 +42,169 @@ static u8 unknown_ns_state_field;
static ScreencapPostPermission screen_capture_post_permission;
-/// Parameter data to be returned in the next call to Glance/ReceiveParameter
+/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
+/// TODO(Subv): Use std::optional once we migrate to C++17.
static boost::optional<MessageParameter> next_parameter;
+enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
+
+static constexpr size_t NumAppletSlot = 4;
+
+enum class AppletSlot : u8 {
+ Application,
+ SystemApplet,
+ HomeMenu,
+ LibraryApplet,
+
+ // An invalid tag
+ Error,
+};
+
+union AppletAttributes {
+ u32 raw;
+
+ BitField<0, 3, u32> applet_pos;
+
+ AppletAttributes() : raw(0) {}
+ AppletAttributes(u32 attributes) : raw(attributes) {}
+};
+
+struct AppletSlotData {
+ AppletId applet_id;
+ AppletSlot slot;
+ bool registered;
+ AppletAttributes attributes;
+ Kernel::SharedPtr<Kernel::Event> notification_event;
+ Kernel::SharedPtr<Kernel::Event> parameter_event;
+};
+
+// Holds data about the concurrently running applets in the system.
+static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
+
+// This overload returns nullptr if no applet with the specified id has been started.
+static AppletSlotData* GetAppletSlotData(AppletId id) {
+ auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
+ return &applet_slots[static_cast<size_t>(slot)];
+ };
+
+ if (id == AppletId::Application) {
+ auto* slot = GetSlot(AppletSlot::Application);
+ if (slot->applet_id != AppletId::None)
+ return slot;
+
+ return nullptr;
+ }
+
+ if (id == AppletId::AnySystemApplet) {
+ auto* system_slot = GetSlot(AppletSlot::SystemApplet);
+ if (system_slot->applet_id != AppletId::None)
+ return system_slot;
+
+ // The Home Menu is also a system applet, but it lives in its own slot to be able to run
+ // concurrently with other system applets.
+ auto* home_slot = GetSlot(AppletSlot::HomeMenu);
+ if (home_slot->applet_id != AppletId::None)
+ return home_slot;
+
+ return nullptr;
+ }
+
+ if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
+ auto* slot = GetSlot(AppletSlot::LibraryApplet);
+ if (slot->applet_id == AppletId::None)
+ return nullptr;
+
+ u32 applet_pos = slot->attributes.applet_pos;
+
+ if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
+ return slot;
+
+ if (id == AppletId::AnySysLibraryApplet &&
+ applet_pos == static_cast<u32>(AppletPos::SysLibrary))
+ return slot;
+
+ return nullptr;
+ }
+
+ if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
+ auto* slot = GetSlot(AppletSlot::HomeMenu);
+ if (slot->applet_id != AppletId::None)
+ return slot;
+
+ return nullptr;
+ }
+
+ for (auto& slot : applet_slots) {
+ if (slot.applet_id == id)
+ return &slot;
+ }
+
+ return nullptr;
+}
+
+static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
+ // Mapping from AppletPos to AppletSlot
+ static constexpr std::array<AppletSlot, 6> applet_position_slots = {
+ AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
+ AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
+
+ u32 applet_pos = attributes.applet_pos;
+ if (applet_pos >= applet_position_slots.size())
+ return nullptr;
+
+ AppletSlot slot = applet_position_slots[applet_pos];
+
+ if (slot == AppletSlot::Error)
+ return nullptr;
+
+ return &applet_slots[static_cast<size_t>(slot)];
+}
+
void SendParameter(const MessageParameter& parameter) {
next_parameter = parameter;
- // Signal the event to let the application know that a new parameter is ready to be read
- parameter_event->Signal();
+ // Signal the event to let the receiver know that a new parameter is ready to be read
+ auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
+ ASSERT(slot_data);
+
+ slot_data->parameter_event->Signal();
}
void Initialize(Service::Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
u32 app_id = rp.Pop<u32>();
- u32 flags = rp.Pop<u32>();
- IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
- rb.Push(RESULT_SUCCESS);
- rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(),
- Kernel::g_handle_table.Create(parameter_event).Unwrap());
+ u32 attributes = rp.Pop<u32>();
+
+ LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes);
- // TODO(bunnei): Check if these events are cleared every time Initialize is called.
- notification_event->Clear();
- parameter_event->Clear();
+ auto* const slot_data = GetAppletSlotData(attributes);
+
+ // Note: The real NS service does not check if the attributes value is valid before accessing
+ // the data in the array
+ ASSERT_MSG(slot_data, "Invalid application attributes");
+
+ if (slot_data->registered) {
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
+ ErrorSummary::InvalidState, ErrorLevel::Status));
+ return;
+ }
- ASSERT_MSG((nullptr != lock), "Cannot initialize without lock");
- lock->Release();
+ slot_data->applet_id = static_cast<AppletId>(app_id);
+ slot_data->attributes.raw = attributes;
- LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags);
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
+ Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
}
void GetSharedFont(Service::Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
+
+ // Log in telemetry if the game uses the shared font
+ Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true);
+
if (!shared_font_loaded) {
LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
rb.Push<u32>(-1); // TODO: Find the right error code
@@ -116,7 +246,12 @@ void GetLockHandle(Service::Interface* self) {
// this will cause the app to wait until parameter_event is signaled.
u32 applet_attributes = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
- rb.Push(RESULT_SUCCESS); // No error
+ rb.Push(RESULT_SUCCESS); // No error
+
+ // TODO(Subv): The output attributes should have an AppletPos of either Library or System |
+ // Library (depending on the type of the last launched applet) if the input attributes'
+ // AppletPos has the Library bit set.
+
rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable.
rb.Push<u32>(0); // Least significant bit = power button state
Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap();
@@ -129,10 +264,22 @@ void GetLockHandle(Service::Interface* self) {
void Enable(Service::Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040
u32 attributes = rp.Pop<u32>();
+
+ LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
+
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
- rb.Push(RESULT_SUCCESS); // No error
- parameter_event->Signal(); // Let the application know that it has been started
- LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes);
+
+ auto* const slot_data = GetAppletSlotData(attributes);
+
+ if (!slot_data) {
+ rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
+ ErrorSummary::InvalidState, ErrorLevel::Status));
+ return;
+ }
+
+ slot_data->registered = true;
+
+ rb.Push(RESULT_SUCCESS);
}
void GetAppletManInfo(Service::Interface* self) {
@@ -150,22 +297,27 @@ void GetAppletManInfo(Service::Interface* self) {
void IsRegistered(Service::Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040
- u32 app_id = rp.Pop<u32>();
+ AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS); // No error
- // TODO(Subv): An application is considered "registered" if it has already called APT::Enable
- // handle this properly once we implement multiprocess support.
- bool is_registered = false; // Set to not registered by default
+ auto* const slot_data = GetAppletSlotData(app_id);
+
+ // Check if an LLE applet was registered first, then fallback to HLE applets
+ bool is_registered = slot_data && slot_data->registered;
- if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) {
- is_registered = HLE::Applets::IsLibraryAppletRunning();
- } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) {
- is_registered = true; // Set to registered
+ if (!is_registered) {
+ if (app_id == AppletId::AnyLibraryApplet) {
+ is_registered = HLE::Applets::IsLibraryAppletRunning();
+ } else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
+ // The applet exists, set it as registered.
+ is_registered = true;
+ }
}
+
rb.Push(is_registered);
- LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
+ LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
}
void InquireNotification(Service::Interface* self) {
@@ -860,14 +1012,23 @@ void Init() {
screen_capture_post_permission =
ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
- // TODO(bunnei): Check if these are created in Initialize or on APT process startup.
- notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification");
- parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start");
+ for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
+ auto& slot_data = applet_slots[slot];
+ slot_data.slot = static_cast<AppletSlot>(slot);
+ slot_data.applet_id = AppletId::None;
+ slot_data.attributes.raw = 0;
+ slot_data.registered = false;
+ slot_data.notification_event =
+ Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
+ slot_data.parameter_event =
+ Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
+ }
// Initialize the parameter to wake up the application.
next_parameter.emplace();
next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
next_parameter->destination_id = static_cast<u32>(AppletId::Application);
+ applet_slots[static_cast<size_t>(AppletSlot::Application)].parameter_event->Signal();
}
void Shutdown() {
@@ -875,8 +1036,12 @@ void Shutdown() {
shared_font_loaded = false;
shared_font_relocated = false;
lock = nullptr;
- notification_event = nullptr;
- parameter_event = nullptr;
+
+ for (auto& slot : applet_slots) {
+ slot.registered = false;
+ slot.notification_event = nullptr;
+ slot.parameter_event = nullptr;
+ }
next_parameter = boost::none;
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 106754853..96b28b438 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -72,6 +72,8 @@ enum class SignalType : u32 {
/// App Id's used by APT functions
enum class AppletId : u32 {
+ None = 0,
+ AnySystemApplet = 0x100,
HomeMenu = 0x101,
AlternateMenu = 0x103,
Camera = 0x110,
@@ -83,6 +85,7 @@ enum class AppletId : u32 {
Miiverse = 0x117,
MiiversePost = 0x118,
AmiiboSettings = 0x119,
+ AnySysLibraryApplet = 0x200,
SoftwareKeyboard1 = 0x201,
Ed1 = 0x202,
PnoteApp = 0x204,
@@ -119,8 +122,9 @@ enum class ScreencapPostPermission : u32 {
namespace ErrCodes {
enum {
ParameterPresent = 2,
+ InvalidAppletSlot = 4,
};
-}
+} // namespace ErrCodes
/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
void SendParameter(const MessageParameter& parameter);
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 6624f1711..3dbeb27cc 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -681,7 +681,7 @@ void GenerateConsoleUniqueId(u32& random_number, u64& console_id) {
CryptoPP::AutoSeededRandomPool rng;
random_number = rng.GenerateWord32(0, 0xFFFF);
u64_le local_friend_code_seed;
- rng.GenerateBlock(reinterpret_cast<byte*>(&local_friend_code_seed),
+ rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
sizeof(local_friend_code_seed));
console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
}
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
index 56f934b3f..6f2bf2061 100644
--- a/src/core/hle/service/dlp/dlp_clnt.cpp
+++ b/src/core/hle/service/dlp/dlp_clnt.cpp
@@ -8,7 +8,26 @@ namespace Service {
namespace DLP {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000100C3, nullptr, "Initialize"}, {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x000100C3, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Finalize"},
+ {0x00030000, nullptr, "GetEventDesc"},
+ {0x00040000, nullptr, "GetChannel"},
+ {0x00050180, nullptr, "StartScan"},
+ {0x00060000, nullptr, "StopScan"},
+ {0x00070080, nullptr, "GetServerInfo"},
+ {0x00080100, nullptr, "GetTitleInfo"},
+ {0x00090040, nullptr, "GetTitleInfoInOrder"},
+ {0x000A0080, nullptr, "DeleteScanInfo"},
+ {0x000B0100, nullptr, "PrepareForSystemDownload"},
+ {0x000C0000, nullptr, "StartSystemDownload"},
+ {0x000D0100, nullptr, "StartTitleDownload"},
+ {0x000E0000, nullptr, "GetMyStatus"},
+ {0x000F0040, nullptr, "GetConnectingNodes"},
+ {0x00100040, nullptr, "GetNodeInfo"},
+ {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x00120000, nullptr, "StopSession"},
+ {0x00130100, nullptr, "GetCupVersion"},
+ {0x00140100, nullptr, "GetDupAvailability"},
};
DLP_CLNT_Interface::DLP_CLNT_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
index 29b9d52e0..fe6be7d32 100644
--- a/src/core/hle/service/dlp/dlp_fkcl.cpp
+++ b/src/core/hle/service/dlp/dlp_fkcl.cpp
@@ -8,7 +8,23 @@ namespace Service {
namespace DLP {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010083, nullptr, "Initialize"}, {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x00010083, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Finalize"},
+ {0x00030000, nullptr, "GetEventDesc"},
+ {0x00040000, nullptr, "GetChannels"},
+ {0x00050180, nullptr, "StartScan"},
+ {0x00060000, nullptr, "StopScan"},
+ {0x00070080, nullptr, "GetServerInfo"},
+ {0x00080100, nullptr, "GetTitleInfo"},
+ {0x00090040, nullptr, "GetTitleInfoInOrder"},
+ {0x000A0080, nullptr, "DeleteScanInfo"},
+ {0x000B0100, nullptr, "StartFakeSession"},
+ {0x000C0000, nullptr, "GetMyStatus"},
+ {0x000D0040, nullptr, "GetConnectingNodes"},
+ {0x000E0040, nullptr, "GetNodeInfo"},
+ {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x00100000, nullptr, "StopSession"},
+ {0x00110203, nullptr, "Initialize2"},
};
DLP_FKCL_Interface::DLP_FKCL_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 32cfa2c44..1bcea43d3 100644
--- a/src/core/hle/service/dlp/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -11,7 +11,7 @@
namespace Service {
namespace DLP {
-static void unk_0x000E0040(Interface* self) {
+static void IsChild(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -24,14 +24,19 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00010183, nullptr, "Initialize"},
{0x00020000, nullptr, "Finalize"},
{0x00030000, nullptr, "GetServerState"},
+ {0x00040000, nullptr, "GetEventDescription"},
{0x00050080, nullptr, "StartAccepting"},
+ {0x00060000, nullptr, "EndAccepting"},
{0x00070000, nullptr, "StartDistribution"},
{0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
{0x00090040, nullptr, "AcceptClient"},
+ {0x000A0040, nullptr, "DisconnectClient"},
{0x000B0042, nullptr, "GetConnectingClients"},
{0x000C0040, nullptr, "GetClientInfo"},
{0x000D0040, nullptr, "GetClientState"},
- {0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
+ {0x000E0040, IsChild, "IsChild"},
+ {0x000F0303, nullptr, "InitializeWithName"},
+ {0x00100000, nullptr, "GetDupNoticeNeed"},
};
DLP_SRVR_Interface::DLP_SRVR_Interface() {
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 7d746054f..42f8950f9 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -147,9 +147,10 @@ static void LoadComponent(Service::Interface* self) {
LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64,
Common::ComputeHash64(component_data.data(), component_data.size()));
// Some versions of the firmware have the location of DSP structures listed here.
- ASSERT(size > 0x37C);
- LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64,
- Common::ComputeHash64(component_data.data() + 0x340, 60));
+ if (size > 0x37C) {
+ LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64,
+ Common::ComputeHash64(component_data.data() + 0x340, 60));
+ }
LOG_WARNING(Service_DSP,
"(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2014b8461..aa5d821f9 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,8 +7,9 @@
#include <cmath>
#include <memory>
#include "common/logging/log.h"
+#include "core/3ds.h"
+#include "core/core.h"
#include "core/core_timing.h"
-#include "core/frontend/emu_window.h"
#include "core/frontend/input.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/event.h"
@@ -18,7 +19,6 @@
#include "core/hle/service/hid/hid_spvr.h"
#include "core/hle/service/hid/hid_user.h"
#include "core/hle/service/service.h"
-#include "video_core/video_core.h"
namespace Service {
namespace HID {
@@ -50,10 +50,15 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
+constexpr float accelerometer_coef = 512.0f; // measured from hw test result
+constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
+
static std::atomic<bool> is_device_reload_pending;
static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
static std::unique_ptr<Input::AnalogDevice> circle_pad;
+static std::unique_ptr<Input::MotionDevice> motion_device;
+static std::unique_ptr<Input::TouchDevice> touch_device;
DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
// 30 degree and 60 degree are angular thresholds for directions
@@ -90,6 +95,8 @@ static void LoadInputDevices() {
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
circle_pad = Input::CreateDevice<Input::AnalogDevice>(
Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
+ motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
+ touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
}
static void UnloadInputDevices() {
@@ -97,6 +104,8 @@ static void UnloadInputDevices() {
button.reset();
}
circle_pad.reset();
+ motion_device.reset();
+ touch_device.reset();
}
static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -165,8 +174,10 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) {
// Get the current touch entry
TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
bool pressed = false;
-
- std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState();
+ float x, y;
+ std::tie(x, y, pressed) = touch_device->GetStatus();
+ touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth);
+ touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight);
touch_entry.valid.Assign(pressed ? 1 : 0);
// TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
@@ -193,10 +204,19 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
mem->accelerometer.index = next_accelerometer_index;
next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
+ Math::Vec3<float> accel;
+ std::tie(accel, std::ignore) = motion_device->GetStatus();
+ accel *= accelerometer_coef;
+ // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
+ // The time stretch formula should be like
+ // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
+
AccelerometerDataEntry& accelerometer_entry =
mem->accelerometer.entries[mem->accelerometer.index];
- std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) =
- VideoCore::g_emu_window->GetAccelerometerState();
+
+ accelerometer_entry.x = static_cast<s16>(accel.x);
+ accelerometer_entry.y = static_cast<s16>(accel.y);
+ accelerometer_entry.z = static_cast<s16>(accel.z);
// Make up "raw" entry
// TODO(wwylele):
@@ -227,8 +247,14 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
- std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) =
- VideoCore::g_emu_window->GetGyroscopeState();
+
+ Math::Vec3<float> gyro;
+ std::tie(std::ignore, gyro) = motion_device->GetStatus();
+ double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
+ gyro *= gyroscope_coef * stretch;
+ gyroscope_entry.x = static_cast<s16>(gyro.x);
+ gyroscope_entry.y = static_cast<s16>(gyro.y);
+ gyroscope_entry.z = static_cast<s16>(gyro.z);
// Make up "raw" entry
mem->gyroscope.raw_entry.x = gyroscope_entry.x;
@@ -326,7 +352,7 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw;
- f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient();
+ f32 coef = gyroscope_coef;
memcpy(&cmd_buff[2], &coef, 4);
}
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 1ef972e70..ef25926b5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -24,7 +24,7 @@ namespace HID {
*/
struct PadState {
union {
- u32 hex;
+ u32 hex{};
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
index 837413f93..0912d5756 100644
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ b/src/core/hle/service/ir/ir_rst.cpp
@@ -18,7 +18,7 @@ namespace Service {
namespace IR {
union PadState {
- u32_le hex;
+ u32_le hex{};
BitField<14, 1, u32_le> zl;
BitField<15, 1, u32_le> zr;
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index e4b803046..dfc36748c 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -31,6 +31,7 @@
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/wait_object.h"
+#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
@@ -1188,7 +1189,7 @@ struct FunctionDef {
Func* func;
const char* name;
};
-}
+} // namespace
static const FunctionDef SVC_Table[] = {
{0x00, nullptr, "Unknown"},
@@ -1332,6 +1333,9 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
void CallSVC(u32 immediate) {
MICROPROFILE_SCOPE(Kernel_SVC);
+ // Lock the global kernel mutex when we enter the kernel HLE.
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+
const FunctionDef* info = GetSVCInfo(immediate);
if (info) {
if (info->func) {
@@ -1342,4 +1346,4 @@ void CallSVC(u32 immediate) {
}
}
-} // namespace
+} // namespace SVC
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 6838e449c..83ad9d898 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -29,7 +29,7 @@ namespace GPU {
Regs g_regs;
/// 268MHz CPU clocks / 60Hz frames per second
-const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE;
+const u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE);
/// Event id for CoreTiming
static int vblank_event;
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 21b127fee..e3d0a0e08 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -74,9 +74,9 @@ struct Regs {
case PixelFormat::RGB5A1:
case PixelFormat::RGBA4:
return 2;
- default:
- UNIMPLEMENTED();
}
+
+ UNREACHABLE();
}
INSERT_PADDING_WORDS(0x4);
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 48bbf687d..e731888a2 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -166,6 +166,15 @@ public:
return ResultStatus::ErrorNotImplemented;
}
+ /**
+ * Get the title of the application
+ * @param title Reference to store the application title into
+ * @return ResultStatus result of function
+ */
+ virtual ResultStatus ReadTitle(std::string& title) {
+ return ResultStatus::ErrorNotImplemented;
+ }
+
protected:
FileUtil::IOFile file;
bool is_loaded = false;
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index fc4d14a59..7aff7f29b 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -4,7 +4,9 @@
#include <algorithm>
#include <cinttypes>
+#include <codecvt>
#include <cstring>
+#include <locale>
#include <memory>
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -18,6 +20,7 @@
#include "core/loader/ncch.h"
#include "core/loader/smdh.h"
#include "core/memory.h"
+#include "network/network.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Loader namespace
@@ -348,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() {
Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
+ if (auto room_member = Network::GetRoomMember().lock()) {
+ Network::GameInfo game_info;
+ ReadTitle(game_info.name);
+ game_info.id = ncch_header.program_id;
+ room_member->SendGameInfo(game_info);
+ }
+
is_loaded = true; // Set state to loaded
result = LoadExec(); // Load the executable into memory for booting
@@ -420,4 +430,22 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_
return ResultStatus::ErrorNotUsed;
}
+ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) {
+ std::vector<u8> data;
+ Loader::SMDH smdh;
+ ReadIcon(data);
+
+ if (!Loader::IsValidSMDH(data)) {
+ return ResultStatus::ErrorInvalidFormat;
+ }
+
+ memcpy(&smdh, data.data(), sizeof(Loader::SMDH));
+
+ const auto& short_title = smdh.GetShortTitle(SMDH::TitleLanguage::English);
+ auto title_end = std::find(short_title.begin(), short_title.end(), u'\0');
+ title = Common::UTF16ToUTF8(std::u16string{short_title.begin(), title_end});
+
+ return ResultStatus::Success;
+}
+
} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 0ebd47fd5..e40cef764 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -191,23 +191,13 @@ public:
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
- /**
- * Get the program id of the application
- * @param out_program_id Reference to store program id into
- * @return ResultStatus result of function
- */
ResultStatus ReadProgramId(u64& out_program_id) override;
- /**
- * Get the RomFS of the application
- * @param romfs_file Reference to buffer to store data
- * @param offset Offset in the file to the RomFS
- * @param size Size of the RomFS in bytes
- * @return ResultStatus result of function
- */
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) override;
+ ResultStatus ReadTitle(std::string& title) override;
+
private:
/**
* Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 65649d9d7..097bc5b47 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/memory_setup.h"
#include "core/mmio.h"
@@ -181,6 +182,9 @@ T Read(const VAddr vaddr) {
return value;
}
+ // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
@@ -219,6 +223,9 @@ void Write(const VAddr vaddr, const T data) {
return;
}
+ // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
@@ -746,4 +753,4 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
return boost::none;
}
-} // namespace
+} // namespace Memory
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d4f0429d1..efcf1267d 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -36,4 +36,4 @@ void Apply() {
Service::IR::ReloadInputDevices();
}
-} // namespace
+} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index ee16bb90a..024f14666 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -15,6 +15,7 @@ enum class LayoutOption {
Default,
SingleScreen,
LargeScreen,
+ SideScreen,
};
namespace NativeButton {
@@ -70,7 +71,7 @@ enum Values {
static const std::array<const char*, NumAnalogs> mapping = {{
"circle_pad", "c_stick",
}};
-} // namespace NumAnalog
+} // namespace NativeAnalog
struct Values {
// CheckNew3DS
@@ -79,6 +80,8 @@ struct Values {
// Controls
std::array<std::string, NativeButton::NumButtons> buttons;
std::array<std::string, NativeAnalog::NumAnalogs> analogs;
+ std::string motion_device;
+ std::string touch_device;
// Core
bool use_cpu_jit;
@@ -128,7 +131,10 @@ struct Values {
u16 gdbstub_port;
// WebService
+ bool enable_telemetry;
std::string telemetry_endpoint_url;
+ std::string citra_username;
+ std::string citra_token;
} extern values;
// a special value for Values::region_value indicating that citra will automatically select a region
@@ -136,4 +142,4 @@ struct Values {
static constexpr int REGION_VALUE_AUTO_SELECT = -1;
void Apply();
-}
+} // namespace Settings
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 841d6cfa1..104a16cc9 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -3,10 +3,13 @@
// Refer to the license.txt file included.
#include <cstring>
+#include <cryptopp/osrng.h>
#include "common/assert.h"
+#include "common/file_util.h"
#include "common/scm_rev.h"
#include "common/x64/cpu_detect.h"
+#include "core/core.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
@@ -28,23 +31,83 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) {
UNREACHABLE();
}
+static u64 GenerateTelemetryId() {
+ u64 telemetry_id{};
+ CryptoPP::AutoSeededRandomPool rng;
+ rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&telemetry_id), sizeof(u64));
+ return telemetry_id;
+}
+
+u64 GetTelemetryId() {
+ u64 telemetry_id{};
+ static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
+
+ if (FileUtil::Exists(filename)) {
+ FileUtil::IOFile file(filename, "rb");
+ if (!file.IsOpen()) {
+ LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
+ return {};
+ }
+ file.ReadBytes(&telemetry_id, sizeof(u64));
+ } else {
+ FileUtil::IOFile file(filename, "wb");
+ if (!file.IsOpen()) {
+ LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
+ return {};
+ }
+ telemetry_id = GenerateTelemetryId();
+ file.WriteBytes(&telemetry_id, sizeof(u64));
+ }
+
+ return telemetry_id;
+}
+
+u64 RegenerateTelemetryId() {
+ const u64 new_telemetry_id{GenerateTelemetryId()};
+ static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
+
+ FileUtil::IOFile file(filename, "wb");
+ if (!file.IsOpen()) {
+ LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str());
+ return {};
+ }
+ file.WriteBytes(&new_telemetry_id, sizeof(u64));
+ return new_telemetry_id;
+}
+
TelemetrySession::TelemetrySession() {
#ifdef ENABLE_WEB_SERVICE
- backend = std::make_unique<WebService::TelemetryJson>();
+ if (Settings::values.enable_telemetry) {
+ backend = std::make_unique<WebService::TelemetryJson>(
+ Settings::values.telemetry_endpoint_url, Settings::values.citra_username,
+ Settings::values.citra_token);
+ } else {
+ backend = std::make_unique<Telemetry::NullVisitor>();
+ }
#else
backend = std::make_unique<Telemetry::NullVisitor>();
#endif
+ // Log one-time top-level information
+ AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
+
// Log one-time session start information
const s64 init_time{std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()};
AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
+ std::string program_name;
+ const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)};
+ if (res == Loader::ResultStatus::Success) {
+ AddField(Telemetry::FieldType::Session, "ProgramName", program_name);
+ }
// Log application information
const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr};
AddField(Telemetry::FieldType::App, "Git_IsDirty", is_git_dirty);
AddField(Telemetry::FieldType::App, "Git_Branch", Common::g_scm_branch);
AddField(Telemetry::FieldType::App, "Git_Revision", Common::g_scm_rev);
+ AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date);
+ AddField(Telemetry::FieldType::App, "BuildName", Common::g_build_name);
// Log user system information
AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
@@ -68,6 +131,15 @@ TelemetrySession::TelemetrySession() {
Common::GetCPUCaps().sse4_1);
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42",
Common::GetCPUCaps().sse4_2);
+#ifdef __APPLE__
+ AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Apple");
+#elif defined(_WIN32)
+ AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Windows");
+#elif defined(__linux__) || defined(linux) || defined(__linux)
+ AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Linux");
+#else
+ AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Unknown");
+#endif
// Log user configuration information
AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index cf53835c3..65613daae 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -35,4 +35,16 @@ private:
std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
};
+/**
+ * Gets TelemetryId, a unique identifier used for the user's telemetry sessions.
+ * @returns The current TelemetryId for the session.
+ */
+u64 GetTelemetryId();
+
+/**
+ * Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions.
+ * @returns The new TelemetryId that was generated.
+ */
+u64 RegenerateTelemetryId();
+
} // namespace Core