summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
m---------externals/nihstro0
-rw-r--r--src/citra/citra.cpp3
-rw-r--r--src/citra/config.cpp6
-rw-r--r--src/citra/default_ini.h27
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp21
-rw-r--r--src/citra_qt/CMakeLists.txt9
-rw-r--r--src/citra_qt/bootmanager.cpp13
-rw-r--r--src/citra_qt/config.cpp36
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp2
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp86
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.h2
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp41
-rw-r--r--src/citra_qt/debugger/profiler.cpp138
-rw-r--r--src/citra_qt/debugger/profiler.h50
-rw-r--r--src/citra_qt/debugger/profiler.ui33
-rw-r--r--src/citra_qt/main.cpp9
-rw-r--r--src/citra_qt/main.h2
-rw-r--r--src/common/CMakeLists.txt12
-rw-r--r--src/common/assert.h31
-rw-r--r--src/common/chunk_file.h2
-rw-r--r--src/common/common.h109
-rw-r--r--src/common/common_funcs.h107
-rw-r--r--src/common/common_paths.h2
-rw-r--r--src/common/emu_window.cpp50
-rw-r--r--src/common/emu_window.h32
-rw-r--r--src/common/extended_trace.cpp428
-rw-r--r--src/common/extended_trace.h50
-rw-r--r--src/common/file_search.cpp103
-rw-r--r--src/common/file_search.h23
-rw-r--r--src/common/file_util.cpp91
-rw-r--r--src/common/hash.cpp13
-rw-r--r--src/common/logging/backend.cpp10
-rw-r--r--src/common/logging/backend.h3
-rw-r--r--src/common/logging/filter.h2
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/logging/text_formatter.cpp7
-rw-r--r--src/common/logging/text_formatter.h3
-rw-r--r--src/common/mem_arena.cpp9
-rw-r--r--src/common/memory_util.cpp14
-rw-r--r--src/common/msg_handler.cpp107
-rw-r--r--src/common/msg_handler.h56
-rw-r--r--src/common/profiler.cpp182
-rw-r--r--src/common/profiler.h152
-rw-r--r--src/common/profiler_reporting.h108
-rw-r--r--src/common/swap.h111
-rw-r--r--src/common/synchronized_wrapper.h69
-rw-r--r--src/common/thread.h19
-rw-r--r--src/common/utf8.cpp459
-rw-r--r--src/common/utf8.h67
-rw-r--r--src/core/CMakeLists.txt28
-rw-r--r--src/core/arm/disassembler/arm_disasm.cpp8
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp70
-rw-r--r--src/core/arm/interpreter/arminit.cpp4
-rw-r--r--src/core/arm/skyeye_common/armdefs.h23
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp_helper.h4
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpdouble.cpp4
-rw-r--r--src/core/core_timing.cpp4
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp33
-rw-r--r--src/core/file_sys/archive_systemsavedata.h14
-rw-r--r--src/core/hle/hle.cpp15
-rw-r--r--src/core/hle/kernel/thread.cpp5
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/am_sys.cpp53
-rw-r--r--src/core/hle/service/apt/apt.cpp285
-rw-r--r--src/core/hle/service/apt/apt.h222
-rw-r--r--src/core/hle/service/apt/apt_a.cpp33
-rw-r--r--src/core/hle/service/apt/apt_a.h (renamed from src/core/hle/service/apt_a.h)13
-rw-r--r--src/core/hle/service/apt/apt_s.cpp104
-rw-r--r--src/core/hle/service/apt/apt_s.h (renamed from src/core/hle/service/apt_s.h)13
-rw-r--r--src/core/hle/service/apt/apt_u.cpp103
-rw-r--r--src/core/hle/service/apt/apt_u.h (renamed from src/core/hle/service/apt_u.h)13
-rw-r--r--src/core/hle/service/apt_a.cpp44
-rw-r--r--src/core/hle/service/apt_s.cpp123
-rw-r--r--src/core/hle/service/apt_u.cpp526
-rw-r--r--src/core/hle/service/cfg/cfg.cpp74
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp14
-rw-r--r--src/core/hle/service/cfg/cfg_i.h13
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp14
-rw-r--r--src/core/hle/service/cfg/cfg_s.h13
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp14
-rw-r--r--src/core/hle/service/cfg/cfg_u.h13
-rw-r--r--src/core/hle/service/err_f.cpp165
-rw-r--r--src/core/hle/service/fs/archive.cpp8
-rw-r--r--src/core/hle/service/fs/fs_user.cpp30
-rw-r--r--src/core/hle/service/fs/fs_user.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp32
-rw-r--r--src/core/hle/service/hid/hid.h36
-rw-r--r--src/core/hle/service/hid/hid_spvr.cpp21
-rw-r--r--src/core/hle/service/hid/hid_spvr.h13
-rw-r--r--src/core/hle/service/hid/hid_user.cpp58
-rw-r--r--src/core/hle/service/hid/hid_user.h15
-rw-r--r--src/core/hle/service/ptm/ptm.cpp76
-rw-r--r--src/core/hle/service/ptm/ptm.h65
-rw-r--r--src/core/hle/service/ptm/ptm_play.cpp (renamed from src/core/hle/service/ptm_play.cpp)16
-rw-r--r--src/core/hle/service/ptm/ptm_play.h (renamed from src/core/hle/service/ptm_play.h)13
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp (renamed from src/core/hle/service/ptm_sysm.cpp)28
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.h (renamed from src/core/hle/service/ptm_sysm.h)13
-rw-r--r--src/core/hle/service/ptm/ptm_u.cpp99
-rw-r--r--src/core/hle/service/ptm/ptm_u.h (renamed from src/core/hle/service/ptm_u.h)15
-rw-r--r--src/core/hle/service/ptm_u.cpp166
-rw-r--r--src/core/hle/service/service.cpp50
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/hle/service/y2r_u.cpp19
-rw-r--r--src/core/hw/gpu.cpp137
-rw-r--r--src/core/hw/gpu.h18
-rw-r--r--src/core/mem_map_funcs.cpp18
-rw-r--r--src/core/settings.h6
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/color.h132
-rw-r--r--src/video_core/command_processor.cpp6
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp84
-rw-r--r--src/video_core/pica.h18
-rw-r--r--src/video_core/rasterizer.cpp84
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp60
-rw-r--r--src/video_core/utils.h50
-rw-r--r--src/video_core/vertex_shader.cpp80
-rw-r--r--src/video_core/video_core.cpp3
-rw-r--r--src/video_core/video_core.h1
119 files changed, 3296 insertions, 3165 deletions
diff --git a/README.md b/README.md
index 7715dfe4f..40a380f50 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ Citra Emulator
[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra)
[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/sdf1o4kh3g1e68m9?svg=true)](https://ci.appveyor.com/project/bunnei/citra)
-Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! None of these run to a playable state, but we are working every day to advance the project forward.
+Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward.
Citra is licensed under the GPLv2. Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project.
diff --git a/externals/nihstro b/externals/nihstro
-Subproject 0a8b4d221425f13e24a3cef9b02edc3221bab21
+Subproject 4a78588b308564f7ebae193e0ae00d9a0d5741d
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 69f0b35b3..2c6ced920 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -22,7 +22,8 @@
int __cdecl main(int argc, char **argv) {
std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
Log::Filter log_filter(Log::Level::Debug);
- std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter);
+ Log::SetFilter(&log_filter);
+ std::thread logging_thread(Log::TextLoggingLoop, logger);
SCOPE_EXIT({
logger->Close();
logging_thread.join();
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index f5b4069c7..e9f3dfd5b 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -44,6 +44,8 @@ void Config::ReadValues() {
Settings::values.pad_y_key = glfw_config->GetInteger("Controls", "pad_y", GLFW_KEY_X);
Settings::values.pad_l_key = glfw_config->GetInteger("Controls", "pad_l", GLFW_KEY_Q);
Settings::values.pad_r_key = glfw_config->GetInteger("Controls", "pad_r", GLFW_KEY_W);
+ Settings::values.pad_zl_key = glfw_config->GetInteger("Controls", "pad_zl", GLFW_KEY_1);
+ Settings::values.pad_zr_key = glfw_config->GetInteger("Controls", "pad_zr", GLFW_KEY_2);
Settings::values.pad_start_key = glfw_config->GetInteger("Controls", "pad_start", GLFW_KEY_M);
Settings::values.pad_select_key = glfw_config->GetInteger("Controls", "pad_select", GLFW_KEY_N);
Settings::values.pad_home_key = glfw_config->GetInteger("Controls", "pad_home", GLFW_KEY_B);
@@ -55,6 +57,10 @@ void Config::ReadValues() {
Settings::values.pad_sdown_key = glfw_config->GetInteger("Controls", "pad_sdown", GLFW_KEY_DOWN);
Settings::values.pad_sleft_key = glfw_config->GetInteger("Controls", "pad_sleft", GLFW_KEY_LEFT);
Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT);
+ Settings::values.pad_cup_key = glfw_config->GetInteger("Controls", "pad_cup", GLFW_KEY_I);
+ Settings::values.pad_cdown_key = glfw_config->GetInteger("Controls", "pad_cdown", GLFW_KEY_K);
+ Settings::values.pad_cleft_key = glfw_config->GetInteger("Controls", "pad_cleft", GLFW_KEY_J);
+ Settings::values.pad_cright_key = glfw_config->GetInteger("Controls", "pad_cright", GLFW_KEY_L);
// Core
Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 30);
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index be4b289bd..fc02a3ceb 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -19,25 +19,42 @@ pad_a =
pad_b =
pad_x =
pad_y =
-pad_r =
pad_l =
+pad_r =
+pad_zl =
+pad_zr =
pad_sup =
pad_sdown =
pad_sleft =
pad_sright =
+pad_cup =
+pad_cdown =
+pad_cleft =
+pad_cright =
[Core]
-gpu_refresh_rate = ## 30 (default)
-frame_skip = ## 0: No frameskip (default), 1 : 2x frameskip, 2 : 4x frameskip, etc.
+# The refresh rate for the GPU
+# Defaults to 30
+gpu_refresh_rate =
+
+# The applied frameskip amount. Must be a power of two.
+# 0 (default): No frameskip, 1: x2 frameskip, 2: x4 frameskip, 3: x8 frameskip, etc.
+frame_skip =
[Data Storage]
+# Whether to create a virtual SD card.
+# 1 (default): Yes, 0: No
use_virtual_sd =
[System Region]
-region_value = ## 0 : Japan, 1 : Usa (default), 2 : Europe, 3 : Australia, 4 : China, 5 : Korea, 6 : Taiwan.
+# The system region that Citra will use during emulation
+# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
+region_value =
[Miscellaneous]
-log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
+# A filter which removes logs below a certain logging level.
+# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
+log_filter = *:Info
)";
}
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
index 8a3ee64a8..81231e1e5 100644
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ b/src/citra/emu_window/emu_window_glfw.cpp
@@ -36,20 +36,13 @@ const bool EmuWindow_GLFW::IsOpen() {
}
void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
- ASSERT(width > 0);
- ASSERT(height > 0);
-
- GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height));
+ GetEmuWindow(win)->NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
}
void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
- ASSERT(width > 0);
- ASSERT(height > 0);
-
// NOTE: GLFW provides no proper way to set a minimal window size.
// Hence, we just ignore the corresponding EmuWindow hint.
-
- GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(width, height));
+ OnFramebufferResizeEvent(win, width, height);
}
/// EmuWindow_GLFW constructor
@@ -139,6 +132,16 @@ void EmuWindow_GLFW::ReloadSetKeymaps() {
KeyMap::SetKeyMapping({Settings::values.pad_l_key, keyboard_id}, Service::HID::PAD_L);
KeyMap::SetKeyMapping({Settings::values.pad_x_key, keyboard_id}, Service::HID::PAD_X);
KeyMap::SetKeyMapping({Settings::values.pad_y_key, keyboard_id}, Service::HID::PAD_Y);
+
+ KeyMap::SetKeyMapping({Settings::values.pad_zl_key, keyboard_id}, Service::HID::PAD_ZL);
+ KeyMap::SetKeyMapping({Settings::values.pad_zr_key, keyboard_id}, Service::HID::PAD_ZR);
+
+ // KeyMap::SetKeyMapping({Settings::values.pad_touch_key, keyboard_id}, Service::HID::PAD_TOUCH);
+
+ KeyMap::SetKeyMapping({Settings::values.pad_cright_key, keyboard_id}, Service::HID::PAD_C_RIGHT);
+ KeyMap::SetKeyMapping({Settings::values.pad_cleft_key, keyboard_id}, Service::HID::PAD_C_LEFT);
+ KeyMap::SetKeyMapping({Settings::values.pad_cup_key, keyboard_id}, Service::HID::PAD_C_UP);
+ KeyMap::SetKeyMapping({Settings::values.pad_cdown_key, keyboard_id}, Service::HID::PAD_C_DOWN);
KeyMap::SetKeyMapping({Settings::values.pad_sright_key, keyboard_id}, Service::HID::PAD_CIRCLE_RIGHT);
KeyMap::SetKeyMapping({Settings::values.pad_sleft_key, keyboard_id}, Service::HID::PAD_CIRCLE_LEFT);
KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, Service::HID::PAD_CIRCLE_UP);
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 586bc84b0..ff780cad4 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -13,6 +13,7 @@ set(SRCS
debugger/graphics_cmdlists.cpp
debugger/graphics_framebuffer.cpp
debugger/graphics_vertex_shader.cpp
+ debugger/profiler.cpp
debugger/ramview.cpp
debugger/registers.cpp
util/spinbox.cpp
@@ -35,6 +36,7 @@ set(HEADERS
debugger/graphics_cmdlists.h
debugger/graphics_framebuffer.h
debugger/graphics_vertex_shader.h
+ debugger/profiler.h
debugger/ramview.h
debugger/registers.h
util/spinbox.h
@@ -48,6 +50,7 @@ set(UIS
config/controller_config.ui
debugger/callstack.ui
debugger/disassembler.ui
+ debugger/profiler.ui
debugger/registers.ui
hotkeys.ui
main.ui
@@ -61,7 +64,11 @@ else()
qt4_wrap_ui(UI_HDRS ${UIS})
endif()
-add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
+if (APPLE)
+ add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS})
+else()
+ add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
+endif()
target_link_libraries(citra-qt core common video_core qhexedit)
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 22a7842bf..a040e75c1 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -155,6 +155,7 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this
child = new GGLWidgetInternal(fmt, this);
QBoxLayout* layout = new QHBoxLayout(this);
+
resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
layout->addWidget(child);
layout->setMargin(0);
@@ -234,7 +235,7 @@ void GRenderWindow::OnFramebufferSizeChanged()
unsigned height = child->QPaintDevice::height();
#endif
- NotifyFramebufferSizeChanged(std::make_pair(width, height));
+ NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
}
void GRenderWindow::BackupGeometry()
@@ -291,6 +292,16 @@ void GRenderWindow::ReloadSetKeymaps()
KeyMap::SetKeyMapping({Settings::values.pad_l_key, keyboard_id}, Service::HID::PAD_L);
KeyMap::SetKeyMapping({Settings::values.pad_x_key, keyboard_id}, Service::HID::PAD_X);
KeyMap::SetKeyMapping({Settings::values.pad_y_key, keyboard_id}, Service::HID::PAD_Y);
+
+ KeyMap::SetKeyMapping({Settings::values.pad_zl_key, keyboard_id}, Service::HID::PAD_ZL);
+ KeyMap::SetKeyMapping({Settings::values.pad_zr_key, keyboard_id}, Service::HID::PAD_ZR);
+
+ // KeyMap::SetKeyMapping({Settings::values.pad_touch_key, keyboard_id}, Service::HID::PAD_TOUCH);
+
+ KeyMap::SetKeyMapping({Settings::values.pad_cright_key, keyboard_id}, Service::HID::PAD_C_RIGHT);
+ KeyMap::SetKeyMapping({Settings::values.pad_cleft_key, keyboard_id}, Service::HID::PAD_C_LEFT);
+ KeyMap::SetKeyMapping({Settings::values.pad_cup_key, keyboard_id}, Service::HID::PAD_C_UP);
+ KeyMap::SetKeyMapping({Settings::values.pad_cdown_key, keyboard_id}, Service::HID::PAD_C_DOWN);
KeyMap::SetKeyMapping({Settings::values.pad_sright_key, keyboard_id}, Service::HID::PAD_CIRCLE_RIGHT);
KeyMap::SetKeyMapping({Settings::values.pad_sleft_key, keyboard_id}, Service::HID::PAD_CIRCLE_LEFT);
KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, Service::HID::PAD_CIRCLE_UP);
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 76aeaedd0..ac250e0a5 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -23,12 +23,14 @@ Config::Config() {
void Config::ReadValues() {
qt_config->beginGroup("Controls");
- Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt();
- Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt();
- Settings::values.pad_x_key = qt_config->value("pad_x", Qt::Key_Z).toInt();
- Settings::values.pad_y_key = qt_config->value("pad_y", Qt::Key_X).toInt();
- Settings::values.pad_l_key = qt_config->value("pad_l", Qt::Key_Q).toInt();
- Settings::values.pad_r_key = qt_config->value("pad_r", Qt::Key_W).toInt();
+ Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt();
+ Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt();
+ Settings::values.pad_x_key = qt_config->value("pad_x", Qt::Key_Z).toInt();
+ Settings::values.pad_y_key = qt_config->value("pad_y", Qt::Key_X).toInt();
+ Settings::values.pad_l_key = qt_config->value("pad_l", Qt::Key_Q).toInt();
+ Settings::values.pad_r_key = qt_config->value("pad_r", Qt::Key_W).toInt();
+ Settings::values.pad_zl_key = qt_config->value("pad_zl", Qt::Key_1).toInt();
+ Settings::values.pad_zr_key = qt_config->value("pad_zr", Qt::Key_2).toInt();
Settings::values.pad_start_key = qt_config->value("pad_start", Qt::Key_M).toInt();
Settings::values.pad_select_key = qt_config->value("pad_select", Qt::Key_N).toInt();
Settings::values.pad_home_key = qt_config->value("pad_home", Qt::Key_B).toInt();
@@ -40,6 +42,10 @@ void Config::ReadValues() {
Settings::values.pad_sdown_key = qt_config->value("pad_sdown", Qt::Key_Down).toInt();
Settings::values.pad_sleft_key = qt_config->value("pad_sleft", Qt::Key_Left).toInt();
Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt();
+ Settings::values.pad_cup_key = qt_config->value("pad_cup", Qt::Key_I).toInt();
+ Settings::values.pad_cdown_key = qt_config->value("pad_cdown", Qt::Key_K).toInt();
+ Settings::values.pad_cleft_key = qt_config->value("pad_cleft", Qt::Key_J).toInt();
+ Settings::values.pad_cright_key = qt_config->value("pad_cright", Qt::Key_L).toInt();
qt_config->endGroup();
qt_config->beginGroup("Core");
@@ -62,12 +68,14 @@ void Config::ReadValues() {
void Config::SaveValues() {
qt_config->beginGroup("Controls");
- qt_config->setValue("pad_a", Settings::values.pad_a_key);
- qt_config->setValue("pad_b", Settings::values.pad_b_key);
- qt_config->setValue("pad_x", Settings::values.pad_x_key);
- qt_config->setValue("pad_y", Settings::values.pad_y_key);
- qt_config->setValue("pad_l", Settings::values.pad_l_key);
- qt_config->setValue("pad_r", Settings::values.pad_r_key);
+ qt_config->setValue("pad_a", Settings::values.pad_a_key);
+ qt_config->setValue("pad_b", Settings::values.pad_b_key);
+ qt_config->setValue("pad_x", Settings::values.pad_x_key);
+ qt_config->setValue("pad_y", Settings::values.pad_y_key);
+ qt_config->setValue("pad_l", Settings::values.pad_l_key);
+ qt_config->setValue("pad_r", Settings::values.pad_r_key);
+ qt_config->setValue("pad_zl", Settings::values.pad_zl_key);
+ qt_config->setValue("pad_zr", Settings::values.pad_zr_key);
qt_config->setValue("pad_start", Settings::values.pad_start_key);
qt_config->setValue("pad_select", Settings::values.pad_select_key);
qt_config->setValue("pad_home", Settings::values.pad_home_key);
@@ -79,6 +87,10 @@ void Config::SaveValues() {
qt_config->setValue("pad_sdown", Settings::values.pad_sdown_key);
qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key);
qt_config->setValue("pad_sright", Settings::values.pad_sright_key);
+ qt_config->setValue("pad_cup", Settings::values.pad_cup_key);
+ qt_config->setValue("pad_cdown", Settings::values.pad_cdown_key);
+ qt_config->setValue("pad_cleft", Settings::values.pad_cleft_key);
+ qt_config->setValue("pad_cright", Settings::values.pad_cright_key);
qt_config->endGroup();
qt_config->beginGroup("Core");
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index bd420f24a..9bcd25821 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -66,7 +66,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo
QComboBox* format_choice = new QComboBox;
format_choice->addItem(tr("RGBA8"));
format_choice->addItem(tr("RGB8"));
- format_choice->addItem(tr("RGBA5551"));
+ format_choice->addItem(tr("RGB5A1"));
format_choice->addItem(tr("RGB565"));
format_choice->addItem(tr("RGBA4"));
format_choice->addItem(tr("IA8"));
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
index 1ba60021f..5bd6c0235 100644
--- a/src/citra_qt/debugger/graphics_framebuffer.cpp
+++ b/src/citra_qt/debugger/graphics_framebuffer.cpp
@@ -9,8 +9,10 @@
#include <QPushButton>
#include <QSpinBox>
+#include "core/hw/gpu.h"
#include "video_core/color.h"
#include "video_core/pica.h"
+#include "video_core/utils.h"
#include "graphics_framebuffer.h"
@@ -44,7 +46,7 @@ GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::Debug
framebuffer_format_control = new QComboBox;
framebuffer_format_control->addItem(tr("RGBA8"));
framebuffer_format_control->addItem(tr("RGB8"));
- framebuffer_format_control->addItem(tr("RGBA5551"));
+ framebuffer_format_control->addItem(tr("RGB5A1"));
framebuffer_format_control->addItem(tr("RGB565"));
framebuffer_format_control->addItem(tr("RGBA4"));
@@ -195,60 +197,42 @@ void GraphicsFramebufferWidget::OnUpdate()
// TODO: Implement a good way to visualize alpha components!
// TODO: Unify this decoding code with the texture decoder
- switch (framebuffer_format) {
- case Format::RGBA8:
- {
- QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
- u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
- for (unsigned int y = 0; y < framebuffer_height; ++y) {
- for (unsigned int x = 0; x < framebuffer_width; ++x) {
- u32 value = *(color_buffer + x + y * framebuffer_width);
-
- decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/));
+ u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer_format));
+
+ QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
+ u8* color_buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
+ for (unsigned int y = 0; y < framebuffer_height; ++y) {
+ for (unsigned int x = 0; x < framebuffer_width; ++x) {
+ const u32 coarse_y = y & ~7;
+ u32 offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer_width * bytes_per_pixel;
+ const u8* pixel = color_buffer + offset;
+ Math::Vec4<u8> color = { 0, 0, 0, 0 };
+
+ switch (framebuffer_format) {
+ case Format::RGBA8:
+ color = Color::DecodeRGBA8(pixel);
+ break;
+ case Format::RGB8:
+ color = Color::DecodeRGB8(pixel);
+ break;
+ case Format::RGB5A1:
+ color = Color::DecodeRGB5A1(pixel);
+ break;
+ case Format::RGB565:
+ color = Color::DecodeRGB565(pixel);
+ break;
+ case Format::RGBA4:
+ color = Color::DecodeRGBA4(pixel);
+ break;
+ default:
+ qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
+ break;
}
- }
- pixmap = QPixmap::fromImage(decoded_image);
- break;
- }
- case Format::RGB8:
- {
- QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
- u8* color_buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
- for (unsigned int y = 0; y < framebuffer_height; ++y) {
- for (unsigned int x = 0; x < framebuffer_width; ++x) {
- u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width;
-
- decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/));
- }
+ decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), 255));
}
- pixmap = QPixmap::fromImage(decoded_image);
- break;
- }
-
- case Format::RGBA5551:
- {
- QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
- u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address));
- for (unsigned int y = 0; y < framebuffer_height; ++y) {
- for (unsigned int x = 0; x < framebuffer_width; ++x) {
- u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2);
- u8 r = Color::Convert5To8((value >> 11) & 0x1F);
- u8 g = Color::Convert5To8((value >> 6) & 0x1F);
- u8 b = Color::Convert5To8((value >> 1) & 0x1F);
- u8 a = Color::Convert1To8(value & 1);
-
- decoded_image.setPixel(x, y, qRgba(r, g, b, 255/*a*/));
- }
- }
- pixmap = QPixmap::fromImage(decoded_image);
- break;
- }
-
- default:
- qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
- break;
}
+ pixmap = QPixmap::fromImage(decoded_image);
framebuffer_address_control->SetValue(framebuffer_address);
framebuffer_width_control->setValue(framebuffer_width);
diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h
index c6e293bc9..15ebd1f7d 100644
--- a/src/citra_qt/debugger/graphics_framebuffer.h
+++ b/src/citra_qt/debugger/graphics_framebuffer.h
@@ -29,7 +29,7 @@ class GraphicsFramebufferWidget : public BreakPointObserverDock {
enum class Format {
RGBA8 = 0,
RGB8 = 1,
- RGBA5551 = 2,
+ RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
};
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index 06eaf0bf0..3b072d015 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -12,6 +12,7 @@
#include "graphics_vertex_shader.h"
+using nihstro::OpCode;
using nihstro::Instruction;
using nihstro::SourceRegister;
using nihstro::SwizzlePattern;
@@ -78,7 +79,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
const SwizzlePattern& swizzle = info.swizzle_info[instr.common.operand_desc_id].pattern;
// longest known instruction name: "setemit "
- output << std::setw(8) << std::left << instr.opcode.GetInfo().name;
+ output << std::setw(8) << std::left << instr.opcode.Value().GetInfo().name;
// e.g. "-c92.xyzw"
static auto print_input = [](std::stringstream& output, const SourceRegister& input,
@@ -109,16 +110,16 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
print_input_indexed(output, input, negate, swizzle_mask, address_register_name);
};
- switch (instr.opcode.GetInfo().type) {
- case Instruction::OpCodeType::Trivial:
+ switch (instr.opcode.Value().GetInfo().type) {
+ case OpCode::Type::Trivial:
// Nothing to do here
break;
- case Instruction::OpCodeType::Arithmetic:
+ case OpCode::Type::Arithmetic:
{
// Use custom code for special instructions
- switch (instr.opcode.EffectiveOpCode()) {
- case Instruction::OpCode::CMP:
+ switch (instr.opcode.Value().EffectiveOpCode()) {
+ case OpCode::Id::CMP:
{
// NOTE: CMP always writes both cc components, so we do not consider the dest mask here.
output << std::setw(4) << std::right << "cc.";
@@ -142,13 +143,13 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
default:
{
- bool src_is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed);
+ bool src_is_inverted = 0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed);
- if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Dest) {
+ if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::Dest) {
// e.g. "r12.xy__"
- output << std::setw(4) << std::right << instr.common.dest.GetName() + ".";
+ output << std::setw(4) << std::right << instr.common.dest.Value().GetName() + ".";
output << swizzle.DestMaskToString();
- } else if (instr.opcode.GetInfo().subtype == Instruction::OpCodeInfo::MOVA) {
+ } else if (instr.opcode.Value().GetInfo().subtype == OpCode::Info::MOVA) {
output << std::setw(4) << std::right << "a0.";
output << swizzle.DestMaskToString();
} else {
@@ -156,7 +157,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
}
output << " ";
- if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src1) {
+ if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::Src1) {
SourceRegister src1 = instr.common.GetSrc1(src_is_inverted);
print_input_indexed(output, src1, swizzle.negate_src1, swizzle.SelectorToString(false), instr.common.AddressRegisterName());
} else {
@@ -164,7 +165,7 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
}
// TODO: In some cases, the Address Register is used as an index for SRC2 instead of SRC1
- if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::Src2) {
+ if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::Src2) {
SourceRegister src2 = instr.common.GetSrc2(src_is_inverted);
print_input(output, src2, swizzle.negate_src2, swizzle.SelectorToString(false));
}
@@ -175,17 +176,17 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
break;
}
- case Instruction::OpCodeType::Conditional:
+ case OpCode::Type::Conditional:
{
- switch (instr.opcode.EffectiveOpCode()) {
- case Instruction::OpCode::LOOP:
+ switch (instr.opcode.Value().EffectiveOpCode()) {
+ case OpCode::Id::LOOP:
output << "(unknown instruction format)";
break;
default:
output << "if ";
- if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasCondition) {
+ if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasCondition) {
const char* ops[] = {
" || ", " && ", "", ""
};
@@ -198,22 +199,22 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con
output << ((!instr.flow_control.refy) ? "!" : " ") << "cc.y";
output << " ";
- } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasUniformIndex) {
+ } else if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasUniformIndex) {
output << "b" << instr.flow_control.bool_uniform_id << " ";
}
u32 target_addr = instr.flow_control.dest_offset;
u32 target_addr_else = instr.flow_control.dest_offset;
- if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasAlternative) {
+ if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasAlternative) {
output << "else jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " ";
- } else if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasExplicitDest) {
+ } else if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasExplicitDest) {
output << "jump to 0x" << std::setw(4) << std::right << std::setfill('0') << 4 * instr.flow_control.dest_offset << " ";
} else {
// TODO: Handle other cases
}
- if (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::HasFinishPoint) {
+ if (instr.opcode.Value().GetInfo().subtype & OpCode::Info::HasFinishPoint) {
output << "(return on " << std::setw(4) << std::right << std::setfill('0')
<< 4 * instr.flow_control.dest_offset + 4 * instr.flow_control.num_instructions << ")";
}
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp
new file mode 100644
index 000000000..ae0568b6a
--- /dev/null
+++ b/src/citra_qt/debugger/profiler.cpp
@@ -0,0 +1,138 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "profiler.h"
+
+#include "common/profiler_reporting.h"
+
+using namespace Common::Profiling;
+
+static QVariant GetDataForColumn(int col, const AggregatedDuration& duration)
+{
+ static auto duration_to_float = [](Duration dur) -> float {
+ using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
+ return std::chrono::duration_cast<FloatMs>(dur).count();
+ };
+
+ switch (col) {
+ case 1: return duration_to_float(duration.avg);
+ case 2: return duration_to_float(duration.min);
+ case 3: return duration_to_float(duration.max);
+ default: return QVariant();
+ }
+}
+
+static const TimingCategoryInfo* GetCategoryInfo(int id)
+{
+ const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
+ if (id >= categories.size()) {
+ return nullptr;
+ } else {
+ return &categories[id];
+ }
+}
+
+ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent)
+{
+ updateProfilingInfo();
+ const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
+ results.time_per_category.resize(categories.size());
+}
+
+QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case 0: return tr("Category");
+ case 1: return tr("Avg");
+ case 2: return tr("Min");
+ case 3: return tr("Max");
+ }
+ }
+
+ return QVariant();
+}
+
+QModelIndex ProfilerModel::index(int row, int column, const QModelIndex& parent) const
+{
+ return createIndex(row, column);
+}
+
+QModelIndex ProfilerModel::parent(const QModelIndex& child) const
+{
+ return QModelIndex();
+}
+
+int ProfilerModel::columnCount(const QModelIndex& parent) const
+{
+ return 4;
+}
+
+int ProfilerModel::rowCount(const QModelIndex& parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ } else {
+ return results.time_per_category.size() + 2;
+ }
+}
+
+QVariant ProfilerModel::data(const QModelIndex& index, int role) const
+{
+ if (role == Qt::DisplayRole) {
+ if (index.row() == 0) {
+ if (index.column() == 0) {
+ return tr("Frame");
+ } else {
+ return GetDataForColumn(index.column(), results.frame_time);
+ }
+ } else if (index.row() == 1) {
+ if (index.column() == 0) {
+ return tr("Frame (with swapping)");
+ } else {
+ return GetDataForColumn(index.column(), results.interframe_time);
+ }
+ } else {
+ if (index.column() == 0) {
+ const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2);
+ return info != nullptr ? QString(info->name) : QVariant();
+ } else {
+ if (index.row() - 2 < results.time_per_category.size()) {
+ return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]);
+ } else {
+ return QVariant();
+ }
+ }
+ }
+ }
+
+ return QVariant();
+}
+
+void ProfilerModel::updateProfilingInfo()
+{
+ results = GetTimingResultsAggregator()->GetAggregatedResults();
+ emit dataChanged(createIndex(0, 1), createIndex(rowCount() - 1, 3));
+}
+
+ProfilerWidget::ProfilerWidget(QWidget* parent) : QDockWidget(parent)
+{
+ ui.setupUi(this);
+
+ model = new ProfilerModel(this);
+ ui.treeView->setModel(model);
+
+ connect(this, SIGNAL(visibilityChanged(bool)), SLOT(setProfilingInfoUpdateEnabled(bool)));
+ connect(&update_timer, SIGNAL(timeout()), model, SLOT(updateProfilingInfo()));
+}
+
+void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable)
+{
+ if (enable) {
+ update_timer.start(100);
+ model->updateProfilingInfo();
+ } else {
+ update_timer.stop();
+ }
+}
diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h
new file mode 100644
index 000000000..a6d87aa0f
--- /dev/null
+++ b/src/citra_qt/debugger/profiler.h
@@ -0,0 +1,50 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QAbstractItemModel>
+#include <QDockWidget>
+#include <QTimer>
+#include "ui_profiler.h"
+
+#include "common/profiler_reporting.h"
+
+class ProfilerModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ ProfilerModel(QObject* parent);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex& child) const override;
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+
+public slots:
+ void updateProfilingInfo();
+
+private:
+ Common::Profiling::AggregatedFrameResult results;
+};
+
+class ProfilerWidget : public QDockWidget
+{
+ Q_OBJECT
+
+public:
+ ProfilerWidget(QWidget* parent = 0);
+
+private slots:
+ void setProfilingInfoUpdateEnabled(bool enable);
+
+private:
+ Ui::Profiler ui;
+ ProfilerModel* model;
+
+ QTimer update_timer;
+};
diff --git a/src/citra_qt/debugger/profiler.ui b/src/citra_qt/debugger/profiler.ui
new file mode 100644
index 000000000..d3c9a9a1f
--- /dev/null
+++ b/src/citra_qt/debugger/profiler.ui
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Profiler</class>
+ <widget class="QDockWidget" name="Profiler">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Profiler</string>
+ </property>
+ <widget class="QWidget" name="dockWidgetContents">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTreeView" name="treeView">
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 881c7d337..e5ca04124 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -35,6 +35,7 @@
#include "debugger/graphics_cmdlists.h"
#include "debugger/graphics_framebuffer.h"
#include "debugger/graphics_vertex_shader.h"
+#include "debugger/profiler.h"
#include "core/settings.h"
#include "core/system.h"
@@ -57,6 +58,10 @@ GMainWindow::GMainWindow()
render_window = new GRenderWindow;
render_window->hide();
+ profilerWidget = new ProfilerWidget(this);
+ addDockWidget(Qt::BottomDockWidgetArea, profilerWidget);
+ profilerWidget->hide();
+
disasmWidget = new DisassemblerWidget(this, render_window->GetEmuThread());
addDockWidget(Qt::BottomDockWidgetArea, disasmWidget);
disasmWidget->hide();
@@ -90,6 +95,7 @@ GMainWindow::GMainWindow()
graphicsVertexShaderWidget->hide();
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
+ debug_menu->addAction(profilerWidget->toggleViewAction());
debug_menu->addAction(disasmWidget->toggleViewAction());
debug_menu->addAction(registersWidget->toggleViewAction());
debug_menu->addAction(callstackWidget->toggleViewAction());
@@ -310,7 +316,8 @@ int __cdecl main(int argc, char* argv[])
{
std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
Log::Filter log_filter(Log::Level::Info);
- std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter);
+ Log::SetFilter(&log_filter);
+ std::thread logging_thread(Log::TextLoggingLoop, logger);
SCOPE_EXIT({
logger->Close();
logging_thread.join();
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index dd53489dd..9b57c5772 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -11,6 +11,7 @@
class GImageInfo;
class GRenderWindow;
+class ProfilerWidget;
class DisassemblerWidget;
class RegistersWidget;
class CallstackWidget;
@@ -54,6 +55,7 @@ private:
GRenderWindow* render_window;
+ ProfilerWidget* profilerWidget;
DisassemblerWidget* disasmWidget;
RegistersWidget* registersWidget;
CallstackWidget* callstackWidget;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 8c87deaa4..daa2d59de 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -4,8 +4,6 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
set(SRCS
break_points.cpp
emu_window.cpp
- extended_trace.cpp
- file_search.cpp
file_util.cpp
hash.cpp
key_map.cpp
@@ -16,13 +14,12 @@ set(SRCS
mem_arena.cpp
memory_util.cpp
misc.cpp
- msg_handler.cpp
+ profiler.cpp
scm_rev.cpp
string_util.cpp
symbols.cpp
thread.cpp
timer.cpp
- utf8.cpp
)
set(HEADERS
@@ -38,9 +35,7 @@ set(HEADERS
cpu_detect.h
debug_interface.h
emu_window.h
- extended_trace.h
fifo_queue.h
- file_search.h
file_util.h
hash.h
key_map.h
@@ -53,18 +48,19 @@ set(HEADERS
math_util.h
mem_arena.h
memory_util.h
- msg_handler.h
platform.h
+ profiler.h
+ profiler_reporting.h
scm_rev.h
scope_exit.h
string_util.h
swap.h
symbols.h
+ synchronized_wrapper.h
thread.h
thread_queue_list.h
thunk.h
timer.h
- utf8.h
)
create_directory_groups(${SRCS} ${HEADERS})
diff --git a/src/common/assert.h b/src/common/assert.h
index 3b2232a7e..9ca7adb15 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -4,24 +4,43 @@
#pragma once
+#include <cstdlib>
+
#include "common/common_funcs.h"
+// For asserts we'd like to keep all the junk executed when an assert happens away from the
+// important code in the function. One way of doing this is to put all the relevant code inside a
+// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
+// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
+// template that calls the lambda. This seems to generate an extra instruction at the call-site
+// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
+// enough for our purposes.
+template <typename Fn>
+#if defined(_MSC_VER)
+ __declspec(noinline, noreturn)
+#elif defined(__GNUC__)
+ __attribute__((noinline, noreturn, cold))
+#endif
+static void assert_noinline_call(const Fn& fn) {
+ fn();
+ Crash();
+ exit(1); // Keeps GCC's mouth shut about this actually returning
+}
+
// TODO (yuriks) allow synchronous logging so we don't need printf
#define ASSERT(_a_) \
- do if (!(_a_)) {\
+ do if (!(_a_)) { assert_noinline_call([] { \
fprintf(stderr, "Assertion Failed!\n\n Line: %d\n File: %s\n Time: %s\n", \
__LINE__, __FILE__, __TIME__); \
- Crash(); \
- } while (0)
+ }); } while (0)
#define ASSERT_MSG(_a_, ...) \
- do if (!(_a_)) {\
+ do if (!(_a_)) { assert_noinline_call([&] { \
fprintf(stderr, "Assertion Failed!\n\n Line: %d\n File: %s\n Time: %s\n", \
__LINE__, __FILE__, __TIME__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
- Crash(); \
- } while (0)
+ }); } while (0)
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h
index dc27da088..3f97d56bf 100644
--- a/src/common/chunk_file.h
+++ b/src/common/chunk_file.h
@@ -637,7 +637,7 @@ public:
Do(cookie);
if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber)
{
- PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
+ LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
SetError(ERROR_FAILURE);
}
}
diff --git a/src/common/common.h b/src/common/common.h
index ad2de6f2e..f7d0f55c5 100644
--- a/src/common/common.h
+++ b/src/common/common.h
@@ -28,7 +28,6 @@ private:
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/common_types.h"
-#include "common/msg_handler.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/platform.h"
@@ -36,13 +35,11 @@ private:
#ifdef __APPLE__
// The Darwin ABI requires that stack frames be aligned to 16-byte boundaries.
// This is only needed on i386 gcc - x86_64 already aligns to 16 bytes.
-#if defined __i386__ && defined __GNUC__
-#undef STACKALIGN
-#define STACKALIGN __attribute__((__force_align_arg_pointer__))
-#endif
-
+ #if defined __i386__ && defined __GNUC__
+ #undef STACKALIGN
+ #define STACKALIGN __attribute__((__force_align_arg_pointer__))
+ #endif
#elif defined _WIN32
-
// Check MSC ver
#if defined _MSC_VER && _MSC_VER <= 1000
#error needs at least version 1000 of MSC
@@ -52,9 +49,6 @@ private:
#define NOMINMAX
#endif
-// Memory leak checks
- #define CHECK_HEAP_INTEGRITY()
-
// Alignment
#define MEMORY_ALIGNED16(x) __declspec(align(16)) x
#define MEMORY_ALIGNED32(x) __declspec(align(32)) x
@@ -62,57 +56,34 @@ private:
#define MEMORY_ALIGNED128(x) __declspec(align(128)) x
#define MEMORY_ALIGNED16_DECL(x) __declspec(align(16)) x
#define MEMORY_ALIGNED64_DECL(x) __declspec(align(64)) x
-
-// Since they are always around on windows
- #define HAVE_WX 1
- #define HAVE_OPENAL 1
-
- #define HAVE_PORTAUDIO 1
-
-// Debug definitions
- #if defined(_DEBUG)
- #include <crtdbg.h>
- #undef CHECK_HEAP_INTEGRITY
- #define CHECK_HEAP_INTEGRITY() {if (!_CrtCheckMemory()) PanicAlert("memory corruption detected. see log.");}
- // If you want to see how much a pain in the ass singletons are, for example:
- // {614} normal block at 0x030C5310, 188 bytes long.
- // Data: <Master Log > 4D 61 73 74 65 72 20 4C 6F 67 00 00 00 00 00 00
- struct CrtDebugBreak { CrtDebugBreak(int spot) { _CrtSetBreakAlloc(spot); } };
- //CrtDebugBreak breakAt(614);
- #endif // end DEBUG/FAST
-
#endif
// Windows compatibility
#ifndef _WIN32
-#ifdef _LP64
-#define _M_X64 1
-#else
-#define _M_IX86 1
-#endif
-#define __forceinline inline __attribute__((always_inline))
-#define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x
-#define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x
-#define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x
-#define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x
-#define MEMORY_ALIGNED16_DECL(x) __attribute__((aligned(16))) x
-#define MEMORY_ALIGNED64_DECL(x) __attribute__((aligned(64))) x
+ #ifdef _LP64
+ #define _M_X64 1
+ #else
+ #define _M_IX86 1
+ #endif
+ #define __forceinline inline __attribute__((always_inline))
+ #define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x
+ #define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x
+ #define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x
+ #define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x
+ #define MEMORY_ALIGNED16_DECL(x) __attribute__((aligned(16))) x
+ #define MEMORY_ALIGNED64_DECL(x) __attribute__((aligned(64))) x
#endif
#ifdef _MSC_VER
-#define __strdup _strdup
-#define __getcwd _getcwd
-#define __chdir _chdir
+ #define __strdup _strdup
+ #define __getcwd _getcwd
+ #define __chdir _chdir
#else
-#define __strdup strdup
-#define __getcwd getcwd
-#define __chdir chdir
+ #define __strdup strdup
+ #define __getcwd getcwd
+ #define __chdir chdir
#endif
-// Dummy macro for marking translatable strings that can not be immediately translated.
-// wxWidgets does not have a true dummy macro for this.
-#define _trans(a) a
-
#if defined _M_GENERIC
# define _M_SSE 0x0
#elif defined __GNUC__
@@ -146,40 +117,4 @@ enum EMUSTATE_CHANGE
EMUSTATE_CHANGE_STOP
};
-
-#ifdef _MSC_VER
-inline unsigned long long bswap64(unsigned long long x) { return _byteswap_uint64(x); }
-inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); }
-inline unsigned short bswap16(unsigned short x) { return _byteswap_ushort(x); }
-#else
-// TODO: speedup
-inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); }
-inline unsigned int bswap32(unsigned int x) { return (x >> 24) | ((x & 0xFF0000) >> 8) | ((x & 0xFF00) << 8) | (x << 24);}
-inline unsigned long long bswap64(unsigned long long x) {return ((unsigned long long)bswap32(x) << 32) | bswap32(x >> 32); }
-#endif
-
-inline float bswapf(float f) {
- union {
- float f;
- unsigned int u32;
- } dat1, dat2;
-
- dat1.f = f;
- dat2.u32 = bswap32(dat1.u32);
-
- return dat2.f;
-}
-
-inline double bswapd(double f) {
- union {
- double f;
- unsigned long long u64;
- } dat1, dat2;
-
- dat1.f = f;
- dat2.u64 = bswap64(dat1.u64);
-
- return dat2.f;
-}
-
#include "swap.h"
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 4bbcc3571..e76cb7d68 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -14,8 +14,6 @@
#define SLEEP(x) usleep(x*1000)
#endif
-template <bool> struct CompileTimeAssert;
-template<> struct CompileTimeAssert<true> {};
#define b2(x) ( (x) | ( (x) >> 1) )
#define b4(x) ( b2(x) | ( b2(x) >> 2) )
@@ -24,11 +22,6 @@ template<> struct CompileTimeAssert<true> {};
#define b32(x) (b16(x) | (b16(x) >>16) )
#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
-#define MIN(a, b) ((a)<(b)?(a):(b))
-#define MAX(a, b) ((a)>(b)?(a):(b))
-
-#define CLAMP(x, min, max) (((x) > max) ? max : (((x) < min) ? min : (x)))
-
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
/// Textually concatenates two tokens. The double-expansion is required by the C preprocessor.
@@ -38,18 +31,12 @@ template<> struct CompileTimeAssert<true> {};
// helper macro to properly align structure members.
// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
// depending on the current source line to make sure variable names are unique.
-#define INSERT_PADDING_BYTES_HELPER1(x, y) x ## y
-#define INSERT_PADDING_BYTES_HELPER2(x, y) INSERT_PADDING_BYTES_HELPER1(x, y)
-#define INSERT_PADDING_BYTES(num_words) u8 INSERT_PADDING_BYTES_HELPER2(pad, __LINE__)[(num_words)]
+#define INSERT_PADDING_BYTES(num_bytes) u8 CONCAT2(pad, __LINE__)[(num_bytes)]
+#define INSERT_PADDING_WORDS(num_words) u32 CONCAT2(pad, __LINE__)[(num_words)]
#ifndef _MSC_VER
#include <errno.h>
-#ifdef __linux__
-#include <byteswap.h>
-#elif defined __FreeBSD__
-#include <sys/endian.h>
-#endif
#if defined(__x86_64__) || defined(_M_X64)
#define Crash() __asm__ __volatile__("int $3")
@@ -148,98 +135,8 @@ inline u64 _rotr64(u64 x, unsigned int shift){
#define Crash() {DebugBreak();}
#endif // _MSC_VER ndef
-// Dolphin's min and max functions
-#undef min
-#undef max
-
-template<class T>
-inline T min(const T& a, const T& b) {return a > b ? b : a;}
-template<class T>
-inline T max(const T& a, const T& b) {return a > b ? a : b;}
-
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
// Defined in Misc.cpp.
const char* GetLastErrorMsg();
-
-namespace Common
-{
-inline u8 swap8(u8 _data) {return _data;}
-inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];}
-
-#ifdef ANDROID
-#undef swap16
-#undef swap32
-#undef swap64
-#endif
-
-#ifdef _MSC_VER
-inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
-inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
-inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
-#elif _M_ARM
-inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;}
-inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
-inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);}
-#elif __linux__
-inline u16 swap16(u16 _data) {return bswap_16(_data);}
-inline u32 swap32(u32 _data) {return bswap_32(_data);}
-inline u64 swap64(u64 _data) {return bswap_64(_data);}
-#elif __APPLE__
-inline __attribute__((always_inline)) u16 swap16(u16 _data)
- {return (_data >> 8) | (_data << 8);}
-inline __attribute__((always_inline)) u32 swap32(u32 _data)
- {return __builtin_bswap32(_data);}
-inline __attribute__((always_inline)) u64 swap64(u64 _data)
- {return __builtin_bswap64(_data);}
-#elif __FreeBSD__
-inline u16 swap16(u16 _data) {return bswap16(_data);}
-inline u32 swap32(u32 _data) {return bswap32(_data);}
-inline u64 swap64(u64 _data) {return bswap64(_data);}
-#else
-// Slow generic implementation.
-inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);}
-inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);}
-inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);}
-#endif
-
-inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);}
-inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);}
-inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);}
-
-template <int count>
-void swap(u8*);
-
-template <>
-inline void swap<1>(u8* data)
-{}
-
-template <>
-inline void swap<2>(u8* data)
-{
- *reinterpret_cast<u16*>(data) = swap16(data);
-}
-
-template <>
-inline void swap<4>(u8* data)
-{
- *reinterpret_cast<u32*>(data) = swap32(data);
-}
-
-template <>
-inline void swap<8>(u8* data)
-{
- *reinterpret_cast<u64*>(data) = swap64(data);
-}
-
-template <typename T>
-inline T FromBigEndian(T data)
-{
- //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types");
-
- swap<sizeof(data)>(reinterpret_cast<u8*>(&data));
- return data;
-}
-
-} // Namespace Common
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 0ecf2d9de..eb43d589f 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -25,7 +25,7 @@
#ifdef USER_DIR
#define EMU_DATA_DIR USER_DIR
#else
- #define EMU_DATA_DIR ".citra-emu"
+ #define EMU_DATA_DIR "citra-emu"
#endif
#endif
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp
index 48bb35db5..6459d2f32 100644
--- a/src/common/emu_window.cpp
+++ b/src/common/emu_window.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "emu_window.h"
+#include "video_core/video_core.h"
void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) {
Service::HID::PadState mapped_key = KeyMap::GetPadKey(key);
@@ -15,3 +16,52 @@ void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) {
Service::HID::PadButtonRelease(mapped_key);
}
+
+EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) {
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+
+ EmuWindow::FramebufferLayout res = { width, height, {}, {} };
+
+ float window_aspect_ratio = static_cast<float>(height) / width;
+ float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) /
+ VideoCore::kScreenTopWidth;
+
+ if (window_aspect_ratio > emulation_aspect_ratio) {
+ // Window is narrower than the emulation content => apply borders to the top and bottom
+ int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width));
+
+ res.top_screen.left = 0;
+ res.top_screen.right = res.top_screen.left + width;
+ res.top_screen.top = (height - viewport_height) / 2;
+ res.top_screen.bottom = res.top_screen.top + viewport_height / 2;
+
+ int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
+ VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
+ int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
+
+ res.bottom_screen.left = bottom_border;
+ res.bottom_screen.right = res.bottom_screen.left + bottom_width;
+ res.bottom_screen.top = res.top_screen.bottom;
+ res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2;
+ } else {
+ // Otherwise, apply borders to the left and right sides of the window.
+ int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio));
+
+ res.top_screen.left = (width - viewport_width) / 2;
+ res.top_screen.right = res.top_screen.left + viewport_width;
+ res.top_screen.top = 0;
+ res.top_screen.bottom = res.top_screen.top + height / 2;
+
+ int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) /
+ VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left));
+ int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2;
+
+ res.bottom_screen.left = res.top_screen.left + bottom_border;
+ res.bottom_screen.right = res.bottom_screen.left + bottom_width;
+ res.bottom_screen.top = res.top_screen.bottom;
+ res.bottom_screen.bottom = res.bottom_screen.top + height / 2;
+ }
+
+ return res;
+}
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index 1ad4b82a3..f6099fdb6 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -8,6 +8,7 @@
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "common/key_map.h"
+#include "common/math_util.h"
/**
* Abstraction class used to provide an interface between emulation code and the frontend
@@ -38,6 +39,23 @@ public:
std::pair<unsigned,unsigned> min_client_area_size;
};
+ /// Describes the layout of the window framebuffer (size and top/bottom screen positions)
+ struct FramebufferLayout {
+
+ /**
+ * Factory method for constructing a default FramebufferLayout
+ * @param width Window framebuffer width in pixels
+ * @param height Window framebuffer height in pixels
+ * @return Newly created FramebufferLayout object with default screen regions initialized
+ */
+ static FramebufferLayout DefaultScreenLayout(unsigned width, unsigned height);
+
+ unsigned width;
+ unsigned height;
+ MathUtil::Rectangle<unsigned> top_screen;
+ MathUtil::Rectangle<unsigned> bottom_screen;
+ };
+
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
@@ -75,11 +93,11 @@ public:
}
/**
- * Gets the framebuffer size in pixels.
+ * Gets the framebuffer layout (width, height, and screen regions)
* @note This method is thread-safe
*/
- const std::pair<unsigned,unsigned> GetFramebufferSize() const {
- return framebuffer_size;
+ const FramebufferLayout& GetFramebufferLayout() const {
+ return framebuffer_layout;
}
/**
@@ -118,11 +136,11 @@ protected:
}
/**
- * Update internal framebuffer size with the given parameter.
+ * Update framebuffer layout with the given parameter.
* @note EmuWindow implementations will usually use this in window resize event handlers.
*/
- void NotifyFramebufferSizeChanged(const std::pair<unsigned,unsigned>& size) {
- framebuffer_size = size;
+ void NotifyFramebufferLayoutChanged(const FramebufferLayout& layout) {
+ framebuffer_layout = layout;
}
/**
@@ -143,7 +161,7 @@ private:
// By default, ignore this request and do nothing.
}
- std::pair<unsigned,unsigned> framebuffer_size;
+ FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
unsigned client_area_width; ///< Current client width, should be set by window impl.
unsigned client_area_height; ///< Current client height, should be set by window impl.
diff --git a/src/common/extended_trace.cpp b/src/common/extended_trace.cpp
deleted file mode 100644
index cf7c346d4..000000000
--- a/src/common/extended_trace.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-// --------------------------------------------------------------------------------------
-//
-// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
-// For companies(Austin,TX): If you would like to get my resume, send an email.
-//
-// The source is free, but if you want to use it, mention my name and e-mail address
-//
-// History:
-// 1.0 Initial version Zoltan Csizmadia
-// 1.1 WhineCube version Masken
-// 1.2 Dolphin version Masken
-//
-// --------------------------------------------------------------------------------------
-
-#if defined(WIN32)
-#include <cstdio>
-#include <windows.h>
-#include "common/extended_trace.h"
-#include "common/string_util.h"
-using namespace std;
-
-#include <tchar.h>
-#include <ImageHlp.h>
-
-#define BUFFERSIZE 0x200
-#pragma warning(disable:4996)
-
-// Unicode safe char* -> TCHAR* conversion
-void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
-{
-#if defined(UNICODE)||defined(_UNICODE)
- ULONG index = 0;
- PCSTR lpAct = lpszIn;
-
- for( ; ; lpAct++ )
- {
- lpszOut[index++] = (TCHAR)(*lpAct);
- if ( *lpAct == 0 )
- break;
- }
-#else
- // This is trivial :)
- strcpy( lpszOut, lpszIn );
-#endif
-}
-
-// Let's figure out the path for the symbol files
-// Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath
-// Note: There is no size check for lpszSymbolPath!
-static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath )
-{
- CHAR lpszPath[BUFFERSIZE];
-
- // Creating the default path
- // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;"
- strcpy( lpszSymbolPath, "." );
-
- // environment variable _NT_SYMBOL_PATH
- if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
- {
- strcat( lpszSymbolPath, ";" );
- strcat( lpszSymbolPath, lpszPath );
- }
-
- // environment variable _NT_ALTERNATE_SYMBOL_PATH
- if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
- {
- strcat( lpszSymbolPath, ";" );
- strcat( lpszSymbolPath, lpszPath );
- }
-
- // environment variable SYSTEMROOT
- if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) )
- {
- strcat( lpszSymbolPath, ";" );
- strcat( lpszSymbolPath, lpszPath );
- strcat( lpszSymbolPath, ";" );
-
- // SYSTEMROOT\System32
- strcat( lpszSymbolPath, lpszPath );
- strcat( lpszSymbolPath, "\\System32" );
- }
-
- // Add user defined path
- if ( lpszIniPath != nullptr )
- if ( lpszIniPath[0] != '\0' )
- {
- strcat( lpszSymbolPath, ";" );
- strcat( lpszSymbolPath, lpszIniPath );
- }
-}
-
-// Uninitialize the loaded symbol files
-BOOL UninitSymInfo() {
- return SymCleanup( GetCurrentProcess() );
-}
-
-// Initializes the symbol files
-BOOL InitSymInfo( PCSTR lpszInitialSymbolPath )
-{
- CHAR lpszSymbolPath[BUFFERSIZE];
- DWORD symOptions = SymGetOptions();
-
- symOptions |= SYMOPT_LOAD_LINES;
- symOptions &= ~SYMOPT_UNDNAME;
- SymSetOptions( symOptions );
- InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath );
-
- return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE);
-}
-
-// Get the module name from a given address
-static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule )
-{
- BOOL ret = FALSE;
- IMAGEHLP_MODULE moduleInfo;
-
- ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) );
- moduleInfo.SizeOfStruct = sizeof(moduleInfo);
-
- if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) )
- {
- // Got it!
- PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule );
- ret = TRUE;
- }
- else
- // Not found :(
- _tcscpy( lpszModule, _T("?") );
-
- return ret;
-}
-
-// Get function prototype and parameter info from ip address and stack address
-static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol )
-{
- BOOL ret = FALSE;
- DWORD dwSymSize = 10000;
- TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
- CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
- LPTSTR lpszParamSep = nullptr;
- LPTSTR lpszParsed = lpszUnDSymbol;
- PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
-
- ::ZeroMemory( pSym, dwSymSize );
- pSym->SizeOfStruct = dwSymSize;
- pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL);
-
- // Set the default to unknown
- _tcscpy( lpszSymbol, _T("?") );
-
- // Get symbol info for IP
-#ifndef _M_X64
- DWORD dwDisp = 0;
- if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
-#else
- //makes it compile but hell im not sure if this works...
- DWORD64 dwDisp = 0;
- if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) )
-#endif
- {
- // Make the symbol readable for humans
- UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE,
- UNDNAME_COMPLETE |
- UNDNAME_NO_THISTYPE |
- UNDNAME_NO_SPECIAL_SYMS |
- UNDNAME_NO_MEMBER_TYPE |
- UNDNAME_NO_MS_KEYWORDS |
- UNDNAME_NO_ACCESS_SPECIFIERS );
-
- // Symbol information is ANSI string
- PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol );
-
- // I am just smarter than the symbol file :)
- if (_tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0)
- _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)"));
- else if (_tcscmp(lpszUnDSymbol, _T("_main")) == 0)
- _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)"));
- else if (_tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0)
- _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()"));
- else if (_tcscmp(lpszUnDSymbol, _T("_wmain")) == 0)
- _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)"));
- else if (_tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0)
- _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()"));
-
- lpszSymbol[0] = _T('\0');
-
- // Let's go through the stack, and modify the function prototype, and insert the actual
- // parameter values from the stack
- if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr)
- {
- ULONG index = 0;
- for( ; ; index++ )
- {
- lpszParamSep = _tcschr( lpszParsed, _T(',') );
- if ( lpszParamSep == nullptr )
- break;
-
- *lpszParamSep = _T('\0');
-
- _tcscat( lpszSymbol, lpszParsed );
- _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) );
-
- lpszParsed = lpszParamSep + 1;
- }
-
- lpszParamSep = _tcschr( lpszParsed, _T(')') );
- if ( lpszParamSep != nullptr )
- {
- *lpszParamSep = _T('\0');
-
- _tcscat( lpszSymbol, lpszParsed );
- _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) );
-
- lpszParsed = lpszParamSep + 1;
- }
- }
-
- _tcscat( lpszSymbol, lpszParsed );
-
- ret = TRUE;
- }
- GlobalFree( pSym );
-
- return ret;
-}
-
-// Get source file name and line number from IP address
-// The output format is: "sourcefile(linenumber)" or
-// "modulename!address" or
-// "address"
-static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
-{
- BOOL ret = FALSE;
- IMAGEHLP_LINE lineInfo;
- DWORD dwDisp;
- TCHAR lpszFileName[BUFFERSIZE] = _T("");
- TCHAR lpModuleInfo[BUFFERSIZE] = _T("");
-
- _tcscpy( lpszSourceInfo, _T("?(?)") );
-
- ::ZeroMemory( &lineInfo, sizeof( lineInfo ) );
- lineInfo.SizeOfStruct = sizeof( lineInfo );
-
- if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) )
- {
- // Got it. Let's use "sourcefile(linenumber)" format
- PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
- TCHAR fname[_MAX_FNAME];
- TCHAR ext[_MAX_EXT];
- _tsplitpath(lpszFileName, nullptr, nullptr, fname, ext);
- _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber );
- ret = TRUE;
- }
- else
- {
- // There is no source file information. :(
- // Let's use the "modulename!address" format
- GetModuleNameFromAddress( address, lpModuleInfo );
-
- if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0'))
- // There is no modulename information. :((
- // Let's use the "address" format
- _stprintf( lpszSourceInfo, _T("0x%08X"), address );
- else
- _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address );
-
- ret = FALSE;
- }
-
- return ret;
-}
-
-void PrintFunctionAndSourceInfo(FILE* file, const STACKFRAME& callstack)
-{
- TCHAR symInfo[BUFFERSIZE] = _T("?");
- TCHAR srcInfo[BUFFERSIZE] = _T("?");
-
- GetFunctionInfoFromAddresses((ULONG)callstack.AddrPC.Offset, (ULONG)callstack.AddrFrame.Offset, symInfo);
- GetSourceInfoFromAddress((ULONG)callstack.AddrPC.Offset, srcInfo);
- etfprint(file, " " + Common::TStrToUTF8(srcInfo) + " : " + Common::TStrToUTF8(symInfo) + "\n");
-}
-
-void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file )
-{
- STACKFRAME callStack;
- BOOL bResult;
- CONTEXT context;
- HANDLE hProcess = GetCurrentProcess();
-
- // If it's not this thread, let's suspend it, and resume it at the end
- if ( hThread != GetCurrentThread() )
- if ( SuspendThread( hThread ) == -1 )
- {
- // whaaat ?!
- etfprint(file, "Call stack info failed\n");
- return;
- }
-
- ::ZeroMemory( &context, sizeof(context) );
- context.ContextFlags = CONTEXT_FULL;
-
- if ( !GetThreadContext( hThread, &context ) )
- {
- etfprint(file, "Call stack info failed\n");
- return;
- }
-
- ::ZeroMemory( &callStack, sizeof(callStack) );
-#ifndef _M_X64
- callStack.AddrPC.Offset = context.Eip;
- callStack.AddrStack.Offset = context.Esp;
- callStack.AddrFrame.Offset = context.Ebp;
-#else
- callStack.AddrPC.Offset = context.Rip;
- callStack.AddrStack.Offset = context.Rsp;
- callStack.AddrFrame.Offset = context.Rbp;
-#endif
- callStack.AddrPC.Mode = AddrModeFlat;
- callStack.AddrStack.Mode = AddrModeFlat;
- callStack.AddrFrame.Mode = AddrModeFlat;
-
- etfprint(file, "Call stack info: \n");
- etfprint(file, lpszMessage);
-
- PrintFunctionAndSourceInfo(file, callStack);
-
- for( ULONG index = 0; ; index++ )
- {
- bResult = StackWalk(
- IMAGE_FILE_MACHINE_I386,
- hProcess,
- hThread,
- &callStack,
- nullptr,
- nullptr,
- SymFunctionTableAccess,
- SymGetModuleBase,
- nullptr);
-
- if ( index == 0 )
- continue;
-
- if( !bResult || callStack.AddrFrame.Offset == 0 )
- break;
-
- PrintFunctionAndSourceInfo(file, callStack);
-
- }
-
- if ( hThread != GetCurrentThread() )
- ResumeThread( hThread );
-}
-
-void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, DWORD esp, DWORD ebp )
-{
- STACKFRAME callStack;
- BOOL bResult;
- TCHAR symInfo[BUFFERSIZE] = _T("?");
- TCHAR srcInfo[BUFFERSIZE] = _T("?");
- HANDLE hProcess = GetCurrentProcess();
-
- // If it's not this thread, let's suspend it, and resume it at the end
- if ( hThread != GetCurrentThread() )
- if ( SuspendThread( hThread ) == -1 )
- {
- // whaaat ?!
- etfprint(file, "Call stack info failed\n");
- return;
- }
-
- ::ZeroMemory( &callStack, sizeof(callStack) );
- callStack.AddrPC.Offset = eip;
- callStack.AddrStack.Offset = esp;
- callStack.AddrFrame.Offset = ebp;
- callStack.AddrPC.Mode = AddrModeFlat;
- callStack.AddrStack.Mode = AddrModeFlat;
- callStack.AddrFrame.Mode = AddrModeFlat;
-
- etfprint(file, "Call stack info: \n");
- etfprint(file, lpszMessage);
-
- PrintFunctionAndSourceInfo(file, callStack);
-
- for( ULONG index = 0; ; index++ )
- {
- bResult = StackWalk(
- IMAGE_FILE_MACHINE_I386,
- hProcess,
- hThread,
- &callStack,
- nullptr,
- nullptr,
- SymFunctionTableAccess,
- SymGetModuleBase,
- nullptr);
-
- if ( index == 0 )
- continue;
-
- if( !bResult || callStack.AddrFrame.Offset == 0 )
- break;
-
- PrintFunctionAndSourceInfo(file, callStack);
- }
-
- if ( hThread != GetCurrentThread() )
- ResumeThread( hThread );
-}
-
-char g_uefbuf[2048];
-
-void etfprintf(FILE *file, const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- int len = vsprintf(g_uefbuf, format, ap);
- fwrite(g_uefbuf, 1, len, file);
- va_end(ap);
-}
-
-void etfprint(FILE *file, const std::string &text)
-{
- size_t len = text.length();
- fwrite(text.data(), 1, len, file);
-}
-
-#endif //WIN32
diff --git a/src/common/extended_trace.h b/src/common/extended_trace.h
deleted file mode 100644
index ed3113a24..000000000
--- a/src/common/extended_trace.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// -----------------------------------------------------------------------------------------
-//
-// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
-// For companies(Austin,TX): If you would like to get my resume, send an email.
-//
-// The source is free, but if you want to use it, mention my name and e-mail address
-//
-// History:
-// 1.0 Initial version Zoltan Csizmadia
-// 1.1 WhineCube version Masken
-// 1.2 Dolphin version Masken
-//
-// ----------------------------------------------------------------------------------------
-
-#pragma once
-
-#if defined(WIN32)
-
-#include <windows.h>
-#include <tchar.h>
-
-#include <string>
-
-#pragma comment( lib, "imagehlp.lib" )
-
-#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) InitSymInfo( IniSymbolPath )
-#define EXTENDEDTRACEUNINITIALIZE() UninitSymInfo()
-#define STACKTRACE(file) StackTrace( GetCurrentThread(), "", file)
-#define STACKTRACE2(file, eip, esp, ebp) StackTrace(GetCurrentThread(), "", file, eip, esp, ebp)
-// class File;
-
-BOOL InitSymInfo( PCSTR );
-BOOL UninitSymInfo();
-void StackTrace(HANDLE, char const* msg, FILE *file);
-void StackTrace(HANDLE, char const* msg, FILE *file, DWORD eip, DWORD esp, DWORD ebp);
-
-// functions by Masken
-void etfprintf(FILE *file, const char *format, ...);
-void etfprint(FILE *file, const std::string &text);
-#define UEFBUFSIZE 2048
-extern char g_uefbuf[UEFBUFSIZE];
-
-#else // not WIN32
-
-#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) ((void)0)
-#define EXTENDEDTRACEUNINITIALIZE() ((void)0)
-#define STACKTRACE(file) ((void)0)
-#define STACKTRACE2(file, eip, esp, ebp) ((void)0)
-
-#endif // WIN32
diff --git a/src/common/file_search.cpp b/src/common/file_search.cpp
deleted file mode 100644
index b3a0a84fb..000000000
--- a/src/common/file_search.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-
-#include "common/common.h"
-
-#ifndef _WIN32
-#include <dirent.h>
-#else
-#include <windows.h>
-#endif
-
-#include <algorithm>
-
-#include "common/file_search.h"
-#include "common/string_util.h"
-
-
-CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories)
-{
- // Reverse the loop order for speed?
- for (size_t j = 0; j < _rSearchStrings.size(); j++)
- {
- for (size_t i = 0; i < _rDirectories.size(); i++)
- {
- FindFiles(_rSearchStrings[j], _rDirectories[i]);
- }
- }
-}
-
-
-void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath)
-{
- std::string GCMSearchPath;
- Common::BuildCompleteFilename(GCMSearchPath, _strPath, _searchString);
-#ifdef _WIN32
- WIN32_FIND_DATA findData;
- HANDLE FindFirst = FindFirstFile(Common::UTF8ToTStr(GCMSearchPath).c_str(), &findData);
-
- if (FindFirst != INVALID_HANDLE_VALUE)
- {
- bool bkeepLooping = true;
-
- while (bkeepLooping)
- {
- if (findData.cFileName[0] != '.')
- {
- std::string strFilename;
- Common::BuildCompleteFilename(strFilename, _strPath, Common::TStrToUTF8(findData.cFileName));
- m_FileNames.push_back(strFilename);
- }
-
- bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false;
- }
- }
- FindClose(FindFirst);
-
-
-#else
- // TODO: super lame/broken
-
- auto end_match(_searchString);
-
- // assuming we have a "*.blah"-like pattern
- if (!end_match.empty() && end_match[0] == '*')
- end_match.erase(0, 1);
-
- // ugly
- if (end_match == ".*")
- end_match.clear();
-
- DIR* dir = opendir(_strPath.c_str());
-
- if (!dir)
- return;
-
- while (auto const dp = readdir(dir))
- {
- std::string found(dp->d_name);
-
- if ((found != ".") && (found != "..")
- && (found.size() >= end_match.size())
- && std::equal(end_match.rbegin(), end_match.rend(), found.rbegin()))
- {
- std::string full_name;
- if (_strPath.c_str()[_strPath.size()-1] == DIR_SEP_CHR)
- full_name = _strPath + found;
- else
- full_name = _strPath + DIR_SEP + found;
-
- m_FileNames.push_back(full_name);
- }
- }
-
- closedir(dir);
-#endif
-}
-
-const CFileSearch::XStringVector& CFileSearch::GetFileNames() const
-{
- return m_FileNames;
-}
diff --git a/src/common/file_search.h b/src/common/file_search.h
deleted file mode 100644
index 55ca02638..000000000
--- a/src/common/file_search.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-class CFileSearch
-{
-public:
- typedef std::vector<std::string>XStringVector;
-
- CFileSearch(const XStringVector& _rSearchStrings, const XStringVector& _rDirectories);
- const XStringVector& GetFileNames() const;
-
-private:
-
- void FindFiles(const std::string& _searchString, const std::string& _strPath);
-
- XStringVector m_FileNames;
-};
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 706e7c842..4ef4918d7 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -16,7 +16,10 @@
#include <tchar.h>
#else
#include <sys/param.h>
+#include <sys/types.h>
#include <dirent.h>
+#include <pwd.h>
+#include <unistd.h>
#endif
#if defined(__APPLE__)
@@ -622,15 +625,64 @@ std::string GetBundleDirectory()
#ifdef _WIN32
std::string& GetExeDirectory()
{
- static std::string DolphinPath;
- if (DolphinPath.empty())
+ static std::string exe_path;
+ if (exe_path.empty())
{
- TCHAR Dolphin_exe_Path[2048];
- GetModuleFileName(nullptr, Dolphin_exe_Path, 2048);
- DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path);
- DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\'));
+ TCHAR tchar_exe_path[2048];
+ GetModuleFileName(nullptr, tchar_exe_path, 2048);
+ exe_path = Common::TStrToUTF8(tchar_exe_path);
+ exe_path = exe_path.substr(0, exe_path.find_last_of('\\'));
}
- return DolphinPath;
+ return exe_path;
+}
+#else
+/**
+ * @return The user’s home directory on POSIX systems
+ */
+static const std::string& GetHomeDirectory() {
+ static std::string home_path;
+ if (home_path.empty()) {
+ const char* envvar = getenv("HOME");
+ if (envvar) {
+ home_path = envvar;
+ } else {
+ auto pw = getpwuid(getuid());
+ ASSERT_MSG(pw, "$HOME isn’t defined, and the current user can’t be found in /etc/passwd.");
+ home_path = pw->pw_dir;
+ }
+ }
+ return home_path;
+}
+
+/**
+ * Follows the XDG Base Directory Specification to get a directory path
+ * @param envvar The XDG environment variable to get the value from
+ * @return The directory path
+ * @sa http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ */
+static const std::string GetUserDirectory(const std::string& envvar) {
+ const char* directory = getenv(envvar.c_str());
+
+ std::string user_dir;
+ if (directory) {
+ user_dir = directory;
+ } else {
+ std::string subdirectory;
+ if (envvar == "XDG_DATA_HOME")
+ subdirectory = DIR_SEP ".local" DIR_SEP "share";
+ else if (envvar == "XDG_CONFIG_HOME")
+ subdirectory = DIR_SEP ".config";
+ else if (envvar == "XDG_CACHE_HOME")
+ subdirectory = DIR_SEP ".cache";
+ else
+ ASSERT_MSG(false, "Unknown XDG variable %s.", envvar.c_str());
+ user_dir = GetHomeDirectory() + subdirectory;
+ }
+
+ ASSERT_MSG(!user_dir.empty(), "User directory %s musn’t be empty.", envvar.c_str());
+ ASSERT_MSG(user_dir[0] == '/', "User directory %s must be absolute.", envvar.c_str());
+
+ return user_dir;
}
#endif
@@ -661,20 +713,27 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
if (paths[D_USER_IDX].empty())
{
#ifdef _WIN32
- paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
+ paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
+ paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
+ paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
#else
- if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR))
- paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
- else
- paths[D_USER_IDX] = std::string(getenv("HOME") ?
- getenv("HOME") : getenv("PWD") ?
- getenv("PWD") : "") + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
+ paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
+ paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
+ paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
+ } else {
+ std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
+ std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
+ std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
+
+ paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ }
#endif
- paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
- paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
diff --git a/src/common/hash.cpp b/src/common/hash.cpp
index fe2c9e636..0624dab8d 100644
--- a/src/common/hash.cpp
+++ b/src/common/hash.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include "common/hash.h"
#if _M_SSE >= 0x402
@@ -155,7 +156,7 @@ u64 GetMurmurHash3(const u8 *src, int len, u32 samples)
const u8 * data = (const u8*)src;
const int nblocks = len / 16;
u32 Step = (len / 8);
- if(samples == 0) samples = max(Step, 1u);
+ if(samples == 0) samples = std::max(Step, 1u);
Step = Step / samples;
if(Step < 1) Step = 1;
@@ -233,7 +234,7 @@ u64 GetCRC32(const u8 *src, int len, u32 samples)
u32 Step = (len / 8);
const u64 *data = (const u64 *)src;
const u64 *end = data + Step;
- if(samples == 0) samples = max(Step, 1u);
+ if(samples == 0) samples = std::max(Step, 1u);
Step = Step / samples;
if(Step < 1) Step = 1;
while(data < end)
@@ -265,7 +266,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
u32 Step = (len / 8);
const u64 *data = (const u64 *)src;
const u64 *end = data + Step;
- if(samples == 0) samples = max(Step, 1u);
+ if(samples == 0) samples = std::max(Step, 1u);
Step = Step / samples;
if(Step < 1) Step = 1;
while(data < end)
@@ -308,7 +309,7 @@ u64 GetCRC32(const u8 *src, int len, u32 samples)
u32 Step = (len/4);
const u32 *data = (const u32 *)src;
const u32 *end = data + Step;
- if(samples == 0) samples = max(Step, 1u);
+ if(samples == 0) samples = std::max(Step, 1u);
Step = Step / samples;
if(Step < 1) Step = 1;
while(data < end)
@@ -380,7 +381,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples)
u32 out[2];
const int nblocks = len / 8;
u32 Step = (len / 4);
- if(samples == 0) samples = max(Step, 1u);
+ if(samples == 0) samples = std::max(Step, 1u);
Step = Step / samples;
if(Step < 1) Step = 1;
@@ -456,7 +457,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
u32 Step = (len / 8);
const u64 *data = (const u64 *)src;
const u64 *end = data + Step;
- if(samples == 0) samples = max(Step, 1u);
+ if(samples == 0) samples = std::max(Step, 1u);
Step = Step / samples;
if(Step < 1) Step = 1;
while(data < end)
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 8fee20a83..7b479b569 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -33,6 +33,7 @@ static std::shared_ptr<Logger> global_logger;
CLS(Service) \
SUB(Service, SRV) \
SUB(Service, FS) \
+ SUB(Service, ERR) \
SUB(Service, APT) \
SUB(Service, GSP) \
SUB(Service, AC) \
@@ -134,9 +135,18 @@ Entry CreateEntry(Class log_class, Level log_level,
return std::move(entry);
}
+static Filter* filter;
+
+void SetFilter(Filter* new_filter) {
+ filter = new_filter;
+}
+
void LogMessage(Class log_class, Level log_level,
const char* filename, unsigned int line_nr, const char* function,
const char* format, ...) {
+ if (!filter->CheckMessage(log_class, log_level))
+ return;
+
va_list args;
va_start(args, format);
Entry entry = CreateEntry(log_class, log_level,
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 1c44c929e..3114f864c 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -10,6 +10,7 @@
#include "common/concurrent_ring_buffer.h"
+#include "common/logging/filter.h"
#include "common/logging/log.h"
namespace Log {
@@ -131,4 +132,6 @@ Entry CreateEntry(Class log_class, Level log_level,
/// Initializes the default Logger.
std::shared_ptr<Logger> InitGlobalLogger();
+void SetFilter(Filter* filter);
+
}
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
index c3da9989f..b53e4e633 100644
--- a/src/common/logging/filter.h
+++ b/src/common/logging/filter.h
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#pragma once
+
#include <array>
#include <string>
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 6c5ca3968..7b67b3c07 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -53,6 +53,7 @@ enum class Class : ClassType {
/// should have its own subclass.
Service_SRV, ///< The SRV (Service Directory) implementation
Service_FS, ///< The FS (Filesystem) service implementation
+ Service_ERR, ///< The ERR (Error) port implementation
Service_APT, ///< The APT (Applets) service
Service_GSP, ///< The GSP (GPU control) service
Service_AC, ///< The AC (WiFi status) service
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index ef5739d84..36c91c4f6 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -11,7 +11,6 @@
#endif
#include "common/logging/backend.h"
-#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
@@ -116,7 +115,7 @@ void PrintColoredMessage(const Entry& entry) {
#endif
}
-void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) {
+void TextLoggingLoop(std::shared_ptr<Logger> logger) {
std::array<Entry, 256> entry_buffer;
while (true) {
@@ -126,9 +125,7 @@ void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) {
}
for (size_t i = 0; i < num_entries; ++i) {
const Entry& entry = entry_buffer[i];
- if (filter->CheckMessage(entry.log_class, entry.log_level)) {
- PrintColoredMessage(entry);
- }
+ PrintColoredMessage(entry);
}
}
}
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
index 2f05794f0..8474a1904 100644
--- a/src/common/logging/text_formatter.h
+++ b/src/common/logging/text_formatter.h
@@ -11,7 +11,6 @@ namespace Log {
class Logger;
struct Entry;
-class Filter;
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
@@ -36,6 +35,6 @@ void PrintColoredMessage(const Entry& entry);
* Logging loop that repeatedly reads messages from the provided logger and prints them to the
* console. It is the baseline barebones log outputter.
*/
-void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter);
+void TextLoggingLoop(std::shared_ptr<Logger> logger);
}
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp
index a20361d6f..76c70701d 100644
--- a/src/common/mem_arena.cpp
+++ b/src/common/mem_arena.cpp
@@ -116,7 +116,7 @@ void MemArena::GrabLowMemSpace(size_t size)
GetSystemInfo(&sysInfo);
#elif defined(ANDROID)
// Use ashmem so we don't have to allocate a file on disk!
- fd = ashmem_create_region("PPSSPP_RAM", size);
+ fd = ashmem_create_region("Citra_RAM", size);
// Note that it appears that ashmem is pinned by default, so no need to pin.
if (fd < 0)
{
@@ -218,7 +218,7 @@ u8* MemArena::Find4GBBase()
void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0);
if (base == MAP_FAILED) {
- PanicAlert("Failed to map 256 MB of memory space: %s", strerror(errno));
+ LOG_ERROR(Common_Memory, "Failed to map 256 MB of memory space: %s", strerror(errno));
return 0;
}
munmap(base, 0x10000000);
@@ -338,7 +338,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
// address space.
if (!Memory_TryBase(base, views, num_views, flags, arena))
{
- PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
+ LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
return 0;
}
#elif defined(_WIN32)
@@ -363,12 +363,11 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
if (!Memory_TryBase(base, views, num_views, flags, arena))
{
LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
- PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
return 0;
}
#endif
if (base_attempts)
- PanicAlert("No possible memory base pointer found!");
+ LOG_ERROR(Common_Memory, "No possible memory base pointer found!");
return base;
}
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
index 8f982da89..7e69d31cb 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -56,7 +56,7 @@ void* AllocateExecutableMemory(size_t size, bool low)
{
ptr = nullptr;
#endif
- PanicAlert("Failed to allocate executable memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
}
#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT)
else
@@ -72,7 +72,7 @@ void* AllocateExecutableMemory(size_t size, bool low)
#if defined(_M_X64)
if ((u64)ptr >= 0x80000000 && low == true)
- PanicAlert("Executable memory ended up above 2GB!");
+ LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
#endif
return ptr;
@@ -94,7 +94,7 @@ void* AllocateMemoryPages(size_t size)
// (unsigned long)size);
if (ptr == nullptr)
- PanicAlert("Failed to allocate raw memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
return ptr;
}
@@ -117,7 +117,7 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
// (unsigned long)size);
if (ptr == nullptr)
- PanicAlert("Failed to allocate aligned memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
return ptr;
}
@@ -129,7 +129,7 @@ void FreeMemoryPages(void* ptr, size_t size)
#ifdef _WIN32
if (!VirtualFree(ptr, 0, MEM_RELEASE))
- PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg());
+ LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n%s", GetLastErrorMsg());
ptr = nullptr; // Is this our responsibility?
#else
@@ -155,7 +155,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
- PanicAlert("WriteProtectMemory failed!\n%s", GetLastErrorMsg());
+ LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n%s", GetLastErrorMsg());
#else
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
#endif
@@ -166,7 +166,7 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
- PanicAlert("UnWriteProtectMemory failed!\n%s", GetLastErrorMsg());
+ LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n%s", GetLastErrorMsg());
#else
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
#endif
diff --git a/src/common/msg_handler.cpp b/src/common/msg_handler.cpp
deleted file mode 100644
index 4a47b518e..000000000
--- a/src/common/msg_handler.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <cstdio>
-
-#include "common/common.h" // Local
-#include "common/string_util.h"
-
-bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style);
-static MsgAlertHandler msg_handler = DefaultMsgHandler;
-static bool AlertEnabled = true;
-
-std::string DefaultStringTranslator(const char* text);
-static StringTranslator str_translator = DefaultStringTranslator;
-
-// Select which of these functions that are used for message boxes. If
-// wxWidgets is enabled we will use wxMsgAlert() that is defined in Main.cpp
-void RegisterMsgAlertHandler(MsgAlertHandler handler)
-{
- msg_handler = handler;
-}
-
-// Select translation function. For wxWidgets use wxStringTranslator in Main.cpp
-void RegisterStringTranslator(StringTranslator translator)
-{
- str_translator = translator;
-}
-
-// enable/disable the alert handler
-void SetEnableAlert(bool enable)
-{
- AlertEnabled = enable;
-}
-
-// This is the first stop for gui alerts where the log is updated and the
-// correct window is shown
-bool MsgAlert(bool yes_no, int Style, const char* format, ...)
-{
- // Read message and write it to the log
- std::string caption;
- char buffer[2048];
-
- static std::string info_caption;
- static std::string warn_caption;
- static std::string ques_caption;
- static std::string crit_caption;
-
- if (!info_caption.length())
- {
- info_caption = str_translator(_trans("Information"));
- ques_caption = str_translator(_trans("Question"));
- warn_caption = str_translator(_trans("Warning"));
- crit_caption = str_translator(_trans("Critical"));
- }
-
- switch(Style)
- {
- case INFORMATION:
- caption = info_caption;
- break;
- case QUESTION:
- caption = ques_caption;
- break;
- case WARNING:
- caption = warn_caption;
- break;
- case CRITICAL:
- caption = crit_caption;
- break;
- }
-
- va_list args;
- va_start(args, format);
- Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args);
- va_end(args);
-
- LOG_INFO(Common, "%s: %s", caption.c_str(), buffer);
-
- // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored
- if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL))
- return msg_handler(caption.c_str(), buffer, yes_no, Style);
-
- return true;
-}
-
-// Default non library dependent panic alert
-bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style)
-{
-//#ifdef _WIN32
-// int STYLE = MB_ICONINFORMATION;
-// if (Style == QUESTION) STYLE = MB_ICONQUESTION;
-// if (Style == WARNING) STYLE = MB_ICONWARNING;
-//
-// return IDYES == MessageBox(0, UTF8ToTStr(text).c_str(), UTF8ToTStr(caption).c_str(), STYLE | (yes_no ? MB_YESNO : MB_OK));
-//#else
- printf("%s\n", text);
- return true;
-//#endif
-}
-
-// Default (non) translator
-std::string DefaultStringTranslator(const char* text)
-{
- return text;
-}
-
diff --git a/src/common/msg_handler.h b/src/common/msg_handler.h
deleted file mode 100644
index 421f93e23..000000000
--- a/src/common/msg_handler.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <string>
-
-// Message alerts
-enum MSG_TYPE
-{
- INFORMATION,
- QUESTION,
- WARNING,
- CRITICAL
-};
-
-typedef bool (*MsgAlertHandler)(const char* caption, const char* text,
- bool yes_no, int Style);
-typedef std::string (*StringTranslator)(const char* text);
-
-void RegisterMsgAlertHandler(MsgAlertHandler handler);
-void RegisterStringTranslator(StringTranslator translator);
-
-extern bool MsgAlert(bool yes_no, int Style, const char* format, ...)
-#ifdef __GNUC__
- __attribute__((format(printf, 3, 4)))
-#endif
- ;
-void SetEnableAlert(bool enable);
-
-#ifdef _MSC_VER
- #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__)
- #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__)
- #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__)
- #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__)
- #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__)
- // Use these macros (that do the same thing) if the message should be translated.
- #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__)
- #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__)
- #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__)
- #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__)
- #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__)
-#else
- #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__)
- #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__)
- #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__)
- #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__)
- #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__)
- // Use these macros (that do the same thing) if the message should be translated.
- #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__)
- #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__)
- #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__)
- #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__)
- #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__)
-#endif
diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp
new file mode 100644
index 000000000..65c3df167
--- /dev/null
+++ b/src/common/profiler.cpp
@@ -0,0 +1,182 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/profiler.h"
+#include "common/profiler_reporting.h"
+#include "common/assert.h"
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013.
+#define NOMINMAX
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h> // For QueryPerformanceCounter/Frequency
+#endif
+
+namespace Common {
+namespace Profiling {
+
+#if ENABLE_PROFILING
+thread_local Timer* Timer::current_timer = nullptr;
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
+QPCClock::time_point QPCClock::now() {
+ static LARGE_INTEGER freq;
+ // Use this dummy local static to ensure this gets initialized once.
+ static BOOL dummy = QueryPerformanceFrequency(&freq);
+
+ LARGE_INTEGER ticks;
+ QueryPerformanceCounter(&ticks);
+
+ // This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The
+ // correct way to approach this would be to just return ticks as a time_point and then subtract
+ // and do this conversion when creating a duration from two time_points, however, as far as I
+ // could tell the C++ requirements for these types are incompatible with this approach.
+ return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart));
+}
+#endif
+
+TimingCategory::TimingCategory(const char* name, TimingCategory* parent)
+ : accumulated_duration(0) {
+
+ ProfilingManager& manager = GetProfilingManager();
+ category_id = manager.RegisterTimingCategory(this, name);
+ if (parent != nullptr)
+ manager.SetTimingCategoryParent(category_id, parent->category_id);
+}
+
+ProfilingManager::ProfilingManager()
+ : last_frame_end(Clock::now()), this_frame_start(Clock::now()) {
+}
+
+unsigned int ProfilingManager::RegisterTimingCategory(TimingCategory* category, const char* name) {
+ TimingCategoryInfo info;
+ info.category = category;
+ info.name = name;
+ info.parent = TimingCategoryInfo::NO_PARENT;
+
+ unsigned int id = (unsigned int)timing_categories.size();
+ timing_categories.push_back(std::move(info));
+
+ return id;
+}
+
+void ProfilingManager::SetTimingCategoryParent(unsigned int category, unsigned int parent) {
+ ASSERT(category < timing_categories.size());
+ ASSERT(parent < timing_categories.size());
+
+ timing_categories[category].parent = parent;
+}
+
+void ProfilingManager::BeginFrame() {
+ this_frame_start = Clock::now();
+}
+
+void ProfilingManager::FinishFrame() {
+ Clock::time_point now = Clock::now();
+
+ results.interframe_time = now - last_frame_end;
+ results.frame_time = now - this_frame_start;
+
+ results.time_per_category.resize(timing_categories.size());
+ for (size_t i = 0; i < timing_categories.size(); ++i) {
+ results.time_per_category[i] = timing_categories[i].category->GetAccumulatedTime();
+ }
+
+ last_frame_end = now;
+}
+
+TimingResultsAggregator::TimingResultsAggregator(size_t window_size)
+ : max_window_size(window_size), window_size(0) {
+ interframe_times.resize(window_size, Duration::zero());
+ frame_times.resize(window_size, Duration::zero());
+}
+
+void TimingResultsAggregator::Clear() {
+ window_size = cursor = 0;
+}
+
+void TimingResultsAggregator::SetNumberOfCategories(size_t n) {
+ size_t old_size = times_per_category.size();
+ if (n == old_size)
+ return;
+
+ times_per_category.resize(n);
+
+ for (size_t i = old_size; i < n; ++i) {
+ times_per_category[i].resize(max_window_size, Duration::zero());
+ }
+}
+
+void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) {
+ SetNumberOfCategories(frame_result.time_per_category.size());
+
+ interframe_times[cursor] = frame_result.interframe_time;
+ frame_times[cursor] = frame_result.frame_time;
+ for (size_t i = 0; i < frame_result.time_per_category.size(); ++i) {
+ times_per_category[i][cursor] = frame_result.time_per_category[i];
+ }
+
+ ++cursor;
+ if (cursor == max_window_size)
+ cursor = 0;
+ if (window_size < max_window_size)
+ ++window_size;
+}
+
+static AggregatedDuration AggregateField(const std::vector<Duration>& v, size_t len) {
+ AggregatedDuration result;
+ result.avg = Duration::zero();
+
+ result.min = result.max = (len == 0 ? Duration::zero() : v[0]);
+
+ for (size_t i = 1; i < len; ++i) {
+ Duration value = v[i];
+ result.avg += value;
+ result.min = std::min(result.min, value);
+ result.max = std::max(result.max, value);
+ }
+ if (len != 0)
+ result.avg /= len;
+
+ return result;
+}
+
+static float tof(Common::Profiling::Duration dur) {
+ using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
+ return std::chrono::duration_cast<FloatMs>(dur).count();
+}
+
+AggregatedFrameResult TimingResultsAggregator::GetAggregatedResults() const {
+ AggregatedFrameResult result;
+
+ result.interframe_time = AggregateField(interframe_times, window_size);
+ result.frame_time = AggregateField(frame_times, window_size);
+
+ if (result.interframe_time.avg != Duration::zero()) {
+ result.fps = 1000.0f / tof(result.interframe_time.avg);
+ } else {
+ result.fps = 0.0f;
+ }
+
+ result.time_per_category.resize(times_per_category.size());
+ for (size_t i = 0; i < times_per_category.size(); ++i) {
+ result.time_per_category[i] = AggregateField(times_per_category[i], window_size);
+ }
+
+ return result;
+}
+
+ProfilingManager& GetProfilingManager() {
+ // Takes advantage of "magic" static initialization for race-free initialization.
+ static ProfilingManager manager;
+ return manager;
+}
+
+SynchronizedRef<TimingResultsAggregator> GetTimingResultsAggregator() {
+ static SynchronizedWrapper<TimingResultsAggregator> aggregator(30);
+ return SynchronizedRef<TimingResultsAggregator>(aggregator);
+}
+
+} // namespace Profiling
+} // namespace Common
diff --git a/src/common/profiler.h b/src/common/profiler.h
new file mode 100644
index 000000000..3e967b4bc
--- /dev/null
+++ b/src/common/profiler.h
@@ -0,0 +1,152 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <chrono>
+
+#include "common/assert.h"
+#include "common/thread.h"
+
+namespace Common {
+namespace Profiling {
+
+// If this is defined to 0, it turns all Timers into no-ops.
+#ifndef ENABLE_PROFILING
+#define ENABLE_PROFILING 1
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013
+// MSVC up to 2013 doesn't use QueryPerformanceCounter for high_resolution_clock, so it has bad
+// precision. We manually implement a clock based on QPC to get good results.
+
+struct QPCClock {
+ using duration = std::chrono::microseconds;
+ using time_point = std::chrono::time_point<QPCClock>;
+ using rep = duration::rep;
+ using period = duration::period;
+ static const bool is_steady = false;
+
+ static time_point now();
+};
+
+using Clock = QPCClock;
+#else
+using Clock = std::chrono::high_resolution_clock;
+#endif
+
+using Duration = Clock::duration;
+
+/**
+ * Represents a timing category that measured time can be accounted towards. Should be declared as a
+ * global variable and passed to Timers.
+ */
+class TimingCategory final {
+public:
+ TimingCategory(const char* name, TimingCategory* parent = nullptr);
+
+ unsigned int GetCategoryId() const {
+ return category_id;
+ }
+
+ /// Adds some time to this category. Can safely be called from multiple threads at the same time.
+ void AddTime(Duration amount) {
+ std::atomic_fetch_add_explicit(
+ &accumulated_duration, amount.count(),
+ std::memory_order_relaxed);
+ }
+
+ /**
+ * Atomically retrieves the accumulated measured time for this category and resets the counter
+ * to zero. Can be safely called concurrently with AddTime.
+ */
+ Duration GetAccumulatedTime() {
+ return Duration(std::atomic_exchange_explicit(
+ &accumulated_duration, (Duration::rep)0,
+ std::memory_order_relaxed));
+ }
+
+private:
+ unsigned int category_id;
+ std::atomic<Duration::rep> accumulated_duration;
+};
+
+/**
+ * Measures time elapsed between a call to Start and a call to Stop and attributes it to the given
+ * TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be
+ * appropriately paired.
+ *
+ * When a Timer is started, it automatically pauses a previously running timer on the same thread,
+ * which is resumed when it is stopped. As such, no special action needs to be taken to avoid
+ * double-accounting of time on two categories.
+ */
+class Timer {
+public:
+ Timer(TimingCategory& category) : category(category) {
+ }
+
+ void Start() {
+#if ENABLE_PROFILING
+ ASSERT(!running);
+ previous_timer = current_timer;
+ current_timer = this;
+ if (previous_timer != nullptr)
+ previous_timer->StopTiming();
+
+ StartTiming();
+#endif
+ }
+
+ void Stop() {
+#if ENABLE_PROFILING
+ ASSERT(running);
+ StopTiming();
+
+ if (previous_timer != nullptr)
+ previous_timer->StartTiming();
+ current_timer = previous_timer;
+#endif
+ }
+
+private:
+#if ENABLE_PROFILING
+ void StartTiming() {
+ start = Clock::now();
+ running = true;
+ }
+
+ void StopTiming() {
+ auto duration = Clock::now() - start;
+ running = false;
+ category.AddTime(std::chrono::duration_cast<Duration>(duration));
+ }
+
+ Clock::time_point start;
+ bool running = false;
+
+ Timer* previous_timer;
+ static thread_local Timer* current_timer;
+#endif
+
+ TimingCategory& category;
+};
+
+/**
+ * A Timer that automatically starts timing when created and stops at the end of the scope. Should
+ * be used in the majority of cases.
+ */
+class ScopeTimer : public Timer {
+public:
+ ScopeTimer(TimingCategory& category) : Timer(category) {
+ Start();
+ }
+
+ ~ScopeTimer() {
+ Stop();
+ }
+};
+
+} // namespace Profiling
+} // namespace Common
diff --git a/src/common/profiler_reporting.h b/src/common/profiler_reporting.h
new file mode 100644
index 000000000..3abb73315
--- /dev/null
+++ b/src/common/profiler_reporting.h
@@ -0,0 +1,108 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <chrono>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+#include "common/profiler.h"
+#include "common/synchronized_wrapper.h"
+
+namespace Common {
+namespace Profiling {
+
+struct TimingCategoryInfo {
+ static const unsigned int NO_PARENT = -1;
+
+ TimingCategory* category;
+ const char* name;
+ unsigned int parent;
+};
+
+struct ProfilingFrameResult {
+ /// Time since the last delivered frame
+ Duration interframe_time;
+
+ /// Time spent processing a frame, excluding VSync
+ Duration frame_time;
+
+ /// Total amount of time spent inside each category in this frame. Indexed by the category id
+ std::vector<Duration> time_per_category;
+};
+
+class ProfilingManager final {
+public:
+ ProfilingManager();
+
+ unsigned int RegisterTimingCategory(TimingCategory* category, const char* name);
+ void SetTimingCategoryParent(unsigned int category, unsigned int parent);
+
+ const std::vector<TimingCategoryInfo>& GetTimingCategoriesInfo() const {
+ return timing_categories;
+ }
+
+ /// This should be called after swapping screen buffers.
+ void BeginFrame();
+ /// This should be called before swapping screen buffers.
+ void FinishFrame();
+
+ /// Get the timing results from the previous frame. This is updated when you call FinishFrame().
+ const ProfilingFrameResult& GetPreviousFrameResults() const {
+ return results;
+ }
+
+private:
+ std::vector<TimingCategoryInfo> timing_categories;
+ Clock::time_point last_frame_end;
+ Clock::time_point this_frame_start;
+
+ ProfilingFrameResult results;
+};
+
+struct AggregatedDuration {
+ Duration avg, min, max;
+};
+
+struct AggregatedFrameResult {
+ /// Time since the last delivered frame
+ AggregatedDuration interframe_time;
+
+ /// Time spent processing a frame, excluding VSync
+ AggregatedDuration frame_time;
+
+ float fps;
+
+ /// Total amount of time spent inside each category in this frame. Indexed by the category id
+ std::vector<AggregatedDuration> time_per_category;
+};
+
+class TimingResultsAggregator final {
+public:
+ TimingResultsAggregator(size_t window_size);
+
+ void Clear();
+ void SetNumberOfCategories(size_t n);
+
+ void AddFrame(const ProfilingFrameResult& frame_result);
+
+ AggregatedFrameResult GetAggregatedResults() const;
+
+ size_t max_window_size;
+ size_t window_size;
+ size_t cursor;
+
+ std::vector<Duration> interframe_times;
+ std::vector<Duration> frame_times;
+ std::vector<std::vector<Duration>> times_per_category;
+};
+
+ProfilingManager& GetProfilingManager();
+SynchronizedRef<TimingResultsAggregator> GetTimingResultsAggregator();
+
+} // namespace Profiling
+} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index e2d918362..7e37655bf 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -17,18 +17,14 @@
#pragma once
-// Android
-#if defined(ANDROID)
+#if defined(__linux__)
+#include <byteswap.h>
+#elif defined(__FreeBSD__)
#include <sys/endian.h>
-
-#if _BYTE_ORDER == _LITTLE_ENDIAN && !defined(COMMON_LITTLE_ENDIAN)
-#define COMMON_LITTLE_ENDIAN 1
-#elif _BYTE_ORDER == _BIG_ENDIAN && !defined(COMMON_BIG_ENDIAN)
-#define COMMON_BIG_ENDIAN 1
#endif
// GCC 4.6+
-#elif __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
@@ -49,7 +45,6 @@
#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
-
#endif
// Worst case, default to little endian.
@@ -57,6 +52,93 @@
#define COMMON_LITTLE_ENDIAN 1
#endif
+namespace Common {
+
+inline u8 swap8(u8 _data) {return _data;}
+inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];}
+
+#ifdef _MSC_VER
+inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
+inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
+inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
+#elif _M_ARM
+inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;}
+inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
+inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);}
+#elif __linux__
+inline u16 swap16(u16 _data) {return bswap_16(_data);}
+inline u32 swap32(u32 _data) {return bswap_32(_data);}
+inline u64 swap64(u64 _data) {return bswap_64(_data);}
+#elif __APPLE__
+inline __attribute__((always_inline)) u16 swap16(u16 _data)
+{return (_data >> 8) | (_data << 8);}
+inline __attribute__((always_inline)) u32 swap32(u32 _data)
+{return __builtin_bswap32(_data);}
+inline __attribute__((always_inline)) u64 swap64(u64 _data)
+{return __builtin_bswap64(_data);}
+#elif __FreeBSD__
+inline u16 swap16(u16 _data) {return bswap16(_data);}
+inline u32 swap32(u32 _data) {return bswap32(_data);}
+inline u64 swap64(u64 _data) {return bswap64(_data);}
+#else
+// Slow generic implementation.
+inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);}
+inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);}
+inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);}
+#endif
+
+inline float swapf(float f) {
+ union {
+ float f;
+ unsigned int u32;
+ } dat1, dat2;
+
+ dat1.f = f;
+ dat2.u32 = swap32(dat1.u32);
+
+ return dat2.f;
+}
+
+inline double swapd(double f) {
+ union {
+ double f;
+ unsigned long long u64;
+ } dat1, dat2;
+
+ dat1.f = f;
+ dat2.u64 = swap64(dat1.u64);
+
+ return dat2.f;
+}
+
+inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);}
+inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);}
+inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);}
+
+template <int count>
+void swap(u8*);
+
+template <>
+inline void swap<1>(u8* data) { }
+
+template <>
+inline void swap<2>(u8* data) {
+ *reinterpret_cast<u16*>(data) = swap16(data);
+}
+
+template <>
+inline void swap<4>(u8* data) {
+ *reinterpret_cast<u32*>(data) = swap32(data);
+}
+
+template <>
+inline void swap<8>(u8* data) {
+ *reinterpret_cast<u64*>(data) = swap64(data);
+}
+
+} // Namespace Common
+
+
template <typename T, typename F>
struct swap_struct_t {
typedef swap_struct_t<T, F> swapped_t;
@@ -448,35 +530,35 @@ bool operator==(const S &p, const swap_struct_t<T, F> v) {
template <typename T>
struct swap_64_t {
static T swap(T x) {
- return (T)bswap64(*(u64 *)&x);
+ return (T)Common::swap64(*(u64 *)&x);
}
};
template <typename T>
struct swap_32_t {
static T swap(T x) {
- return (T)bswap32(*(u32 *)&x);
+ return (T)Common::swap32(*(u32 *)&x);
}
};
template <typename T>
struct swap_16_t {
static T swap(T x) {
- return (T)bswap16(*(u16 *)&x);
+ return (T)Common::swap16(*(u16 *)&x);
}
};
template <typename T>
struct swap_float_t {
static T swap(T x) {
- return (T)bswapf(*(float *)&x);
+ return (T)Common::swapf(*(float *)&x);
}
};
template <typename T>
struct swap_double_t {
static T swap(T x) {
- return (T)bswapd(*(double *)&x);
+ return (T)Common::swapd(*(double *)&x);
}
};
@@ -527,4 +609,5 @@ typedef s64 s64_be;
typedef float float_be;
typedef double double_be;
+
#endif
diff --git a/src/common/synchronized_wrapper.h b/src/common/synchronized_wrapper.h
new file mode 100644
index 000000000..946252b8c
--- /dev/null
+++ b/src/common/synchronized_wrapper.h
@@ -0,0 +1,69 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <mutex>
+
+namespace Common {
+
+/**
+ * Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no
+ * one forgets to lock a mutex before acessing an object. To access the wrapped object construct a
+ * SyncronizedRef on this wrapper. Inspired by Rust's Mutex type (http://doc.rust-lang.org/std/sync/struct.Mutex.html).
+ */
+template <typename T>
+class SynchronizedWrapper {
+public:
+ template <typename... Args>
+ SynchronizedWrapper(Args&&... args) :
+ data(std::forward<Args>(args)...) {
+ }
+
+private:
+ template <typename U>
+ friend class SynchronizedRef;
+
+ std::mutex mutex;
+ T data;
+};
+
+/**
+ * Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This
+ * greatly reduces the chance that someone will access the wrapped resource without locking the
+ * mutex.
+ */
+template <typename T>
+class SynchronizedRef {
+public:
+ SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) {
+ wrapper.mutex.lock();
+ }
+
+ SynchronizedRef(SynchronizedRef&) = delete;
+ SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) {
+ o.wrapper = nullptr;
+ }
+
+ ~SynchronizedRef() {
+ if (wrapper)
+ wrapper->mutex.unlock();
+ }
+
+ SynchronizedRef& operator=(SynchronizedRef&) = delete;
+ SynchronizedRef& operator=(SynchronizedRef&& o) {
+ std::swap(wrapper, o.wrapper);
+ }
+
+ T& operator*() { return wrapper->data; }
+ const T& operator*() const { return wrapper->data; }
+
+ T* operator->() { return &wrapper->data; }
+ const T* operator->() const { return &wrapper->data; }
+
+private:
+ SynchronizedWrapper<T>* wrapper;
+};
+
+} // namespace Common
diff --git a/src/common/thread.h b/src/common/thread.h
index eaf1ba00c..a45728e1e 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -24,6 +24,25 @@
#include <unistd.h>
#endif
+// Support for C++11's thread_local keyword was surprisingly spotty in compilers until very
+// recently. Fortunately, thread local variables have been well supported for compilers for a while,
+// but with semantics supporting only POD types, so we can use a few defines to get some amount of
+// backwards compat support.
+// WARNING: This only works correctly with POD types.
+#if defined(__clang__)
+# if !__has_feature(cxx_thread_local)
+# define thread_local __thread
+# endif
+#elif defined(__GNUC__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
+# define thread_local __thread
+# endif
+#elif defined(_MSC_VER)
+# if _MSC_VER < 1900
+# define thread_local __declspec(thread)
+# endif
+#endif
+
namespace Common
{
diff --git a/src/common/utf8.cpp b/src/common/utf8.cpp
deleted file mode 100644
index 56609634c..000000000
--- a/src/common/utf8.cpp
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- Basic UTF-8 manipulation routines
- by Jeff Bezanson
- placed in the public domain Fall 2005
-
- This code is designed to provide the utilities you need to manipulate
- UTF-8 as an internal string encoding. These functions do not perform the
- error checking normally needed when handling UTF-8 data, so if you happen
- to be from the Unicode Consortium you will want to flay me alive.
- I do this because error checking can be performed at the boundaries (I/O),
- with these routines reserved for higher performance on data known to be
- valid.
-*/
-
-#ifdef _WIN32
-#include <windows.h>
-#undef min
-#undef max
-#endif
-
-#include <cstdlib>
-#include <cstring>
-#include <algorithm>
-
-#include "common/common_types.h"
-#include "common/utf8.h"
-
-// is start of UTF sequence
-inline bool isutf(char c) {
- return (c & 0xC0) != 0x80;
-}
-
-static const u32 offsetsFromUTF8[6] = {
- 0x00000000UL, 0x00003080UL, 0x000E2080UL,
- 0x03C82080UL, 0xFA082080UL, 0x82082080UL
-};
-
-static const u8 trailingBytesForUTF8[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5,
-};
-
-/* returns length of next utf-8 sequence */
-int u8_seqlen(const char *s)
-{
- return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1;
-}
-
-/* conversions without error checking
- only works for valid UTF-8, i.e. no 5- or 6-byte sequences
- srcsz = source size in bytes, or -1 if 0-terminated
- sz = dest size in # of wide characters
-
- returns # characters converted
- dest will always be L'\0'-terminated, even if there isn't enough room
- for all the characters.
- if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
-*/
-int u8_toucs(u32 *dest, int sz, const char *src, int srcsz)
-{
- u32 ch;
- const char *src_end = src + srcsz;
- int nb;
- int i=0;
-
- while (i < sz-1) {
- nb = trailingBytesForUTF8[(unsigned char)*src];
- if (srcsz == -1) {
- if (*src == 0)
- goto done_toucs;
- }
- else {
- if (src + nb >= src_end)
- goto done_toucs;
- }
- ch = 0;
- switch (nb) {
- /* these fall through deliberately */
- case 3: ch += (unsigned char)*src++; ch <<= 6;
- case 2: ch += (unsigned char)*src++; ch <<= 6;
- case 1: ch += (unsigned char)*src++; ch <<= 6;
- case 0: ch += (unsigned char)*src++;
- }
- ch -= offsetsFromUTF8[nb];
- dest[i++] = ch;
- }
- done_toucs:
- dest[i] = 0;
- return i;
-}
-
-/* srcsz = number of source characters, or -1 if 0-terminated
- sz = size of dest buffer in bytes
-
- returns # characters converted
- dest will only be '\0'-terminated if there is enough space. this is
- for consistency; imagine there are 2 bytes of space left, but the next
- character requires 3 bytes. in this case we could NUL-terminate, but in
- general we can't when there's insufficient space. therefore this function
- only NUL-terminates if all the characters fit, and there's space for
- the NUL as well.
- the destination string will never be bigger than the source string.
-*/
-int u8_toutf8(char *dest, int sz, u32 *src, int srcsz)
-{
- u32 ch;
- int i = 0;
- char *dest_end = dest + sz;
-
- while (srcsz<0 ? src[i]!=0 : i < srcsz) {
- ch = src[i];
- if (ch < 0x80) {
- if (dest >= dest_end)
- return i;
- *dest++ = (char)ch;
- }
- else if (ch < 0x800) {
- if (dest >= dest_end-1)
- return i;
- *dest++ = (ch>>6) | 0xC0;
- *dest++ = (ch & 0x3F) | 0x80;
- }
- else if (ch < 0x10000) {
- if (dest >= dest_end-2)
- return i;
- *dest++ = (ch>>12) | 0xE0;
- *dest++ = ((ch>>6) & 0x3F) | 0x80;
- *dest++ = (ch & 0x3F) | 0x80;
- }
- else if (ch < 0x110000) {
- if (dest >= dest_end-3)
- return i;
- *dest++ = (ch>>18) | 0xF0;
- *dest++ = ((ch>>12) & 0x3F) | 0x80;
- *dest++ = ((ch>>6) & 0x3F) | 0x80;
- *dest++ = (ch & 0x3F) | 0x80;
- }
- i++;
- }
- if (dest < dest_end)
- *dest = '\0';
- return i;
-}
-
-int u8_wc_toutf8(char *dest, u32 ch)
-{
- if (ch < 0x80) {
- dest[0] = (char)ch;
- return 1;
- }
- if (ch < 0x800) {
- dest[0] = (ch>>6) | 0xC0;
- dest[1] = (ch & 0x3F) | 0x80;
- return 2;
- }
- if (ch < 0x10000) {
- dest[0] = (ch>>12) | 0xE0;
- dest[1] = ((ch>>6) & 0x3F) | 0x80;
- dest[2] = (ch & 0x3F) | 0x80;
- return 3;
- }
- if (ch < 0x110000) {
- dest[0] = (ch>>18) | 0xF0;
- dest[1] = ((ch>>12) & 0x3F) | 0x80;
- dest[2] = ((ch>>6) & 0x3F) | 0x80;
- dest[3] = (ch & 0x3F) | 0x80;
- return 4;
- }
- return 0;
-}
-
-/* charnum => byte offset */
-int u8_offset(const char *str, int charnum)
-{
- int offs=0;
-
- while (charnum > 0 && str[offs]) {
- (void)(isutf(str[++offs]) || isutf(str[++offs]) ||
- isutf(str[++offs]) || ++offs);
- charnum--;
- }
- return offs;
-}
-
-/* byte offset => charnum */
-int u8_charnum(const char *s, int offset)
-{
- int charnum = 0, offs=0;
-
- while (offs < offset && s[offs]) {
- (void)(isutf(s[++offs]) || isutf(s[++offs]) ||
- isutf(s[++offs]) || ++offs);
- charnum++;
- }
- return charnum;
-}
-
-/* number of characters */
-int u8_strlen(const char *s)
-{
- int count = 0;
- int i = 0;
-
- while (u8_nextchar(s, &i) != 0)
- count++;
-
- return count;
-}
-
-/* reads the next utf-8 sequence out of a string, updating an index */
-u32 u8_nextchar(const char *s, int *i)
-{
- u32 ch = 0;
- int sz = 0;
-
- do {
- ch <<= 6;
- ch += (unsigned char)s[(*i)++];
- sz++;
- } while (s[*i] && !isutf(s[*i]));
- ch -= offsetsFromUTF8[sz-1];
-
- return ch;
-}
-
-void u8_inc(const char *s, int *i)
-{
- (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) ||
- isutf(s[++(*i)]) || ++(*i));
-}
-
-void u8_dec(const char *s, int *i)
-{
- (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) ||
- isutf(s[--(*i)]) || --(*i));
-}
-
-int octal_digit(char c)
-{
- return (c >= '0' && c <= '7');
-}
-
-int hex_digit(char c)
-{
- return ((c >= '0' && c <= '9') ||
- (c >= 'A' && c <= 'F') ||
- (c >= 'a' && c <= 'f'));
-}
-
-/* assumes that src points to the character after a backslash
- returns number of input characters processed */
-int u8_read_escape_sequence(const char *str, u32 *dest)
-{
- u32 ch;
- char digs[9]="\0\0\0\0\0\0\0\0";
- int dno=0, i=1;
-
- ch = (u32)str[0]; /* take literal character */
- if (str[0] == 'n')
- ch = L'\n';
- else if (str[0] == 't')
- ch = L'\t';
- else if (str[0] == 'r')
- ch = L'\r';
- else if (str[0] == 'b')
- ch = L'\b';
- else if (str[0] == 'f')
- ch = L'\f';
- else if (str[0] == 'v')
- ch = L'\v';
- else if (str[0] == 'a')
- ch = L'\a';
- else if (octal_digit(str[0])) {
- i = 0;
- do {
- digs[dno++] = str[i++];
- } while (octal_digit(str[i]) && dno < 3);
- ch = strtol(digs, nullptr, 8);
- }
- else if (str[0] == 'x') {
- while (hex_digit(str[i]) && dno < 2) {
- digs[dno++] = str[i++];
- }
- if (dno > 0)
- ch = strtol(digs, nullptr, 16);
- }
- else if (str[0] == 'u') {
- while (hex_digit(str[i]) && dno < 4) {
- digs[dno++] = str[i++];
- }
- if (dno > 0)
- ch = strtol(digs, nullptr, 16);
- }
- else if (str[0] == 'U') {
- while (hex_digit(str[i]) && dno < 8) {
- digs[dno++] = str[i++];
- }
- if (dno > 0)
- ch = strtol(digs, nullptr, 16);
- }
- *dest = ch;
-
- return i;
-}
-
-/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8
- example: u8_unescape(mybuf, 256, "hello\\u220e")
- note the double backslash is needed if called on a C string literal */
-int u8_unescape(char *buf, int sz, char *src)
-{
- int c=0, amt;
- u32 ch;
- char temp[4];
-
- while (*src && c < sz) {
- if (*src == '\\') {
- src++;
- amt = u8_read_escape_sequence(src, &ch);
- }
- else {
- ch = (u32)*src;
- amt = 1;
- }
- src += amt;
- amt = u8_wc_toutf8(temp, ch);
- if (amt > sz-c)
- break;
- memcpy(&buf[c], temp, amt);
- c += amt;
- }
- if (c < sz)
- buf[c] = '\0';
- return c;
-}
-
-const char *u8_strchr(const char *s, u32 ch, int *charn)
-{
- int i = 0, lasti=0;
- u32 c;
-
- *charn = 0;
- while (s[i]) {
- c = u8_nextchar(s, &i);
- if (c == ch) {
- return &s[lasti];
- }
- lasti = i;
- (*charn)++;
- }
- return nullptr;
-}
-
-const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn)
-{
- u32 i = 0, lasti=0;
- u32 c;
- int csz;
-
- *charn = 0;
- while (i < sz) {
- c = csz = 0;
- do {
- c <<= 6;
- c += (unsigned char)s[i++];
- csz++;
- } while (i < sz && !isutf(s[i]));
- c -= offsetsFromUTF8[csz-1];
-
- if (c == ch) {
- return &s[lasti];
- }
- lasti = i;
- (*charn)++;
- }
- return nullptr;
-}
-
-int u8_is_locale_utf8(const char *locale)
-{
- /* this code based on libutf8 */
- const char* cp = locale;
-
- for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) {
- if (*cp == '.') {
- const char* encoding = ++cp;
- for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++)
- ;
- if ((cp-encoding == 5 && !strncmp(encoding, "UTF-8", 5))
- || (cp-encoding == 4 && !strncmp(encoding, "utf8", 4)))
- return 1; /* it's UTF-8 */
- break;
- }
- }
- return 0;
-}
-
-int UTF8StringNonASCIICount(const char *utf8string) {
- UTF8 utf(utf8string);
- int count = 0;
- while (!utf.end()) {
- int c = utf.next();
- if (c > 127)
- ++count;
- }
- return count;
-}
-
-bool UTF8StringHasNonASCII(const char *utf8string) {
- return UTF8StringNonASCIICount(utf8string) > 0;
-}
-
-#ifdef _WIN32
-
-std::string ConvertWStringToUTF8(const wchar_t *wstr) {
- int len = (int)wcslen(wstr);
- int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, nullptr, nullptr);
- std::string s;
- s.resize(size);
- if (size > 0) {
- WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, nullptr, nullptr);
- }
- return s;
-}
-
-std::string ConvertWStringToUTF8(const std::wstring &wstr) {
- int len = (int)wstr.size();
- int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, nullptr, nullptr);
- std::string s;
- s.resize(size);
- if (size > 0) {
- WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, nullptr, nullptr);
- }
- return s;
-}
-
-void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) {
- int len = (int)source.size();
- int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0);
- MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size));
-}
-
-std::wstring ConvertUTF8ToWString(const std::string &source) {
- int len = (int)source.size();
- int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0);
- std::wstring str;
- str.resize(size);
- if (size > 0) {
- MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, &str[0], size);
- }
- return str;
-}
-
-#endif
diff --git a/src/common/utf8.h b/src/common/utf8.h
deleted file mode 100644
index a6e84913b..000000000
--- a/src/common/utf8.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- Basic UTF-8 manipulation routines
- by Jeff Bezanson
- placed in the public domain Fall 2005
-
- This code is designed to provide the utilities you need to manipulate
- UTF-8 as an internal string encoding. These functions do not perform the
- error checking normally needed when handling UTF-8 data, so if you happen
- to be from the Unicode Consortium you will want to flay me alive.
- I do this because error checking can be performed at the boundaries (I/O),
- with these routines reserved for higher performance on data known to be
- valid.
-*/
-
-// Further modified, and C++ stuff added, by hrydgard@gmail.com.
-
-#pragma once
-
-#include "common/common_types.h"
-#include <string>
-
-u32 u8_nextchar(const char *s, int *i);
-int u8_wc_toutf8(char *dest, u32 ch);
-int u8_strlen(const char *s);
-
-class UTF8 {
-public:
- static const u32 INVALID = (u32)-1;
- UTF8(const char *c) : c_(c), index_(0) {}
- bool end() const { return c_[index_] == 0; }
- u32 next() {
- return u8_nextchar(c_, &index_);
- }
- u32 peek() {
- int tempIndex = index_;
- return u8_nextchar(c_, &tempIndex);
- }
- int length() const {
- return u8_strlen(c_);
- }
- int byteIndex() const {
- return index_;
- }
- static int encode(char *dest, u32 ch) {
- return u8_wc_toutf8(dest, ch);
- }
-
-private:
- const char *c_;
- int index_;
-};
-
-int UTF8StringNonASCIICount(const char *utf8string);
-
-bool UTF8StringHasNonASCII(const char *utf8string);
-
-
-// UTF8 to Win32 UTF-16
-// Should be used when calling Win32 api calls
-#ifdef _WIN32
-
-std::string ConvertWStringToUTF8(const std::wstring &wstr);
-std::string ConvertWStringToUTF8(const wchar_t *wstr);
-void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source);
-std::wstring ConvertUTF8ToWString(const std::string &source);
-
-#endif
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0ab0e440c..212da25c5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -35,9 +35,10 @@ set(SRCS
hle/service/am_app.cpp
hle/service/am_net.cpp
hle/service/am_sys.cpp
- hle/service/apt_a.cpp
- hle/service/apt_s.cpp
- hle/service/apt_u.cpp
+ hle/service/apt/apt.cpp
+ hle/service/apt/apt_a.cpp
+ hle/service/apt/apt_s.cpp
+ hle/service/apt/apt_u.cpp
hle/service/boss_p.cpp
hle/service/boss_u.cpp
hle/service/cam_u.cpp
@@ -71,9 +72,10 @@ set(SRCS
hle/service/ns_s.cpp
hle/service/nwm_uds.cpp
hle/service/pm_app.cpp
- hle/service/ptm_play.cpp
- hle/service/ptm_u.cpp
- hle/service/ptm_sysm.cpp
+ hle/service/ptm/ptm.cpp
+ hle/service/ptm/ptm_play.cpp
+ hle/service/ptm/ptm_u.cpp
+ hle/service/ptm/ptm_sysm.cpp
hle/service/service.cpp
hle/service/soc_u.cpp
hle/service/srv.cpp
@@ -140,9 +142,10 @@ set(HEADERS
hle/service/am_app.h
hle/service/am_net.h
hle/service/am_sys.h
- hle/service/apt_a.h
- hle/service/apt_s.h
- hle/service/apt_u.h
+ hle/service/apt/apt.h
+ hle/service/apt/apt_a.h
+ hle/service/apt/apt_s.h
+ hle/service/apt/apt_u.h
hle/service/boss_p.h
hle/service/boss_u.h
hle/service/cam_u.h
@@ -176,9 +179,10 @@ set(HEADERS
hle/service/ns_s.h
hle/service/nwm_uds.h
hle/service/pm_app.h
- hle/service/ptm_play.h
- hle/service/ptm_u.h
- hle/service/ptm_sysm.h
+ hle/service/ptm/ptm.h
+ hle/service/ptm/ptm_play.h
+ hle/service/ptm/ptm_u.h
+ hle/service/ptm/ptm_sysm.h
hle/service/service.h
hle/service/soc_u.h
hle/service/srv.h
diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp
index f7c7451e9..913dc1454 100644
--- a/src/core/arm/disassembler/arm_disasm.cpp
+++ b/src/core/arm/disassembler/arm_disasm.cpp
@@ -24,7 +24,7 @@ static const char *cond_names[] = {
"RESERVED"
};
-const char *opcode_names[] = {
+static const char *opcode_names[] = {
"invalid",
"undefined",
"adc",
@@ -131,7 +131,7 @@ static const char *shift_names[] = {
"ROR"
};
-static const char* cond_to_str(int cond) {
+static const char* cond_to_str(uint32_t cond) {
return cond_names[cond];
}
@@ -337,8 +337,9 @@ std::string ARM_Disasm::DisassembleBX(uint32_t insn)
std::string ARM_Disasm::DisassembleBKPT(uint32_t insn)
{
+ uint8_t cond = (insn >> 28) & 0xf;
uint32_t immed = (((insn >> 8) & 0xfff) << 4) | (insn & 0xf);
- return Common::StringFromFormat("bkpt\t#%d", immed);
+ return Common::StringFromFormat("bkpt%s\t#%d", cond_to_str(cond), immed);
}
std::string ARM_Disasm::DisassembleCLZ(uint32_t insn)
@@ -351,7 +352,6 @@ std::string ARM_Disasm::DisassembleCLZ(uint32_t insn)
std::string ARM_Disasm::DisassembleMemblock(Opcode opcode, uint32_t insn)
{
- std::string tmp_reg;
std::string tmp_list;
uint8_t cond = (insn >> 28) & 0xf;
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index b691ffbc3..2f72f5077 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -9,6 +9,7 @@
#include <unordered_map>
#include "common/logging/log.h"
+#include "common/profiler.h"
#include "core/mem_map.h"
#include "core/hle/hle.h"
@@ -20,6 +21,9 @@
#include "core/arm/skyeye_common/armmmu.h"
#include "core/arm/skyeye_common/vfp/vfp.h"
+Common::Profiling::TimingCategory profile_execute("DynCom::Execute");
+Common::Profiling::TimingCategory profile_decode("DynCom::Decode");
+
enum {
COND = (1 << 0),
NON_BRANCH = (1 << 1),
@@ -3569,6 +3573,8 @@ typedef struct instruction_set_encoding_item ISEITEM;
extern const ISEITEM arm_instruction[];
static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, addr_t addr) {
+ Common::Profiling::ScopeTimer timer_decode(profile_decode);
+
// Decode instruction, get index
// Allocate memory and init InsCream
// Go on next, until terminal instruction
@@ -3641,6 +3647,8 @@ static bool InAPrivilegedMode(ARMul_State* core) {
}
unsigned InterpreterMainLoop(ARMul_State* state) {
+ Common::Profiling::ScopeTimer timer_execute(profile_execute);
+
#undef RM
#undef RS
@@ -4354,6 +4362,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
cpu->Reg[14] = Memory::Read32(addr);
else
cpu->Reg_usr[1] = Memory::Read32(addr);
+
+ addr += 4;
}
} else if (!BIT(inst, 22)) {
for(int i = 0; i < 16; i++ ){
@@ -4422,12 +4432,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
unsigned int value = Memory::Read32(addr);
- if (BIT(CP15_REG(CP15_CONTROL), 22) == 1)
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- else {
- value = ROTATE_RIGHT_32(value,(8*(addr&0x3)));
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- }
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
// For armv5t, should enter thumb when bits[0] is non-zero.
@@ -4450,12 +4455,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
inst_cream->get_addr(cpu, inst_cream->inst, addr, 1);
unsigned int value = Memory::Read32(addr);
- if (BIT(CP15_REG(CP15_CONTROL), 22) == 1)
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- else {
- value = ROTATE_RIGHT_32(value,(8*(addr&0x3)));
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- }
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
// For armv5t, should enter thumb when bits[0] is non-zero.
@@ -4699,11 +4699,6 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
unsigned int value = Memory::Read32(addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- if (BIT(CP15_REG(CP15_CONTROL), 22) == 1)
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- else
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = ROTATE_RIGHT_32(value,(8*(addr&0x3))) ;
-
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
goto DISPATCH;
@@ -4724,9 +4719,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
DEBUG_MSG;
} else {
if (inst_cream->cp_num == 15) {
- if(CRn == 0 && OPCODE_2 == 0 && CRm == 0) {
- CP15_REG(CP15_MAIN_ID) = RD;
- } else if(CRn == 1 && CRm == 0 && OPCODE_2 == 0) {
+ if (CRn == 1 && CRm == 0 && OPCODE_2 == 0) {
CP15_REG(CP15_CONTROL) = RD;
} else if (CRn == 1 && CRm == 0 && OPCODE_2 == 1) {
CP15_REG(CP15_AUXILIARY_CONTROL) = RD;
@@ -5983,54 +5976,51 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
unsigned int inst = inst_cream->inst;
- int i;
unsigned int Rn = BITS(inst, 16, 19);
unsigned int old_RN = cpu->Reg[Rn];
inst_cream->get_addr(cpu, inst_cream->inst, addr, 0);
if (BIT(inst_cream->inst, 22) == 1) {
- for (i = 0; i < 13; i++) {
- if(BIT(inst_cream->inst, i)) {
+ for (int i = 0; i < 13; i++) {
+ if (BIT(inst_cream->inst, i)) {
Memory::Write32(addr, cpu->Reg[i]);
addr += 4;
}
}
if (BIT(inst_cream->inst, 13)) {
- if (cpu->Mode == USER32MODE) {
- Memory::Write32(addr, cpu->Reg[i]);
- addr += 4;
- } else {
+ if (cpu->Mode == USER32MODE)
+ Memory::Write32(addr, cpu->Reg[13]);
+ else
Memory::Write32(addr, cpu->Reg_usr[0]);
- addr += 4;
- }
+
+ addr += 4;
}
if (BIT(inst_cream->inst, 14)) {
- if (cpu->Mode == USER32MODE) {
- Memory::Write32(addr, cpu->Reg[i]);
- addr += 4;
- } else {
+ if (cpu->Mode == USER32MODE)
+ Memory::Write32(addr, cpu->Reg[14]);
+ else
Memory::Write32(addr, cpu->Reg_usr[1]);
- addr += 4;
- }
+
+ addr += 4;
}
if (BIT(inst_cream->inst, 15)) {
Memory::Write32(addr, cpu->Reg_usr[1] + 8);
}
} else {
- for( i = 0; i < 15; i++ ) {
- if(BIT(inst_cream->inst, i)) {
- if(i == Rn)
+ for (int i = 0; i < 15; i++) {
+ if (BIT(inst_cream->inst, i)) {
+ if (i == Rn)
Memory::Write32(addr, old_RN);
else
Memory::Write32(addr, cpu->Reg[i]);
+
addr += 4;
}
}
// Check PC reg
- if(BIT(inst_cream->inst, i)) {
+ if (BIT(inst_cream->inst, 15))
Memory::Write32(addr, cpu->Reg_usr[1] + 8);
- }
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp
index abafe226e..4ac827e0a 100644
--- a/src/core/arm/interpreter/arminit.cpp
+++ b/src/core/arm/interpreter/arminit.cpp
@@ -58,11 +58,7 @@ void ARMul_SelectProcessor(ARMul_State* state, unsigned properties)
state->is_v4 = (properties & (ARM_v4_Prop | ARM_v5_Prop)) != 0;
state->is_v5 = (properties & ARM_v5_Prop) != 0;
state->is_v5e = (properties & ARM_v5e_Prop) != 0;
- state->is_XScale = (properties & ARM_XScale_Prop) != 0;
- state->is_iWMMXt = (properties & ARM_iWMMXt_Prop) != 0;
state->is_v6 = (properties & ARM_v6_Prop) != 0;
- state->is_ep9312 = (properties & ARM_ep9312_Prop) != 0;
- state->is_pxa27x = (properties & ARM_PXA27X_Prop) != 0;
state->is_v7 = (properties & ARM_v7_Prop) != 0;
// Only initialse the coprocessor support once we
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h
index 070fcf7dc..16f3ac86c 100644
--- a/src/core/arm/skyeye_common/armdefs.h
+++ b/src/core/arm/skyeye_common/armdefs.h
@@ -185,10 +185,6 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
bool is_v5e; // Are we emulating a v5e architecture?
bool is_v6; // Are we emulating a v6 architecture?
bool is_v7; // Are we emulating a v7 architecture?
- bool is_XScale; // Are we emulating an XScale architecture?
- bool is_iWMMXt; // Are we emulating an iWMMXt co-processor?
- bool is_ep9312; // Are we emulating a Cirrus Maverick co-processor?
- bool is_pxa27x; // Are we emulating a Intel PXA27x co-processor?
// ARM_ARM A2-18
// 0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model
@@ -211,20 +207,11 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
\***************************************************************************/
enum {
- ARM_Nexec_Prop = 0x02,
- ARM_Debug_Prop = 0x10,
- ARM_Isync_Prop = ARM_Debug_Prop,
- ARM_Lock_Prop = 0x20,
- ARM_v4_Prop = 0x40,
- ARM_v5_Prop = 0x80,
- ARM_v6_Prop = 0xc0,
-
- ARM_v5e_Prop = 0x100,
- ARM_XScale_Prop = 0x200,
- ARM_ep9312_Prop = 0x400,
- ARM_iWMMXt_Prop = 0x800,
- ARM_PXA27X_Prop = 0x1000,
- ARM_v7_Prop = 0x2000,
+ ARM_v4_Prop = 0x01,
+ ARM_v5_Prop = 0x02,
+ ARM_v5e_Prop = 0x04,
+ ARM_v6_Prop = 0x08,
+ ARM_v7_Prop = 0x10,
};
/***************************************************************************\
diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h
index 75d860e95..5d1b4e53f 100644
--- a/src/core/arm/skyeye_common/vfp/vfp_helper.h
+++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h
@@ -144,8 +144,8 @@ static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
u32 nh, nl, mh, ml;
u64 rh, rma, rmb, rl;
- nl = n;
- ml = m;
+ nl = static_cast<u32>(n);
+ ml = static_cast<u32>(m);
rl = (u64)nl * ml;
nh = n >> 32;
diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
index 1a05ef8c1..d76d37fd4 100644
--- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
@@ -661,8 +661,8 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
if ((rem + incr) < rem && d < 0xffffffff)
d += 1;
- if (d > (0x7fffffff + (vdm.sign != 0))) {
- d = (0x7fffffff + (vdm.sign != 0));
+ if (d > (0x7fffffffU + (vdm.sign != 0))) {
+ d = (0x7fffffffU + (vdm.sign != 0));
exceptions |= FPSCR_IOC;
} else if (rem)
exceptions |= FPSCR_IXC;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index cabe2a074..6f716b1ca 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -147,7 +147,7 @@ void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callba
void UnregisterAllEvents() {
if (first)
- PanicAlert("Cannot unregister events with events pending");
+ LOG_ERROR(Core_Timing, "Cannot unregister events with events pending");
event_types.clear();
}
@@ -535,7 +535,7 @@ std::string GetScheduledEventsSummary() {
while (event) {
unsigned int t = event->type;
if (t >= event_types.size())
- PanicAlert("Invalid event type"); // %i", t);
+ LOG_ERROR(Core_Timing, "Invalid event type"); // %i", t);
const char* name = event_types[event->type].name;
if (!name)
name = "[unknown]";
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index c2a5d641a..25c94cd26 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -6,9 +6,9 @@
#include "common/common_types.h"
#include "common/file_util.h"
+#include "common/make_unique.h"
#include "core/file_sys/archive_systemsavedata.h"
-#include "core/file_sys/disk_archive.h"
#include "core/hle/service/fs/archive.h"
#include "core/settings.h"
@@ -17,9 +17,11 @@
namespace FileSys {
-static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 save_id) {
- u32 save_high = static_cast<u32>((save_id >> 32) & 0xFFFFFFFF);
- u32 save_low = static_cast<u32>(save_id & 0xFFFFFFFF);
+static std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) {
+ std::vector<u8> vec_data = path.AsBinary();
+ const u32* data = reinterpret_cast<const u32*>(vec_data.data());
+ u32 save_low = data[1];
+ u32 save_high = data[0];
return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high);
}
@@ -27,18 +29,25 @@ static std::string GetSystemSaveDataContainerPath(const std::string& mount_point
return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID.c_str());
}
-Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id)
- : DiskArchive(GetSystemSaveDataPath(GetSystemSaveDataContainerPath(mount_point), save_id)) {
- LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str());
+ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path)
+ : base_path(GetSystemSaveDataContainerPath(nand_path)) {
}
-bool Archive_SystemSaveData::Initialize() {
- if (!FileUtil::CreateFullPath(mount_point)) {
- LOG_ERROR(Service_FS, "Unable to create SystemSaveData path.");
- return false;
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path) {
+ std::string fullpath = GetSystemSaveDataPath(base_path, path);
+ if (!FileUtil::Exists(fullpath)) {
+ // TODO(Subv): Check error code, this one is probably wrong
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
}
+ auto archive = Common::make_unique<DiskArchive>(fullpath);
+ return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
+}
- return true;
+ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path) {
+ std::string fullpath = GetSystemSaveDataPath(base_path, path);
+ FileUtil::CreateFullPath(fullpath);
+ return RESULT_SUCCESS;
}
} // namespace FileSys
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h
index c8f5845ca..556a2a488 100644
--- a/src/core/file_sys/archive_systemsavedata.h
+++ b/src/core/file_sys/archive_systemsavedata.h
@@ -15,17 +15,17 @@
namespace FileSys {
/// File system interface to the SystemSaveData archive
-class Archive_SystemSaveData final : public DiskArchive {
+class ArchiveFactory_SystemSaveData final : public ArchiveFactory {
public:
- Archive_SystemSaveData(const std::string& mount_point, u64 save_id);
+ ArchiveFactory_SystemSaveData(const std::string& mount_point);
- /**
- * Initialize the archive.
- * @return true if it initialized successfully
- */
- bool Initialize();
+ ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path) override;
std::string GetName() const override { return "SystemSaveData"; }
+
+private:
+ std::string base_path;
};
} // namespace FileSys
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index b0066e15e..1aaeaa9c9 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -4,6 +4,8 @@
#include <vector>
+#include "common/profiler.h"
+
#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/hle.h"
@@ -11,14 +13,13 @@
#include "core/hle/shared_page.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
-#include "core/hle/service/fs/archive.h"
-#include "core/hle/service/cfg/cfg.h"
-#include "core/hle/service/hid/hid.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace HLE {
+Common::Profiling::TimingCategory profiler_svc("SVC Calls");
+
static std::vector<ModuleDef> g_module_db;
bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a new thread
@@ -33,6 +34,8 @@ static const FunctionDef* GetSVCInfo(u32 opcode) {
}
void CallSVC(u32 opcode) {
+ Common::Profiling::ScopeTimer timer_svc(profiler_svc);
+
const FunctionDef *info = GetSVCInfo(opcode);
if (!info) {
@@ -70,9 +73,6 @@ static void RegisterAllModules() {
void Init() {
Service::Init();
- Service::FS::ArchiveInit();
- Service::CFG::CFGInit();
- Service::HID::HIDInit();
RegisterAllModules();
@@ -83,9 +83,6 @@ void Init() {
}
void Shutdown() {
- Service::HID::HIDShutdown();
- Service::CFG::CFGShutdown();
- Service::FS::ArchiveShutdown();
Service::Shutdown();
g_module_db.clear();
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f8c834a8d..be1aed615 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -7,6 +7,7 @@
#include <vector>
#include "common/common.h"
+#include "common/math_util.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
@@ -339,7 +340,7 @@ static void DebugThreadQueue() {
ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
u32 arg, s32 processor_id, VAddr stack_top) {
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
- s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ s32 new_priority = MathUtil::Clamp<s32>(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
name.c_str(), priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
@@ -387,7 +388,7 @@ static void ClampPriority(const Thread* thread, s32* priority) {
if (*priority < THREADPRIO_HIGHEST || *priority > THREADPRIO_LOWEST) {
DEBUG_ASSERT_MSG(false, "Application passed an out of range priority. An error should be returned.");
- s32 new_priority = CLAMP(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ s32 new_priority = MathUtil::Clamp<s32>(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
thread->name.c_str(), *priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 0e391fe2d..3648a168b 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -208,11 +208,11 @@ union ResultCode {
}
};
-inline bool operator==(const ResultCode a, const ResultCode b) {
+inline bool operator==(const ResultCode& a, const ResultCode& b) {
return a.raw == b.raw;
}
-inline bool operator!=(const ResultCode a, const ResultCode b) {
+inline bool operator!=(const ResultCode& a, const ResultCode& b) {
return a.raw != b.raw;
}
diff --git a/src/core/hle/service/am_sys.cpp b/src/core/hle/service/am_sys.cpp
index 7ab89569f..b244190a2 100644
--- a/src/core/hle/service/am_sys.cpp
+++ b/src/core/hle/service/am_sys.cpp
@@ -5,19 +5,56 @@
#include "core/hle/hle.h"
#include "core/hle/service/am_sys.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace AM_SYS
-
namespace AM_SYS {
-// Empty arrays are illegal -- commented out until an entry is added.
-//const Interface::FunctionInfo FunctionTable[] = { };
+/**
+ * Gets the number of installed titles in the requested media type
+ * Inputs:
+ * 0: Command header (0x00010040)
+ * 1: Media type to load the titles from
+ * Outputs:
+ * 1: Result, 0 on success, otherwise error code
+ * 2: The number of titles in the requested media type
+ */
+static void TitleIDListGetTotal(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 media_type = cmd_buff[1] & 0xFF;
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+ LOG_WARNING(Service_CFG, "(STUBBED) media_type %u", media_type);
+}
+
+/**
+ * Loads information about the desired number of titles from the desired media type into an array
+ * Inputs:
+ * 0: Command header (0x00020082)
+ * 1: The maximum number of titles to load
+ * 2: Media type to load the titles from
+ * 3: Descriptor of the output buffer pointer
+ * 4: Address of the output buffer
+ * Outputs:
+ * 1: Result, 0 on success, otherwise error code
+ * 2: The number of titles loaded from the requested media type
+ */
+static void GetTitleIDList(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 num_titles = cmd_buff[1];
+ u32 media_type = cmd_buff[2] & 0xFF;
+ u32 addr = cmd_buff[4];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+ LOG_WARNING(Service_CFG, "(STUBBED) Requested %u titles from media type %u", num_titles, media_type);
+}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
+ {0x00020082, GetTitleIDList, "GetTitleIDList"},
+};
Interface::Interface() {
- //Register(FunctionTable);
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
new file mode 100644
index 000000000..5971f860b
--- /dev/null
+++ b/src/core/hle/service/apt/apt.cpp
@@ -0,0 +1,285 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/file_util.h"
+
+#include "core/hle/service/service.h"
+#include "core/hle/service/apt/apt.h"
+#include "core/hle/service/apt/apt_a.h"
+#include "core/hle/service/apt/apt_s.h"
+#include "core/hle/service/apt/apt_u.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Service {
+namespace APT {
+
+// Address used for shared font (as observed on HW)
+// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
+// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
+// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
+// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
+// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
+static const VAddr SHARED_FONT_VADDR = 0x18000000;
+
+/// Handle to shared memory region designated to for shared system font
+static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
+
+static Kernel::SharedPtr<Kernel::Mutex> lock;
+static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
+static Kernel::SharedPtr<Kernel::Event> pause_event = 0; ///< APT pause event
+static std::vector<u8> shared_font;
+
+void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom();
+
+ // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called.
+ notification_event->Clear();
+ pause_event->Signal(); // Fire start event
+
+ ASSERT_MSG((nullptr != lock), "Cannot initialize without lock");
+ lock->Release();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+}
+
+void GetSharedFont(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ if (!shared_font.empty()) {
+ // TODO(bunnei): This function shouldn't copy the shared font every time it's called.
+ // Instead, it should probably map the shared font as RO memory. We don't currently have
+ // an easy way to do this, but the copy should be sufficient for now.
+ memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
+
+ cmd_buff[0] = 0x00440082;
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = SHARED_FONT_VADDR;
+ cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
+ } else {
+ cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
+ LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
+ }
+}
+
+void NotifyToWait(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further.
+ pause_event->Signal();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
+}
+
+void GetLockHandle(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ // Not sure what these parameters are used for, but retail apps check that they are 0 after
+ // GetLockHandle has been called.
+ cmd_buff[2] = 0;
+ cmd_buff[3] = 0;
+ cmd_buff[4] = 0;
+
+ cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom();
+ LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
+}
+
+void Enable(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
+}
+
+void GetAppletManInfo(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 unk = cmd_buff[1];
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 0;
+ cmd_buff[3] = 0;
+ cmd_buff[4] = static_cast<u32>(AppID::HomeMenu); // Home menu AppID
+ cmd_buff[5] = static_cast<u32>(AppID::Application); // TODO(purpasmart96): Do this correctly
+
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
+}
+
+void IsRegistered(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 1; // Set to registered
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
+}
+
+void InquireNotification(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
+}
+
+void SendParameter(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 src_app_id = cmd_buff[1];
+ u32 dst_app_id = cmd_buff[2];
+ u32 signal_type = cmd_buff[3];
+ u32 buffer_size = cmd_buff[4];
+ u32 value = cmd_buff[5];
+ u32 handle = cmd_buff[6];
+ u32 size = cmd_buff[7];
+ u32 in_param_buffer_ptr = cmd_buff[8];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
+ "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X",
+ src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr);
+}
+
+void ReceiveParameter(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ u32 buffer_size = cmd_buff[2];
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 0;
+ cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
+ cmd_buff[5] = 0;
+ cmd_buff[6] = 0;
+ cmd_buff[7] = 0;
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+}
+
+void GlanceParameter(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ u32 buffer_size = cmd_buff[2];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 0;
+ cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
+ cmd_buff[5] = 0;
+ cmd_buff[6] = 0;
+ cmd_buff[7] = 0;
+
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+}
+
+void CancelParameter(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 flag1 = cmd_buff[1];
+ u32 unk = cmd_buff[2];
+ u32 flag2 = cmd_buff[3];
+ u32 app_id = cmd_buff[4];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 1; // Set to Success
+
+ LOG_WARNING(Service_APT, "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X",
+ flag1, unk, flag2, app_id);
+}
+
+void AppletUtility(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // These are from 3dbrew - I'm not really sure what they're used for.
+ u32 unk = cmd_buff[1];
+ u32 buffer1_size = cmd_buff[2];
+ u32 buffer2_size = cmd_buff[3];
+ u32 buffer1_addr = cmd_buff[5];
+ u32 buffer2_addr = cmd_buff[65];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
+ "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
+ buffer1_addr, buffer2_addr);
+}
+
+void SetAppCpuTimeLimit(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 value = cmd_buff[1];
+ u32 percent = cmd_buff[2];
+
+ if (value != 1) {
+ LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
+ }
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+
+ LOG_WARNING(Service_APT, "(STUBBED) called percent=0x%08X, value=0x%08x", percent, value);
+}
+
+void GetAppCpuTimeLimit(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 value = cmd_buff[1];
+
+ if (value != 1) {
+ LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
+ }
+
+ // TODO(purpasmart96): This is incorrect, I'm pretty sure the percentage should
+ // be set by the application.
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = 0x80; // Set to 80%
+
+ LOG_WARNING(Service_APT, "(STUBBED) called value=0x%08x", value);
+}
+
+void APTInit() {
+ AddService(new APT_A_Interface);
+ AddService(new APT_S_Interface);
+ AddService(new APT_U_Interface);
+
+ // Load the shared system font (if available).
+ // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
+ // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
+ // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
+ // "shared_font.bin" in the Citra "sysdata" directory.
+
+ shared_font.clear();
+ std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
+
+ FileUtil::CreateFullPath(filepath); // Create path if not already created
+ FileUtil::IOFile file(filepath, "rb");
+
+ if (file.IsOpen()) {
+ // Read shared font data
+ shared_font.resize((size_t)file.GetSize());
+ file.ReadBytes(shared_font.data(), (size_t)file.GetSize());
+
+ // Create shared font memory object
+ shared_font_mem = Kernel::SharedMemory::Create("APT_U:shared_font_mem");
+ } else {
+ LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
+ shared_font_mem = nullptr;
+ }
+
+ lock = Kernel::Mutex::Create(false, "APT_U:Lock");
+
+ // TODO(bunnei): Check if these are created in Initialize or on APT process startup.
+ notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification");
+ pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause");
+}
+
+void APTShutdown() {
+
+}
+
+} // namespace APT
+} // namespace Service
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
new file mode 100644
index 000000000..a39adbff9
--- /dev/null
+++ b/src/core/hle/service/apt/apt.h
@@ -0,0 +1,222 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "core/hle/result.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace APT {
+
+/// Signals used by APT functions
+enum class SignalType : u32 {
+ None = 0x0,
+ AppJustStarted = 0x1,
+ ReturningToApp = 0xB,
+ ExitingApp = 0xC,
+};
+
+/// App Id's used by APT functions
+enum class AppID : u32 {
+ HomeMenu = 0x101,
+ AlternateMenu = 0x103,
+ Camera = 0x110,
+ FriendsList = 0x112,
+ GameNotes = 0x113,
+ InternetBrowser = 0x114,
+ InstructionManual = 0x115,
+ Notifications = 0x116,
+ Miiverse = 0x117,
+ SoftwareKeyboard1 = 0x201,
+ Ed = 0x202,
+ PnoteApp = 0x204,
+ SnoteApp = 0x205,
+ Error = 0x206,
+ Mint = 0x207,
+ Extrapad = 0x208,
+ Memolib = 0x209,
+ Application = 0x300,
+ SoftwareKeyboard2 = 0x401,
+};
+
+/**
+ * APT::Initialize service function
+ * Service function that initializes the APT process for the running application
+ * Outputs:
+ * 1 : Result of the function, 0 on success, otherwise error code
+ * 3 : Handle to the notification event
+ * 4 : Handle to the pause event
+ */
+void Initialize(Service::Interface* self);
+
+/**
+ * APT::GetSharedFont service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Virtual address of where shared font will be loaded in memory
+ * 4 : Handle to shared font memory
+ */
+void GetSharedFont(Service::Interface* self);
+
+/**
+ * APT::NotifyToWait service function
+ * Inputs:
+ * 1 : AppID
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void NotifyToWait(Service::Interface* self);
+
+void GetLockHandle(Service::Interface* self);
+
+void Enable(Service::Interface* self);
+
+/**
+ * APT::GetAppletManInfo service function.
+ * Inputs:
+ * 1 : Unknown
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unknown u32 value
+ * 3 : Unknown u8 value
+ * 4 : Home Menu AppId
+ * 5 : AppID of currently active app
+ */
+void GetAppletManInfo(Service::Interface* self);
+
+/**
+ * APT::IsRegistered service function. This returns whether the specified AppID is registered with NS yet.
+ * An AppID is "registered" once the process associated with the AppID uses APT:Enable. Home Menu uses this
+ * command to determine when the launched process is running and to determine when to stop using GSP etc,
+ * while displaying the "Nintendo 3DS" loading screen.
+ * Inputs:
+ * 1 : AppID
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output, 0 = not registered, 1 = registered.
+ */
+void IsRegistered(Service::Interface* self);
+
+void InquireNotification(Service::Interface* self);
+
+/**
+ * APT::SendParameter service function. This sets the parameter data state.
+ * Inputs:
+ * 1 : Source AppID
+ * 2 : Destination AppID
+ * 3 : Signal type
+ * 4 : Parameter buffer size, max size is 0x1000 (this can be zero)
+ * 5 : Value
+ * 6 : Handle to the destination process, likely used for shared memory (this can be zero)
+ * 7 : (Size<<14) | 2
+ * 8 : Input parameter buffer ptr
+ * Outputs:
+ * 0 : Return Header
+ * 1 : Result of function, 0 on success, otherwise error code
+*/
+void SendParameter(Service::Interface* self);
+
+/**
+ * APT::ReceiveParameter service function. This returns the current parameter data from NS state,
+ * from the source process which set the parameters. Once finished, NS will clear a flag in the NS
+ * state so that this command will return an error if this command is used again if parameters were
+ * not set again. This is called when the second Initialize event is triggered. It returns a signal
+ * type indicating why it was triggered.
+ * Inputs:
+ * 1 : AppID
+ * 2 : Parameter buffer size, max size is 0x1000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : AppID of the process which sent these parameters
+ * 3 : Signal type
+ * 4 : Actual parameter buffer size, this is <= to the the input size
+ * 5 : Value
+ * 6 : Handle from the source process which set the parameters, likely used for shared memory
+ * 7 : Size
+ * 8 : Output parameter buffer ptr
+ */
+void ReceiveParameter(Service::Interface* self);
+
+/**
+ * APT::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
+ * (except for the word value prior to the output handle), except this will not clear the flag
+ * (except when responseword[3]==8 || responseword[3]==9) in NS state.
+ * Inputs:
+ * 1 : AppID
+ * 2 : Parameter buffer size, max size is 0x1000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unknown, for now assume AppID of the process which sent these parameters
+ * 3 : Unknown, for now assume Signal type
+ * 4 : Actual parameter buffer size, this is <= to the the input size
+ * 5 : Value
+ * 6 : Handle from the source process which set the parameters, likely used for shared memory
+ * 7 : Size
+ * 8 : Output parameter buffer ptr
+ */
+void GlanceParameter(Service::Interface* self);
+
+/**
+ * APT::CancelParameter service function. When the parameter data is available, and when the above
+ * specified fields match the ones in NS state(for the ones where the checks are enabled), this
+ * clears the flag which indicates that parameter data is available
+ * (same flag cleared by APT:ReceiveParameter).
+ * Inputs:
+ * 1 : Flag, when non-zero NS will compare the word after this one with a field in the NS state.
+ * 2 : Unknown, this is the same as the first unknown field returned by APT:ReceiveParameter.
+ * 3 : Flag, when non-zero NS will compare the word after this one with a field in the NS state.
+ * 4 : AppID
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Status flag, 0 = failure due to no parameter data being available, or the above enabled
+ * fields don't match the fields in NS state. 1 = success.
+ */
+void CancelParameter(Service::Interface* self);
+
+/**
+ * APT::AppletUtility service function
+ * Inputs:
+ * 1 : Unknown, but clearly used for something
+ * 2 : Buffer 1 size (purpose is unknown)
+ * 3 : Buffer 2 size (purpose is unknown)
+ * 5 : Buffer 1 address (purpose is unknown)
+ * 65 : Buffer 2 address (purpose is unknown)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void AppletUtility(Service::Interface* self);
+
+/**
+ * APT::SetAppCpuTimeLimit service function
+ * Inputs:
+ * 1 : Value, must be one
+ * 2 : Percentage of CPU time from 5 to 80
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void SetAppCpuTimeLimit(Service::Interface* self);
+
+/**
+ * APT::GetAppCpuTimeLimit service function
+ * Inputs:
+ * 1 : Value, must be one
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : System core CPU time percentage
+ */
+void GetAppCpuTimeLimit(Service::Interface* self);
+
+/// Initialize the APT service
+void APTInit();
+
+/// Shutdown the APT service
+void APTShutdown();
+
+} // namespace APT
+} // namespace Service
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
new file mode 100644
index 000000000..dbe5c1d87
--- /dev/null
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -0,0 +1,33 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/hle.h"
+#include "core/hle/service/apt/apt.h"
+#include "core/hle/service/apt/apt_a.h"
+
+namespace Service {
+namespace APT {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, GetLockHandle, "GetLockHandle?"},
+ {0x00020080, Initialize, "Initialize?"},
+ {0x00030040, nullptr, "Enable?"},
+ {0x00040040, nullptr, "Finalize?"},
+ {0x00050040, nullptr, "GetAppletManInfo?"},
+ {0x00060040, nullptr, "GetAppletInfo?"},
+ {0x000D0080, ReceiveParameter, "ReceiveParameter?"},
+ {0x000E0080, GlanceParameter, "GlanceParameter?"},
+ {0x003B0040, nullptr, "CancelLibraryApplet?"},
+ {0x00430040, nullptr, "NotifyToWait?"},
+ {0x00440000, GetSharedFont, "GetSharedFont?"},
+ {0x004B00C2, nullptr, "AppletUtility?"},
+ {0x00550040, nullptr, "WriteInputToNsState?"},
+};
+
+APT_A_Interface::APT_A_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace APT
+} // namespace Service
diff --git a/src/core/hle/service/apt_a.h b/src/core/hle/service/apt/apt_a.h
index 6cbf1288f..331fb5586 100644
--- a/src/core/hle/service/apt_a.h
+++ b/src/core/hle/service/apt/apt_a.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace APT_A
+namespace Service {
+namespace APT {
-namespace APT_A {
-
-class Interface : public Service::Interface {
+class APT_A_Interface : public Service::Interface {
public:
- Interface();
+ APT_A_Interface();
std::string GetPortName() const override {
return "APT:A";
}
};
-} // namespace
+} // namespace APT
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
new file mode 100644
index 000000000..3fd348651
--- /dev/null
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -0,0 +1,104 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "common/common.h"
+#include "common/file_util.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/service/apt/apt.h"
+#include "core/hle/service/apt/apt_s.h"
+
+namespace Service {
+namespace APT {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, GetLockHandle, "GetLockHandle"},
+ {0x00020080, Initialize, "Initialize"},
+ {0x00030040, Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
+ {0x00050040, nullptr, "GetAppletManInfo"},
+ {0x00060040, nullptr, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
+ {0x00090040, nullptr, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
+ {0x000B0040, InquireNotification, "InquireNotification"},
+ {0x000C0104, nullptr, "SendParameter"},
+ {0x000D0080, ReceiveParameter, "ReceiveParameter"},
+ {0x000E0080, GlanceParameter, "GlanceParameter"},
+ {0x000F0100, nullptr, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, NotifyToWait, "NotifyToWait"},
+ {0x00440000, GetSharedFont, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, AppletUtility, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
+ {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
+ {0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
+};
+
+APT_S_Interface::APT_S_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace APT
+} // namespace Service
diff --git a/src/core/hle/service/apt_s.h b/src/core/hle/service/apt/apt_s.h
index f097c9747..8e87b69af 100644
--- a/src/core/hle/service/apt_s.h
+++ b/src/core/hle/service/apt/apt_s.h
@@ -6,10 +6,8 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace APT_S
-
-namespace APT_S {
+namespace Service {
+namespace APT {
// Application and title launching service. These services handle signaling for home/power button as
// well. Only one session for either APT service can be open at a time, normally processes close the
@@ -18,13 +16,14 @@ namespace APT_S {
// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
/// Interface to "APT:S" service
-class Interface : public Service::Interface {
+class APT_S_Interface : public Service::Interface {
public:
- Interface();
+ APT_S_Interface();
std::string GetPortName() const override {
return "APT:S";
}
};
-} // namespace
+} // namespace APT
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
new file mode 100644
index 000000000..5ab23801e
--- /dev/null
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -0,0 +1,103 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "common/common.h"
+#include "common/file_util.h"
+
+#include "core/hle/service/apt/apt.h"
+#include "core/hle/service/apt/apt_u.h"
+
+namespace Service {
+namespace APT {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, GetLockHandle, "GetLockHandle"},
+ {0x00020080, Initialize, "Initialize"},
+ {0x00030040, Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
+ {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
+ {0x00060040, nullptr, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
+ {0x00090040, IsRegistered, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
+ {0x000B0040, InquireNotification, "InquireNotification"},
+ {0x000C0104, SendParameter, "SendParameter"},
+ {0x000D0080, ReceiveParameter, "ReceiveParameter"},
+ {0x000E0080, GlanceParameter, "GlanceParameter"},
+ {0x000F0100, CancelParameter, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, NotifyToWait, "NotifyToWait"},
+ {0x00440000, GetSharedFont, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, AppletUtility, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
+ {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
+ {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
+};
+
+APT_U_Interface::APT_U_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace APT
+} // namespace Service
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt/apt_u.h
index aad918cfc..8c7fe0ccb 100644
--- a/src/core/hle/service/apt_u.h
+++ b/src/core/hle/service/apt/apt_u.h
@@ -6,10 +6,8 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace APT_U
-
-namespace APT_U {
+namespace Service {
+namespace APT {
// Application and title launching service. These services handle signaling for home/power button as
// well. Only one session for either APT service can be open at a time, normally processes close the
@@ -18,13 +16,14 @@ namespace APT_U {
// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
/// Interface to "APT:U" service
-class Interface : public Service::Interface {
+class APT_U_Interface : public Service::Interface {
public:
- Interface();
+ APT_U_Interface();
std::string GetPortName() const override {
return "APT:U";
}
};
-} // namespace
+} // namespace APT
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/apt_a.cpp b/src/core/hle/service/apt_a.cpp
deleted file mode 100644
index 1c1d92572..000000000
--- a/src/core/hle/service/apt_a.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/hle/hle.h"
-#include "core/hle/service/apt_a.h"
-
-namespace APT_U {
- extern void Initialize(Service::Interface* self);
- extern void GetLockHandle(Service::Interface* self);
- extern void ReceiveParameter(Service::Interface* self);
- extern void GlanceParameter(Service::Interface* self);
- extern void GetSharedFont(Service::Interface* self);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace APT_A
-
-namespace APT_A {
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, APT_U::GetLockHandle, "GetLockHandle?"},
- {0x00020080, APT_U::Initialize, "Initialize?"},
- {0x00030040, nullptr, "Enable?"},
- {0x00040040, nullptr, "Finalize?"},
- {0x00050040, nullptr, "GetAppletManInfo?"},
- {0x00060040, nullptr, "GetAppletInfo?"},
- {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter?"},
- {0x000E0080, APT_U::GlanceParameter, "GlanceParameter?"},
- {0x003B0040, nullptr, "CancelLibraryApplet?"},
- {0x00430040, nullptr, "NotifyToWait?"},
- {0x00440000, APT_U::GetSharedFont, "GetSharedFont?"},
- {0x004B00C2, nullptr, "AppletUtility?"},
- {0x00550040, nullptr, "WriteInputToNsState?"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- Register(FunctionTable);
-}
-
-} // namespace
diff --git a/src/core/hle/service/apt_s.cpp b/src/core/hle/service/apt_s.cpp
deleted file mode 100644
index 686335428..000000000
--- a/src/core/hle/service/apt_s.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-
-#include "common/common.h"
-#include "common/file_util.h"
-
-#include "core/hle/hle.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/mutex.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/service/apt_s.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace APT_S
-
-namespace APT_U {
- extern void GetLockHandle(Service::Interface* self);
- extern void Initialize(Service::Interface* self);
- extern void Enable(Service::Interface* self);
- extern void InquireNotification(Service::Interface* self);
- extern void NotifyToWait(Service::Interface* self);
- extern void GetSharedFont(Service::Interface* self);
- extern void AppletUtility(Service::Interface* self);
- extern void GlanceParameter(Service::Interface* self);
- extern void ReceiveParameter(Service::Interface* self);
-}
-
-namespace APT_S {
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, APT_U::GetLockHandle, "GetLockHandle"},
- {0x00020080, APT_U::Initialize, "Initialize"},
- {0x00030040, APT_U::Enable, "Enable"},
- {0x00040040, nullptr, "Finalize"},
- {0x00050040, nullptr, "GetAppletManInfo"},
- {0x00060040, nullptr, "GetAppletInfo"},
- {0x00070000, nullptr, "GetLastSignaledAppletId"},
- {0x00080000, nullptr, "CountRegisteredApplet"},
- {0x00090040, nullptr, "IsRegistered"},
- {0x000A0040, nullptr, "GetAttribute"},
- {0x000B0040, APT_U::InquireNotification, "InquireNotification"},
- {0x000C0104, nullptr, "SendParameter"},
- {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter"},
- {0x000E0080, APT_U::GlanceParameter, "GlanceParameter"},
- {0x000F0100, nullptr, "CancelParameter"},
- {0x001000C2, nullptr, "DebugFunc"},
- {0x001100C0, nullptr, "MapProgramIdForDebug"},
- {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
- {0x00130000, nullptr, "GetPreparationState"},
- {0x00140040, nullptr, "SetPreparationState"},
- {0x00150140, nullptr, "PrepareToStartApplication"},
- {0x00160040, nullptr, "PreloadLibraryApplet"},
- {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
- {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
- {0x00190040, nullptr, "PrepareToStartSystemApplet"},
- {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
- {0x001B00C4, nullptr, "StartApplication"},
- {0x001C0000, nullptr, "WakeupApplication"},
- {0x001D0000, nullptr, "CancelApplication"},
- {0x001E0084, nullptr, "StartLibraryApplet"},
- {0x001F0084, nullptr, "StartSystemApplet"},
- {0x00200044, nullptr, "StartNewestHomeMenu"},
- {0x00210000, nullptr, "OrderToCloseApplication"},
- {0x00220040, nullptr, "PrepareToCloseApplication"},
- {0x00230040, nullptr, "PrepareToJumpToApplication"},
- {0x00240044, nullptr, "JumpToApplication"},
- {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
- {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
- {0x00270044, nullptr, "CloseApplication"},
- {0x00280044, nullptr, "CloseLibraryApplet"},
- {0x00290044, nullptr, "CloseSystemApplet"},
- {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
- {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
- {0x002C0044, nullptr, "JumpToHomeMenu"},
- {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
- {0x002E0044, nullptr, "LeaveHomeMenu"},
- {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
- {0x00300044, nullptr, "LeaveResidentApplet"},
- {0x00310100, nullptr, "PrepareToDoApplicationJump"},
- {0x00320084, nullptr, "DoApplicationJump"},
- {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
- {0x00340084, nullptr, "SendDeliverArg"},
- {0x00350080, nullptr, "ReceiveDeliverArg"},
- {0x00360040, nullptr, "LoadSysMenuArg"},
- {0x00370042, nullptr, "StoreSysMenuArg"},
- {0x00380040, nullptr, "PreloadResidentApplet"},
- {0x00390040, nullptr, "PrepareToStartResidentApplet"},
- {0x003A0044, nullptr, "StartResidentApplet"},
- {0x003B0040, nullptr, "CancelLibraryApplet"},
- {0x003C0042, nullptr, "SendDspSleep"},
- {0x003D0042, nullptr, "SendDspWakeUp"},
- {0x003E0080, nullptr, "ReplySleepQuery"},
- {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
- {0x00400042, nullptr, "SendCaptureBufferInfo"},
- {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
- {0x00420080, nullptr, "SleepSystem"},
- {0x00430040, APT_U::NotifyToWait, "NotifyToWait"},
- {0x00440000, APT_U::GetSharedFont, "GetSharedFont"},
- {0x00450040, nullptr, "GetWirelessRebootInfo"},
- {0x00460104, nullptr, "Wrap"},
- {0x00470104, nullptr, "Unwrap"},
- {0x00480100, nullptr, "GetProgramInfo"},
- {0x00490180, nullptr, "Reboot"},
- {0x004A0040, nullptr, "GetCaptureInfo"},
- {0x004B00C2, APT_U::AppletUtility, "AppletUtility"},
- {0x004C0000, nullptr, "SetFatalErrDispMode"},
- {0x004D0080, nullptr, "GetAppletProgramInfo"},
- {0x004E0000, nullptr, "HardwareResetAsync"},
- {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
- {0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- Register(FunctionTable);
-}
-
-} // namespace
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
deleted file mode 100644
index 2d605a767..000000000
--- a/src/core/hle/service/apt_u.cpp
+++ /dev/null
@@ -1,526 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-
-#include "common/common.h"
-#include "common/file_util.h"
-
-#include "core/hle/hle.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/mutex.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/kernel/thread.h"
-#include "core/hle/service/apt_u.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace APT_U
-
-namespace APT_U {
-
-// Address used for shared font (as observed on HW)
-// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
-// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
-// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
-// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
-// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
-static const VAddr SHARED_FONT_VADDR = 0x18000000;
-
-/// Handle to shared memory region designated to for shared system font
-static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
-
-static Kernel::SharedPtr<Kernel::Mutex> lock;
-static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event
-static Kernel::SharedPtr<Kernel::Event> pause_event = 0; ///< APT pause event
-static std::vector<u8> shared_font;
-
-/// Signals used by APT functions
-enum class SignalType : u32 {
- None = 0x0,
- AppJustStarted = 0x1,
- ReturningToApp = 0xB,
- ExitingApp = 0xC,
-};
-
-/// App Id's used by APT functions
-enum class AppID : u32 {
- HomeMenu = 0x101,
- AlternateMenu = 0x103,
- Camera = 0x110,
- FriendsList = 0x112,
- GameNotes = 0x113,
- InternetBrowser = 0x114,
- InstructionManual = 0x115,
- Notifications = 0x116,
- Miiverse = 0x117,
- SoftwareKeyboard1 = 0x201,
- Ed = 0x202,
- PnoteApp = 0x204,
- SnoteApp = 0x205,
- Error = 0x206,
- Mint = 0x207,
- Extrapad = 0x208,
- Memolib = 0x209,
- Application = 0x300,
- SoftwareKeyboard2 = 0x401,
-};
-
-void Initialize(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- // TODO(bunnei): Check if these are created in Initialize or on APT process startup.
- notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification");
- pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause");
-
- cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom();
- cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom();
-
- // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called.
- notification_event->Clear();
- pause_event->Signal(); // Fire start event
-
- ASSERT_MSG((nullptr != lock), "Cannot initialize without lock");
- lock->Release();
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
-}
-
-/**
- * APT_U::NotifyToWait service function
- * Inputs:
- * 1 : AppID
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- */
-void NotifyToWait(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further.
- pause_event->Signal();
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
-}
-
-void GetLockHandle(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
-
- // Not sure what these parameters are used for, but retail apps check that they are 0 after
- // GetLockHandle has been called.
- cmd_buff[2] = 0;
- cmd_buff[3] = 0;
- cmd_buff[4] = 0;
-
- cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom();
- LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
-}
-
-void Enable(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
-}
-
-/**
- * APT_U::GetAppletManInfo service function.
- * Inputs:
- * 1 : Unknown
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Unknown u32 value
- * 3 : Unknown u8 value
- * 4 : Home Menu AppId
- * 5 : AppID of currently active app
- */
-void GetAppletManInfo(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 unk = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0;
- cmd_buff[3] = 0;
- cmd_buff[4] = static_cast<u32>(AppID::HomeMenu); // Home menu AppID
- cmd_buff[5] = static_cast<u32>(AppID::Application); // TODO(purpasmart96): Do this correctly
-
- LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
-}
-
-/**
- * APT_U::IsRegistered service function. This returns whether the specified AppID is registered with NS yet.
- * An AppID is "registered" once the process associated with the AppID uses APT:Enable. Home Menu uses this
- * command to determine when the launched process is running and to determine when to stop using GSP etc,
- * while displaying the "Nintendo 3DS" loading screen.
- * Inputs:
- * 1 : AppID
- * Outputs:
- * 0 : Return header
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Output, 0 = not registered, 1 = registered.
- */
-static void IsRegistered(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 1; // Set to registered
- LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
-}
-
-void InquireNotification(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
- LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
-}
-
-/**
- * APT_U::SendParameter service function. This sets the parameter data state.
- * Inputs:
- * 1 : Source AppID
- * 2 : Destination AppID
- * 3 : Signal type
- * 4 : Parameter buffer size, max size is 0x1000 (this can be zero)
- * 5 : Value
- * 6 : Handle to the destination process, likely used for shared memory (this can be zero)
- * 7 : (Size<<14) | 2
- * 8 : Input parameter buffer ptr
- * Outputs:
- * 0 : Return Header
- * 1 : Result of function, 0 on success, otherwise error code
-*/
-static void SendParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 src_app_id = cmd_buff[1];
- u32 dst_app_id = cmd_buff[2];
- u32 signal_type = cmd_buff[3];
- u32 buffer_size = cmd_buff[4];
- u32 value = cmd_buff[5];
- u32 handle = cmd_buff[6];
- u32 size = cmd_buff[7];
- u32 in_param_buffer_ptr = cmd_buff[8];
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
-
- LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
- "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X",
- src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr);
-}
-
-/**
- * APT_U::ReceiveParameter service function. This returns the current parameter data from NS state,
- * from the source process which set the parameters. Once finished, NS will clear a flag in the NS
- * state so that this command will return an error if this command is used again if parameters were
- * not set again. This is called when the second Initialize event is triggered. It returns a signal
- * type indicating why it was triggered.
- * Inputs:
- * 1 : AppID
- * 2 : Parameter buffer size, max size is 0x1000
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : AppID of the process which sent these parameters
- * 3 : Signal type
- * 4 : Actual parameter buffer size, this is <= to the the input size
- * 5 : Value
- * 6 : Handle from the source process which set the parameters, likely used for shared memory
- * 7 : Size
- * 8 : Output parameter buffer ptr
- */
-void ReceiveParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- u32 buffer_size = cmd_buff[2];
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0;
- cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0x10; // Parameter buffer size (16)
- cmd_buff[5] = 0;
- cmd_buff[6] = 0;
- cmd_buff[7] = 0;
- LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
-}
-
-/**
- * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
- * (except for the word value prior to the output handle), except this will not clear the flag
- * (except when responseword[3]==8 || responseword[3]==9) in NS state.
- * Inputs:
- * 1 : AppID
- * 2 : Parameter buffer size, max size is 0x1000
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Unknown, for now assume AppID of the process which sent these parameters
- * 3 : Unknown, for now assume Signal type
- * 4 : Actual parameter buffer size, this is <= to the the input size
- * 5 : Value
- * 6 : Handle from the source process which set the parameters, likely used for shared memory
- * 7 : Size
- * 8 : Output parameter buffer ptr
- */
-void GlanceParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[1];
- u32 buffer_size = cmd_buff[2];
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0;
- cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0x10; // Parameter buffer size (16)
- cmd_buff[5] = 0;
- cmd_buff[6] = 0;
- cmd_buff[7] = 0;
-
- LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
-}
-
-/**
- * APT_U::CancelParameter service function. When the parameter data is available, and when the above
- * specified fields match the ones in NS state(for the ones where the checks are enabled), this
- * clears the flag which indicates that parameter data is available
- * (same flag cleared by APT:ReceiveParameter).
- * Inputs:
- * 1 : Flag, when non-zero NS will compare the word after this one with a field in the NS state.
- * 2 : Unknown, this is the same as the first unknown field returned by APT:ReceiveParameter.
- * 3 : Flag, when non-zero NS will compare the word after this one with a field in the NS state.
- * 4 : AppID
- * Outputs:
- * 0 : Return header
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Status flag, 0 = failure due to no parameter data being available, or the above enabled
- * fields don't match the fields in NS state. 1 = success.
- */
-static void CancelParameter(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 flag1 = cmd_buff[1];
- u32 unk = cmd_buff[2];
- u32 flag2 = cmd_buff[3];
- u32 app_id = cmd_buff[4];
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 1; // Set to Success
-
- LOG_WARNING(Service_APT, "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X",
- flag1, unk, flag2, app_id);
-}
-
-/**
- * APT_U::AppletUtility service function
- * Inputs:
- * 1 : Unknown, but clearly used for something
- * 2 : Buffer 1 size (purpose is unknown)
- * 3 : Buffer 2 size (purpose is unknown)
- * 5 : Buffer 1 address (purpose is unknown)
- * 65 : Buffer 2 address (purpose is unknown)
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- */
-void AppletUtility(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- // These are from 3dbrew - I'm not really sure what they're used for.
- u32 unk = cmd_buff[1];
- u32 buffer1_size = cmd_buff[2];
- u32 buffer2_size = cmd_buff[3];
- u32 buffer1_addr = cmd_buff[5];
- u32 buffer2_addr = cmd_buff[65];
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
-
- LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
- "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
- buffer1_addr, buffer2_addr);
-}
-
-/**
- * APT_U::GetSharedFont service function
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Virtual address of where shared font will be loaded in memory
- * 4 : Handle to shared font memory
- */
-void GetSharedFont(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- if (!shared_font.empty()) {
- // TODO(bunnei): This function shouldn't copy the shared font every time it's called.
- // Instead, it should probably map the shared font as RO memory. We don't currently have
- // an easy way to do this, but the copy should be sufficient for now.
- memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
-
- cmd_buff[0] = 0x00440082;
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = SHARED_FONT_VADDR;
- cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom();
- } else {
- cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
- LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
- }
-}
-
-/**
- * APT_U::SetAppCpuTimeLimit service function
- * Inputs:
- * 1 : Value, must be one
- * 2 : Percentage of CPU time from 5 to 80
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- */
-static void SetAppCpuTimeLimit(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 value = cmd_buff[1];
- u32 percent = cmd_buff[2];
-
- if (value != 1) {
- LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
- }
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
-
- LOG_WARNING(Service_APT, "(STUBBED) called percent=0x%08X, value=0x%08x", percent, value);
-}
-
-/**
- * APT_U::GetAppCpuTimeLimit service function
- * Inputs:
- * 1 : Value, must be one
- * Outputs:
- * 0 : Return header
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : System core CPU time percentage
- */
-static void GetAppCpuTimeLimit(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 value = cmd_buff[1];
-
- if (value != 1) {
- LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value);
- }
-
- // TODO(purpasmart96): This is incorrect, I'm pretty sure the percentage should
- // be set by the application.
-
- cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- cmd_buff[2] = 0x80; // Set to 80%
-
- LOG_WARNING(Service_APT, "(STUBBED) called value=0x%08x", value);
-}
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, GetLockHandle, "GetLockHandle"},
- {0x00020080, Initialize, "Initialize"},
- {0x00030040, Enable, "Enable"},
- {0x00040040, nullptr, "Finalize"},
- {0x00050040, GetAppletManInfo, "GetAppletManInfo"},
- {0x00060040, nullptr, "GetAppletInfo"},
- {0x00070000, nullptr, "GetLastSignaledAppletId"},
- {0x00080000, nullptr, "CountRegisteredApplet"},
- {0x00090040, IsRegistered, "IsRegistered"},
- {0x000A0040, nullptr, "GetAttribute"},
- {0x000B0040, InquireNotification, "InquireNotification"},
- {0x000C0104, SendParameter, "SendParameter"},
- {0x000D0080, ReceiveParameter, "ReceiveParameter"},
- {0x000E0080, GlanceParameter, "GlanceParameter"},
- {0x000F0100, CancelParameter, "CancelParameter"},
- {0x001000C2, nullptr, "DebugFunc"},
- {0x001100C0, nullptr, "MapProgramIdForDebug"},
- {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
- {0x00130000, nullptr, "GetPreparationState"},
- {0x00140040, nullptr, "SetPreparationState"},
- {0x00150140, nullptr, "PrepareToStartApplication"},
- {0x00160040, nullptr, "PreloadLibraryApplet"},
- {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
- {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
- {0x00190040, nullptr, "PrepareToStartSystemApplet"},
- {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
- {0x001B00C4, nullptr, "StartApplication"},
- {0x001C0000, nullptr, "WakeupApplication"},
- {0x001D0000, nullptr, "CancelApplication"},
- {0x001E0084, nullptr, "StartLibraryApplet"},
- {0x001F0084, nullptr, "StartSystemApplet"},
- {0x00200044, nullptr, "StartNewestHomeMenu"},
- {0x00210000, nullptr, "OrderToCloseApplication"},
- {0x00220040, nullptr, "PrepareToCloseApplication"},
- {0x00230040, nullptr, "PrepareToJumpToApplication"},
- {0x00240044, nullptr, "JumpToApplication"},
- {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
- {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
- {0x00270044, nullptr, "CloseApplication"},
- {0x00280044, nullptr, "CloseLibraryApplet"},
- {0x00290044, nullptr, "CloseSystemApplet"},
- {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
- {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
- {0x002C0044, nullptr, "JumpToHomeMenu"},
- {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
- {0x002E0044, nullptr, "LeaveHomeMenu"},
- {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
- {0x00300044, nullptr, "LeaveResidentApplet"},
- {0x00310100, nullptr, "PrepareToDoApplicationJump"},
- {0x00320084, nullptr, "DoApplicationJump"},
- {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
- {0x00340084, nullptr, "SendDeliverArg"},
- {0x00350080, nullptr, "ReceiveDeliverArg"},
- {0x00360040, nullptr, "LoadSysMenuArg"},
- {0x00370042, nullptr, "StoreSysMenuArg"},
- {0x00380040, nullptr, "PreloadResidentApplet"},
- {0x00390040, nullptr, "PrepareToStartResidentApplet"},
- {0x003A0044, nullptr, "StartResidentApplet"},
- {0x003B0040, nullptr, "CancelLibraryApplet"},
- {0x003C0042, nullptr, "SendDspSleep"},
- {0x003D0042, nullptr, "SendDspWakeUp"},
- {0x003E0080, nullptr, "ReplySleepQuery"},
- {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
- {0x00400042, nullptr, "SendCaptureBufferInfo"},
- {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
- {0x00420080, nullptr, "SleepSystem"},
- {0x00430040, NotifyToWait, "NotifyToWait"},
- {0x00440000, GetSharedFont, "GetSharedFont"},
- {0x00450040, nullptr, "GetWirelessRebootInfo"},
- {0x00460104, nullptr, "Wrap"},
- {0x00470104, nullptr, "Unwrap"},
- {0x00480100, nullptr, "GetProgramInfo"},
- {0x00490180, nullptr, "Reboot"},
- {0x004A0040, nullptr, "GetCaptureInfo"},
- {0x004B00C2, AppletUtility, "AppletUtility"},
- {0x004C0000, nullptr, "SetFatalErrDispMode"},
- {0x004D0080, nullptr, "GetAppletProgramInfo"},
- {0x004E0000, nullptr, "HardwareResetAsync"},
- {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
- {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- // Load the shared system font (if available).
- // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
- // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
- // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
- // "shared_font.bin" in the Citra "sysdata" directory.
-
- shared_font.clear();
- std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
-
- FileUtil::CreateFullPath(filepath); // Create path if not already created
- FileUtil::IOFile file(filepath, "rb");
-
- if (file.IsOpen()) {
- // Read shared font data
- shared_font.resize((size_t)file.GetSize());
- file.ReadBytes(shared_font.data(), (size_t)file.GetSize());
-
- // Create shared font memory object
- shared_font_mem = Kernel::SharedMemory::Create("APT_U:shared_font_mem");
- } else {
- LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
- shared_font_mem = nullptr;
- }
-
- lock = Kernel::Mutex::Create(false, "APT_U:Lock");
-
- Register(FunctionTable);
-}
-
-} // namespace
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 71986f7d2..6adadb224 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -3,9 +3,13 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include "common/make_unique.h"
-#include "core/file_sys/archive_systemsavedata.h"
+
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/service.h"
#include "core/hle/service/cfg/cfg.h"
+#include "core/hle/service/cfg/cfg_i.h"
+#include "core/hle/service/cfg/cfg_s.h"
+#include "core/hle/service/cfg/cfg_u.h"
namespace Service {
namespace CFG {
@@ -36,7 +40,8 @@ const std::array<float, 8> STEREO_CAMERA_SETTINGS = {
static const u32 CONFIG_SAVEFILE_SIZE = 0x8000;
static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
-static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data;
+static Service::FS::ArchiveHandle cfg_system_save_data_archive;
+static const std::vector<u8> cfg_system_savedata_id = { 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00 };
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
// Read the header
@@ -102,19 +107,22 @@ ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data
ResultCode DeleteConfigNANDSaveFile() {
FileSys::Path path("config");
- if (cfg_system_save_data->DeleteFile(path))
- return RESULT_SUCCESS;
- return ResultCode(-1); // TODO(Subv): Find the right error code
+ return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path);
}
ResultCode UpdateConfigNANDSavegame() {
FileSys::Mode mode = {};
mode.write_flag = 1;
mode.create_flag = 1;
+
FileSys::Path path("config");
- auto file = cfg_system_save_data->OpenFile(path, mode);
- ASSERT_MSG(file != nullptr, "could not open file");
- file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data());
+
+ auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode);
+ ASSERT_MSG(config_result.Succeeded(), "could not open file");
+
+ auto config = config_result.MoveFrom();
+ config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data());
+
return RESULT_SUCCESS;
}
@@ -163,27 +171,37 @@ ResultCode FormatConfig() {
}
void CFGInit() {
- // TODO(Subv): In the future we should use the FS service to query this archive,
- // currently it is not possible because you can only have one open archive of the same type at any time
- std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
- cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>(
- nand_directory, CFG_SAVE_ID);
- if (!cfg_system_save_data->Initialize()) {
- LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service");
- return;
+ AddService(new CFG_I_Interface);
+ AddService(new CFG_S_Interface);
+ AddService(new CFG_U_Interface);
+
+ // Open the SystemSaveData archive 0x00010017
+ FileSys::Path archive_path(cfg_system_savedata_id);
+ auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
+
+ // If the archive didn't exist, create the files inside
+ if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
+ // Format the archive to create the directories
+ Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
+
+ // Open it again to get a valid archive now that the folder exists
+ archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
}
- // TODO(Subv): All this code should be moved to cfg:i,
- // it's only here because we do not currently emulate the lower level code that uses that service
- // Try to open the file in read-only mode to check its existence
- FileSys::Mode mode = {};
- mode.read_flag = 1;
- FileSys::Path path("config");
- auto file = cfg_system_save_data->OpenFile(path, mode);
+ ASSERT_MSG(archive_result.Succeeded(), "Could not open the CFG SystemSaveData archive!");
+
+ cfg_system_save_data_archive = *archive_result;
+
+ FileSys::Path config_path("config");
+ FileSys::Mode open_mode = {};
+ open_mode.read_flag = 1;
- // Load the config if it already exists
- if (file != nullptr) {
- file->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
+ auto config_result = Service::FS::OpenFileFromArchive(*archive_result, config_path, open_mode);
+
+ // Read the file if it already exists
+ if (config_result.Succeeded()) {
+ auto config = config_result.MoveFrom();
+ config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
return;
}
@@ -191,10 +209,12 @@ void CFGInit() {
// TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals
CONSOLE_USERNAME_BLOCK.ng_word = 0;
CONSOLE_USERNAME_BLOCK.zero = 0;
+
// Copy string to buffer and pad with zeros at the end
auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14);
std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size,
std::end(CONSOLE_USERNAME_BLOCK.username), 0);
+
FormatConfig();
}
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp
index 20b09a8cb..6d1eee4e0 100644
--- a/src/core/hle/service/cfg/cfg_i.cpp
+++ b/src/core/hle/service/cfg/cfg_i.cpp
@@ -6,10 +6,8 @@
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_i.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace CFG_I
-
-namespace CFG_I {
+namespace Service {
+namespace CFG {
/**
* CFG_I::GetConfigInfoBlk8 service function
@@ -99,11 +97,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08180042, nullptr, "SecureInfoGetSerialNo"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+CFG_I_Interface::CFG_I_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace CFG
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/cfg/cfg_i.h b/src/core/hle/service/cfg/cfg_i.h
index a498dd589..d0a2cce39 100644
--- a/src/core/hle/service/cfg/cfg_i.h
+++ b/src/core/hle/service/cfg/cfg_i.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace CFG_I
+namespace Service {
+namespace CFG {
-namespace CFG_I {
-
-class Interface : public Service::Interface {
+class CFG_I_Interface : public Service::Interface {
public:
- Interface();
+ CFG_I_Interface();
std::string GetPortName() const override {
return "cfg:i";
}
};
-} // namespace
+} // namespace CFG
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
index d80aeae8d..d9a3e5d51 100644
--- a/src/core/hle/service/cfg/cfg_s.cpp
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -6,10 +6,8 @@
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_s.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace CFG_S
-
-namespace CFG_S {
+namespace Service {
+namespace CFG {
/**
* CFG_S::GetConfigInfoBlk2 service function
@@ -87,11 +85,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x04090000, nullptr, "UpdateConfigBlk00040003"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+CFG_S_Interface::CFG_S_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace CFG
+} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h
index d8b67137f..5568d6485 100644
--- a/src/core/hle/service/cfg/cfg_s.h
+++ b/src/core/hle/service/cfg/cfg_s.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace CFG_S
+namespace Service {
+namespace CFG {
-namespace CFG_S {
-
-class Interface : public Service::Interface {
+class CFG_S_Interface : public Service::Interface {
public:
- Interface();
+ CFG_S_Interface();
std::string GetPortName() const override {
return "cfg:s";
}
};
-} // namespace
+} // namespace CFG
+} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp
index a65da90c5..c8c1c5b17 100644
--- a/src/core/hle/service/cfg/cfg_u.cpp
+++ b/src/core/hle/service/cfg/cfg_u.cpp
@@ -10,10 +10,8 @@
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_u.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace CFG_U
-
-namespace CFG_U {
+namespace Service {
+namespace CFG {
// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
#define C(code) ((code)[0] | ((code)[1] << 8))
@@ -241,11 +239,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+CFG_U_Interface::CFG_U_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace CFG
+} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg_u.h b/src/core/hle/service/cfg/cfg_u.h
index 9ad73f355..5303d8ac6 100644
--- a/src/core/hle/service/cfg/cfg_u.h
+++ b/src/core/hle/service/cfg/cfg_u.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace CFG_U
+namespace Service {
+namespace CFG {
-namespace CFG_U {
-
-class Interface : public Service::Interface {
+class CFG_U_Interface : public Service::Interface {
public:
- Interface();
+ CFG_U_Interface();
std::string GetPortName() const override {
return "cfg:u";
}
};
-} // namespace
+} // namespace CFG
+} // namespace Service
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 8d765acb5..58c5acd1e 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -10,8 +10,171 @@
namespace ERR_F {
+enum {
+ ErrSpecifier0 = 0,
+ ErrSpecifier1 = 1,
+ ErrSpecifier3 = 3,
+ ErrSpecifier4 = 4,
+};
+
+// This is used instead of ResultCode from result.h
+// because we can't have non-trivial data members in unions.
+union RSL {
+ u32 raw;
+
+ BitField<0, 10, u32> description;
+ BitField<10, 8, u32> module;
+ BitField<21, 6, u32> summary;
+ BitField<27, 5, u32> level;
+};
+
+union ErrInfo {
+ u8 specifier;
+
+ struct {
+ u8 specifier; // 0x0
+ u8 rev_high; // 0x1
+ u16 rev_low; // 0x2
+ RSL result_code; // 0x4
+ u32 address; // 0x8
+ INSERT_PADDING_BYTES(4); // 0xC
+ u32 pid_low; // 0x10
+ u32 pid_high; // 0x14
+ u32 aid_low; // 0x18
+ u32 aid_high; // 0x1C
+ } errtype1;
+
+ struct {
+ u8 specifier; // 0x0
+ u8 rev_high; // 0x1
+ u16 rev_low; // 0x2
+ INSERT_PADDING_BYTES(0xC); // 0x4
+ u32 pid_low; // 0x10
+ u32 pid_high; // 0x14
+ u32 aid_low; // 0x18
+ u32 aid_high; // 0x1C
+ u8 error_type; // 0x20
+ INSERT_PADDING_BYTES(3); // 0x21
+ u32 fault_status_reg; // 0x24
+ u32 fault_addr; // 0x28
+ u32 fpexc; // 0x2C
+ u32 finst; // 0x30
+ u32 finst2; // 0x34
+ INSERT_PADDING_BYTES(0x34); // 0x38
+ u32 sp; // 0x6C
+ u32 pc; // 0x70
+ u32 lr; // 0x74
+ u32 cpsr; // 0x78
+ } errtype3;
+
+ struct {
+ u8 specifier; // 0x0
+ u8 rev_high; // 0x1
+ u16 rev_low; // 0x2
+ RSL result_code; // 0x4
+ INSERT_PADDING_BYTES(8); // 0x8
+ u32 pid_low; // 0x10
+ u32 pid_high; // 0x14
+ u32 aid_low; // 0x18
+ u32 aid_high; // 0x1C
+ char debug_string1[0x2E]; // 0x20
+ char debug_string2[0x2E]; // 0x4E
+ } errtype4;
+};
+
+enum {
+ PrefetchAbort = 0,
+ DataAbort = 1,
+ UndefInstr = 2,
+ VectorFP = 3
+};
+
+static std::string GetErrInfo3Type(u8 type_code) {
+ switch (type_code) {
+ case PrefetchAbort: return "Prefetch Abort";
+ case DataAbort: return "Data Abort";
+ case UndefInstr: return "Undefined Instruction";
+ case VectorFP: return "Vector Floating Point";
+ default: return "unknown";
+ }
+}
+
+static void ThrowFatalError(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ LOG_CRITICAL(Service_ERR, "Fatal error!");
+ const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]);
+
+ switch (errinfo->specifier) {
+ case ErrSpecifier0:
+ case ErrSpecifier1:
+ {
+ const auto& errtype = errinfo->errtype1;
+ LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high);
+ LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16));
+ LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high);
+ LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errtype.address);
+
+ LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw);
+ LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value());
+ LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value());
+ LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value());
+ LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value());
+ break;
+ }
+
+ case ErrSpecifier3:
+ {
+ const auto& errtype = errinfo->errtype3;
+ LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high);
+ LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16));
+ LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high);
+ LOG_CRITICAL(Service_ERR, "TYPE: %s", GetErrInfo3Type(errtype.error_type).c_str());
+
+ LOG_CRITICAL(Service_ERR, "PC: 0x%08X", errtype.pc);
+ LOG_CRITICAL(Service_ERR, "LR: 0x%08X", errtype.lr);
+ LOG_CRITICAL(Service_ERR, "SP: 0x%08X", errtype.sp);
+ LOG_CRITICAL(Service_ERR, "CPSR: 0x%08X", errtype.cpsr);
+
+ switch (errtype.error_type) {
+ case PrefetchAbort:
+ case DataAbort:
+ LOG_CRITICAL(Service_ERR, "Fault Address: 0x%08X", errtype.fault_addr);
+ LOG_CRITICAL(Service_ERR, "Fault Status Register: 0x%08X", errtype.fault_status_reg);
+ break;
+ case VectorFP:
+ LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", errtype.fpexc);
+ LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", errtype.finst);
+ LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.finst2);
+ break;
+ }
+ break;
+ }
+
+ case ErrSpecifier4:
+ {
+ const auto& errtype = errinfo->errtype4;
+ LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high);
+ LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16));
+ LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high);
+
+ LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw);
+ LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value());
+ LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value());
+ LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value());
+ LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value());
+
+ LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string1);
+ LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string2);
+ break;
+ }
+ }
+
+ cmd_buff[1] = 0; // No error
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010800, nullptr, "ThrowFatalError"}
+ {0x00010800, ThrowFatalError, "ThrowFatalError"}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index a69c4f25b..9da2e7aa2 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -18,8 +18,11 @@
#include "core/file_sys/archive_savedata.h"
#include "core/file_sys/archive_savedatacheck.h"
#include "core/file_sys/archive_sdmc.h"
+#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/directory_backend.h"
+#include "core/hle/service/service.h"
#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/fs/fs_user.h"
#include "core/hle/result.h"
// Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map.
@@ -418,6 +421,8 @@ ResultCode CreateExtSaveData(u32 high, u32 low) {
void ArchiveInit() {
next_handle = 1;
+ AddService(new FS::Interface);
+
// TODO(Subv): Add the other archive types (see here for the known types:
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
@@ -449,6 +454,9 @@ void ArchiveInit() {
// Create the SaveDataCheck archive, basically a small variation of the RomFS archive
auto savedatacheck_factory = Common::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory);
RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck);
+
+ auto systemsavedata_factory = Common::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
+ RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
}
/// Shutdown archives
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 71ee4ff55..eb312496e 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -487,6 +487,15 @@ static void FormatThisUserSaveData(Service::Interface* self) {
cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw;
}
+/**
+ * FS_User::CreateExtSaveData service function
+ * Inputs:
+ * 0: 0x08510242
+ * 1: High word of the saveid to create
+ * 2: Low word of the saveid to create
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
static void CreateExtSaveData(Service::Interface* self) {
// TODO(Subv): Figure out the other parameters.
u32* cmd_buff = Kernel::GetCommandBuffer();
@@ -496,7 +505,22 @@ static void CreateExtSaveData(Service::Interface* self) {
cmd_buff[1] = CreateExtSaveData(save_high, save_low).raw;
}
-const FSUserInterface::FunctionInfo FunctionTable[] = {
+/**
+ * FS_User::CardSlotIsInserted service function.
+ * Inputs:
+ * 0: 0x08210000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Whether there is a game card inserted into the slot or not.
+ */
+static void CardSlotIsInserted(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
{0x000100C6, nullptr, "Dummy1"},
{0x040100C4, nullptr, "Control"},
{0x08010002, Initialize, "Initialize"},
@@ -531,7 +555,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
{0x081E0042, nullptr, "GetNandLog"},
{0x081F0000, nullptr, "ClearSdmcLog"},
{0x08200000, nullptr, "ClearNandLog"},
- {0x08210000, nullptr, "CardSlotIsInserted"},
+ {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"},
{0x08220000, nullptr, "CardSlotPowerOn"},
{0x08230000, nullptr, "CardSlotPowerOff"},
{0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
@@ -590,7 +614,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
-FSUserInterface::FSUserInterface() {
+Interface::Interface() {
Register(FunctionTable);
}
diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h
index 2d896dd5f..bb6ab195e 100644
--- a/src/core/hle/service/fs/fs_user.h
+++ b/src/core/hle/service/fs/fs_user.h
@@ -6,16 +6,13 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace FS_User
-
namespace Service {
namespace FS {
/// Interface to "fs:USER" service
-class FSUserInterface : public Service::Interface {
+class Interface : public Service::Interface {
public:
- FSUserInterface();
+ Interface();
std::string GetPortName() const override {
return "fs:USER";
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 7cb01729e..e0689be2e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,7 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/service/service.h"
#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/hid/hid_spvr.h"
+#include "core/hle/service/hid/hid_user.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/event.h"
@@ -35,6 +38,19 @@ static inline PadData* GetPadData() {
return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr));
}
+// TODO(peachum):
+// Add a method for setting analog input from joystick device for the circle Pad.
+//
+// This method should:
+// * Be called after both PadButton<Press, Release>().
+// * Be called before PadUpdateComplete()
+// * Set current PadEntry.circle_pad_<axis> using analog data
+// * Set PadData.raw_circle_pad_data
+// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41
+// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41
+// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41
+// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
+
/**
* Circle Pad from keys.
*
@@ -121,9 +137,25 @@ void PadUpdateComplete() {
g_event_pad_or_touch_2->Signal();
}
+void GetIPCHandles(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
+ cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom();
+ cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom();
+ cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom();
+ cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom();
+ cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom();
+}
+
void HIDInit() {
using namespace Kernel;
+ AddService(new HID_U_Interface);
+ AddService(new HID_SPVR_Interface);
+
g_shared_mem = SharedMemory::Create("HID:SharedMem");
// Create event handles
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 2116d2ca3..9c6e86f77 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -7,6 +7,7 @@
#include <array>
#include "core/hle/kernel/kernel.h"
+#include "core/hle/service/service.h"
#include "common/bit_field.h"
namespace Kernel {
@@ -47,6 +48,15 @@ struct PadState {
BitField<10, 1, u32> x;
BitField<11, 1, u32> y;
+ BitField<14, 1, u32> zl;
+ BitField<15, 1, u32> zr;
+
+ BitField<20, 1, u32> touch;
+
+ BitField<24, 1, u32> c_right;
+ BitField<25, 1, u32> c_left;
+ BitField<26, 1, u32> c_up;
+ BitField<27, 1, u32> c_down;
BitField<28, 1, u32> circle_right;
BitField<29, 1, u32> circle_left;
BitField<30, 1, u32> circle_up;
@@ -99,11 +109,37 @@ const PadState PAD_R = {{1u << 8}};
const PadState PAD_L = {{1u << 9}};
const PadState PAD_X = {{1u << 10}};
const PadState PAD_Y = {{1u << 11}};
+
+const PadState PAD_ZL = {{1u << 14}};
+const PadState PAD_ZR = {{1u << 15}};
+
+const PadState PAD_TOUCH = {{1u << 20}};
+
+const PadState PAD_C_RIGHT = {{1u << 24}};
+const PadState PAD_C_LEFT = {{1u << 25}};
+const PadState PAD_C_UP = {{1u << 26}};
+const PadState PAD_C_DOWN = {{1u << 27}};
const PadState PAD_CIRCLE_RIGHT = {{1u << 28}};
const PadState PAD_CIRCLE_LEFT = {{1u << 29}};
const PadState PAD_CIRCLE_UP = {{1u << 30}};
const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
+/**
+ * HID::GetIPCHandles service function
+ * Inputs:
+ * None
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Unused
+ * 3 : Handle to HID_User shared memory
+ * 4 : Event signaled by HID_User
+ * 5 : Event signaled by HID_User
+ * 6 : Event signaled by HID_User
+ * 7 : Gyroscope event
+ * 8 : Event signaled by HID_User
+ */
+void GetIPCHandles(Interface* self);
+
// Methods for updating the HID module's state
void PadButtonPress(const PadState& pad_state);
void PadButtonRelease(const PadState& pad_state);
diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp
index 8f06b224d..790dcabbf 100644
--- a/src/core/hle/service/hid/hid_spvr.cpp
+++ b/src/core/hle/service/hid/hid_spvr.cpp
@@ -3,19 +3,14 @@
// Refer to the license.txt file included.
#include "core/hle/hle.h"
+#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_spvr.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace HID_SPVR
-
-namespace HID_User {
- extern void GetIPCHandles(Service::Interface* self);
-}
-
-namespace HID_SPVR {
+namespace Service {
+namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000A0000, HID_User::GetIPCHandles, "GetIPCHandles"},
+ {0x000A0000, GetIPCHandles, "GetIPCHandles"},
{0x000B0000, nullptr, "StartAnalogStickCalibration"},
{0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
{0x00110000, nullptr, "EnableAccelerometer"},
@@ -27,11 +22,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00170000, nullptr, "GetSoundVolume"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+HID_SPVR_Interface::HID_SPVR_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace HID
+} // namespace Service
diff --git a/src/core/hle/service/hid/hid_spvr.h b/src/core/hle/service/hid/hid_spvr.h
index 53ddc8569..ba61583d2 100644
--- a/src/core/hle/service/hid/hid_spvr.h
+++ b/src/core/hle/service/hid/hid_spvr.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace HID_SPVR
+namespace Service {
+namespace HID {
-namespace HID_SPVR {
-
-class Interface : public Service::Interface {
+class HID_SPVR_Interface : public Service::Interface {
public:
- Interface();
+ HID_SPVR_Interface();
std::string GetPortName() const override {
return "hid:SPVR";
}
};
-} // namespace
+} // namespace HID
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp
index 7f464705f..1d0accefe 100644
--- a/src/core/hle/service/hid/hid_user.cpp
+++ b/src/core/hle/service/hid/hid_user.cpp
@@ -6,54 +6,10 @@
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/hid.h"
-#include "hid_user.h"
+#include "core/hle/service/hid/hid_user.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace HID_User
-
-namespace HID_User {
-
-
-// TODO(peachum):
-// Add a method for setting analog input from joystick device for the circle Pad.
-//
-// This method should:
-// * Be called after both PadButton<Press, Release>().
-// * Be called before PadUpdateComplete()
-// * Set current PadEntry.circle_pad_<axis> using analog data
-// * Set PadData.raw_circle_pad_data
-// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41
-// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41
-// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41
-// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41
-
-
-/**
- * HID_User::GetIPCHandles service function
- * Inputs:
- * None
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Unused
- * 3 : Handle to HID_User shared memory
- * 4 : Event signaled by HID_User
- * 5 : Event signaled by HID_User
- * 6 : Event signaled by HID_User
- * 7 : Gyroscope event
- * 8 : Event signaled by HID_User
- */
-void GetIPCHandles(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- cmd_buff[1] = 0; // No error
- // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
- cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom();
- cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom();
- cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom();
- cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom();
- cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom();
- cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom();
-}
+namespace Service {
+namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, GetIPCHandles, "GetIPCHandles"},
@@ -66,11 +22,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00170000, nullptr, "GetSoundVolume"},
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+HID_U_Interface::HID_U_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace HID
+} // namespace Service
diff --git a/src/core/hle/service/hid/hid_user.h b/src/core/hle/service/hid/hid_user.h
index 1d9929e67..0eeec2c25 100644
--- a/src/core/hle/service/hid/hid_user.h
+++ b/src/core/hle/service/hid/hid_user.h
@@ -6,24 +6,23 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace HID_User
-
// This service is used for interfacing to physical user controls.
// Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad.
-namespace HID_User {
-
+namespace Service {
+namespace HID {
+
/**
* HID service interface.
*/
-class Interface : public Service::Interface {
+class HID_U_Interface : public Service::Interface {
public:
- Interface();
+ HID_U_Interface();
std::string GetPortName() const override {
return "hid:USER";
}
};
-} // namespace
+} // namespace HID
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
new file mode 100644
index 000000000..56c918d4f
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -0,0 +1,76 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/service.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ptm_play.h"
+#include "core/hle/service/ptm/ptm_sysm.h"
+#include "core/hle/service/ptm/ptm_u.h"
+
+namespace Service {
+namespace PTM {
+
+/// Values for the default gamecoin.dat file
+static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 };
+
+/// Id of the SharedExtData archive used by the PTM process
+static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0};
+
+static bool shell_open = true;
+
+static bool battery_is_charging = true;
+
+u32 GetAdapterState() {
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+ return battery_is_charging ? 1 : 0;
+}
+
+u32 GetShellState() {
+ return shell_open ? 1 : 0;
+}
+
+ChargeLevels GetBatteryLevel() {
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+ return ChargeLevels::CompletelyFull; // Set to a completely full battery
+}
+
+void PTMInit() {
+ AddService(new PTM_Play_Interface);
+ AddService(new PTM_Sysm_Interface);
+ AddService(new PTM_U_Interface);
+
+ // Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't exist
+ FileSys::Path archive_path(ptm_shared_extdata_id);
+ auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
+ // If the archive didn't exist, create the files inside
+ if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
+ // Format the archive to create the directories
+ Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
+ // Open it again to get a valid archive now that the folder exists
+ archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
+ ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
+
+ FileSys::Path gamecoin_path("gamecoin.dat");
+ FileSys::Mode open_mode = {};
+ open_mode.write_flag = 1;
+ open_mode.create_flag = 1;
+ // Open the file and write the default gamecoin information
+ auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
+ if (gamecoin_result.Succeeded()) {
+ auto gamecoin = gamecoin_result.MoveFrom();
+ gamecoin->backend->Write(0, sizeof(GameCoin), 1, reinterpret_cast<const u8*>(&default_game_coin));
+ gamecoin->backend->Close();
+ }
+ }
+}
+
+void PTMShutdown() {
+
+}
+
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
new file mode 100644
index 000000000..f697aae4d
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.h
@@ -0,0 +1,65 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "core/hle/result.h"
+
+namespace Service {
+namespace PTM {
+
+/// Charge levels used by PTM functions
+enum class ChargeLevels : u32 {
+ CriticalBattery = 1,
+ LowBattery = 2,
+ HalfFull = 3,
+ MostlyFull = 4,
+ CompletelyFull = 5,
+};
+
+/**
+ * Represents the gamecoin file structure in the SharedExtData archive
+ * More information in 3dbrew (http://www.3dbrew.org/wiki/Extdata#Shared_Extdata_0xf000000b_gamecoin.dat)
+ */
+struct GameCoin {
+ u32 magic; ///< Magic number: 0x4F00
+ u16 total_coins; ///< Total Play Coins
+ u16 total_coins_on_date; ///< Total Play Coins obtained on the date stored below.
+ u32 step_count; ///< Total step count at the time a new Play Coin was obtained.
+ u32 last_step_count; ///< Step count for the day the last Play Coin was obtained
+ u16 year;
+ u8 month;
+ u8 day;
+};
+
+/**
+ * Returns whether the battery is charging or not.
+ * It is unknown if GetAdapterState is the same as GetBatteryChargeState,
+ * it is likely to just be a duplicate function of GetBatteryChargeState
+ * that controls another part of the HW.
+ * @returns 1 if the battery is charging, and 0 otherwise.
+ */
+u32 GetAdapterState();
+
+/**
+ * Returns whether the 3DS's physical shell casing is open or closed
+ * @returns 1 if the shell is open, and 0 if otherwise
+ */
+u32 GetShellState();
+
+/**
+ * Get the current battery's charge level.
+ * @returns The battery's charge level.
+ */
+ChargeLevels GetBatteryLevel();
+
+/// Initialize the PTM service
+void PTMInit();
+
+/// Shutdown the PTM service
+void PTMShutdown();
+
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp
index f21d9088e..8e8ae8558 100644
--- a/src/core/hle/service/ptm_play.cpp
+++ b/src/core/hle/service/ptm/ptm_play.cpp
@@ -3,12 +3,10 @@
// Refer to the license.txt file included.
#include "core/hle/hle.h"
-#include "core/hle/service/ptm_play.h"
+#include "core/hle/service/ptm/ptm_play.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace PTM_PLAY
-
-namespace PTM_PLAY {
+namespace Service {
+namespace PTM {
const Interface::FunctionInfo FunctionTable[] = {
{ 0x08070082, nullptr, "GetPlayHistory" },
@@ -17,11 +15,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{ 0x080B0080, nullptr, "CalcPlayHistoryStart" },
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+PTM_Play_Interface::PTM_Play_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace PTM
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/ptm_play.h b/src/core/hle/service/ptm/ptm_play.h
index 2f4f0d6fd..e5c3e04df 100644
--- a/src/core/hle/service/ptm_play.h
+++ b/src/core/hle/service/ptm/ptm_play.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace PTM_PLAY
+namespace Service {
+namespace PTM {
-namespace PTM_PLAY {
-
-class Interface : public Service::Interface {
+class PTM_Play_Interface : public Service::Interface {
public:
- Interface();
+ PTM_Play_Interface();
std::string GetPortName() const override {
return "ptm:play";
}
};
-} // namespace
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp
index 96ef2dce0..2d841f69c 100644
--- a/src/core/hle/service/ptm_sysm.cpp
+++ b/src/core/hle/service/ptm/ptm_sysm.cpp
@@ -5,12 +5,22 @@
#include "common/make_unique.h"
#include "core/file_sys/archive_extsavedata.h"
#include "core/hle/hle.h"
-#include "core/hle/service/ptm_sysm.h"
+#include "core/hle/service/ptm/ptm_sysm.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace PTM_SYSM
+namespace Service {
+namespace PTM {
-namespace PTM_SYSM {
+/**
+ * Returns whether the system is powering off (?)
+ * Outputs:
+ * 1: Result code, 0 on success, otherwise error code
+ * 2: Whether the system is going through a power off
+ */
+void IsLegacyPowerOff(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+}
const Interface::FunctionInfo FunctionTable[] = {
{0x040100C0, nullptr, "SetRtcAlarmEx"},
@@ -37,7 +47,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x080C0080, nullptr, "SetUserTime"},
{0x080D0000, nullptr, "InvalidateSystemTime"},
{0x080E0140, nullptr, "NotifyPlayEvent"},
- {0x080F0000, nullptr, "IsLegacyPowerOff"},
+ {0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"},
{0x08100000, nullptr, "ClearLegacyPowerOff"},
{0x08110000, nullptr, "GetShellStatus"},
{0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
@@ -45,11 +55,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}
};
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
+PTM_Sysm_Interface::PTM_Sysm_Interface() {
Register(FunctionTable);
}
-} // namespace
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm_sysm.h b/src/core/hle/service/ptm/ptm_sysm.h
index 0f267b214..e37f20546 100644
--- a/src/core/hle/service/ptm_sysm.h
+++ b/src/core/hle/service/ptm/ptm_sysm.h
@@ -6,18 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace PTM_SYSM
+namespace Service {
+namespace PTM {
-namespace PTM_SYSM {
-
-class Interface : public Service::Interface {
+class PTM_Sysm_Interface : public Interface {
public:
- Interface();
+ PTM_Sysm_Interface();
std::string GetPortName() const override {
return "ptm:sysm";
}
};
-} // namespace
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp
new file mode 100644
index 000000000..0af7c8bf6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm_u.cpp
@@ -0,0 +1,99 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/make_unique.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ptm_u.h"
+
+namespace Service {
+namespace PTM {
+
+/**
+ * PTM_U::GetAdapterState service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, 0 = not charging, 1 = charging.
+ */
+static void GetAdapterState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = GetAdapterState();
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
+/*
+ * PTM_User::GetShellState service function.
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0)
+ */
+static void GetShellState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = GetShellState();
+}
+
+/**
+ * PTM_U::GetBatteryLevel service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery,
+ * 3 = half full battery, 2 = low battery, 1 = critical battery.
+ */
+static void GetBatteryLevel(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = static_cast<u32>(GetBatteryLevel());
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
+/**
+ * PTM_U::GetBatteryChargeState service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, 0 = not charging, 1 = charging.
+ */
+static void GetBatteryChargeState(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(purpasmart96): This function is only a stub,
+ // it returns a valid result without implementing full functionality.
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = GetAdapterState();
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010002, nullptr, "RegisterAlarmClient"},
+ {0x00020080, nullptr, "SetRtcAlarm"},
+ {0x00030000, nullptr, "GetRtcAlarm"},
+ {0x00040000, nullptr, "CancelRtcAlarm"},
+ {0x00050000, GetAdapterState, "GetAdapterState"},
+ {0x00060000, GetShellState, "GetShellState"},
+ {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
+ {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
+ {0x00090000, nullptr, "GetPedometerState"},
+ {0x000A0042, nullptr, "GetStepHistoryEntry"},
+ {0x000B00C2, nullptr, "GetStepHistory"},
+ {0x000C0000, nullptr, "GetTotalStepCount"},
+ {0x000D0040, nullptr, "SetPedometerRecordingMode"},
+ {0x000E0000, nullptr, "GetPedometerRecordingMode"},
+ {0x000F0084, nullptr, "GetStepHistoryAll"},
+};
+
+PTM_U_Interface::PTM_U_Interface() {
+ Register(FunctionTable);
+}
+
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm/ptm_u.h
index a44624fd5..bf132f610 100644
--- a/src/core/hle/service/ptm_u.h
+++ b/src/core/hle/service/ptm/ptm_u.h
@@ -6,20 +6,17 @@
#include "core/hle/service/service.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace PTM_U
+namespace Service {
+namespace PTM {
-// ptm service
-
-namespace PTM_U {
-
-class Interface : public Service::Interface {
+class PTM_U_Interface : public Interface {
public:
- Interface();
+ PTM_U_Interface();
std::string GetPortName() const override {
return "ptm:u";
}
};
-} // namespace
+} // namespace PTM
+} // namespace Service \ No newline at end of file
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
deleted file mode 100644
index 7121d837c..000000000
--- a/src/core/hle/service/ptm_u.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/make_unique.h"
-
-#include "core/hle/hle.h"
-#include "core/hle/service/fs/archive.h"
-#include "core/hle/service/ptm_u.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace PTM_U
-
-namespace PTM_U {
-
-/**
- * Represents the gamecoin file structure in the SharedExtData archive
- * More information in 3dbrew (http://www.3dbrew.org/wiki/Extdata#Shared_Extdata_0xf000000b_gamecoin.dat)
- */
-struct GameCoin {
- u32 magic; ///< Magic number: 0x4F00
- u16 total_coins; ///< Total Play Coins
- u16 total_coins_on_date; ///< Total Play Coins obtained on the date stored below.
- u32 step_count; ///< Total step count at the time a new Play Coin was obtained.
- u32 last_step_count; ///< Step count for the day the last Play Coin was obtained
- u16 year;
- u8 month;
- u8 day;
-};
-static const GameCoin default_game_coin = { 0x4F00, 42, 0, 0, 0, 2014, 12, 29 };
-static const std::vector<u8> ptm_shared_extdata_id = {0, 0, 0, 0, 0x0B, 0, 0, 0xF0, 0, 0, 0, 0};
-
-/// Charge levels used by PTM functions
-enum class ChargeLevels : u32 {
- CriticalBattery = 1,
- LowBattery = 2,
- HalfFull = 3,
- MostlyFull = 4,
- CompletelyFull = 5,
-};
-
-static bool shell_open = true;
-
-static bool battery_is_charging = true;
-
-/**
- * It is unknown if GetAdapterState is the same as GetBatteryChargeState,
- * it is likely to just be a duplicate function of GetBatteryChargeState
- * that controls another part of the HW.
- * PTM_U::GetAdapterState service function
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Output of function, 0 = not charging, 1 = charging.
- */
-static void GetAdapterState(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- // TODO(purpasmart96): This function is only a stub,
- // it returns a valid result without implementing full functionality.
-
- cmd_buff[1] = 0; // No error
- cmd_buff[2] = battery_is_charging ? 1 : 0;
-
- LOG_WARNING(Service_PTM, "(STUBBED) called");
-}
-
-/*
- * PTM_User::GetShellState service function.
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0)
- */
-static void GetShellState(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- cmd_buff[1] = 0;
- cmd_buff[2] = shell_open ? 1 : 0;
-}
-
-/**
- * PTM_U::GetBatteryLevel service function
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery,
- * 3 = half full battery, 2 = low battery, 1 = critical battery.
- */
-static void GetBatteryLevel(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- // TODO(purpasmart96): This function is only a stub,
- // it returns a valid result without implementing full functionality.
-
- cmd_buff[1] = 0; // No error
- cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery
-
- LOG_WARNING(Service_PTM, "(STUBBED) called");
-}
-
-/**
- * PTM_U::GetBatteryChargeState service function
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- * 2 : Output of function, 0 = not charging, 1 = charging.
- */
-static void GetBatteryChargeState(Service::Interface* self) {
- u32* cmd_buff = Kernel::GetCommandBuffer();
-
- // TODO(purpasmart96): This function is only a stub,
- // it returns a valid result without implementing full functionality.
-
- cmd_buff[1] = 0; // No error
- cmd_buff[2] = battery_is_charging ? 1 : 0;
-
- LOG_WARNING(Service_PTM, "(STUBBED) called");
-}
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x00010002, nullptr, "RegisterAlarmClient"},
- {0x00020080, nullptr, "SetRtcAlarm"},
- {0x00030000, nullptr, "GetRtcAlarm"},
- {0x00040000, nullptr, "CancelRtcAlarm"},
- {0x00050000, GetAdapterState, "GetAdapterState"},
- {0x00060000, GetShellState, "GetShellState"},
- {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
- {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
- {0x00090000, nullptr, "GetPedometerState"},
- {0x000A0042, nullptr, "GetStepHistoryEntry"},
- {0x000B00C2, nullptr, "GetStepHistory"},
- {0x000C0000, nullptr, "GetTotalStepCount"},
- {0x000D0040, nullptr, "SetPedometerRecordingMode"},
- {0x000E0000, nullptr, "GetPedometerRecordingMode"},
- {0x000F0084, nullptr, "GetStepHistoryAll"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- Register(FunctionTable);
-
- // Open the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file
- FileSys::Path archive_path(ptm_shared_extdata_id);
- auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
- // If the archive didn't exist, create the files inside
- if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
- // Format the archive to create the directories
- Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
- // Open it again to get a valid archive now that the folder exists
- archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
- ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
-
- FileSys::Path gamecoin_path("gamecoin.dat");
- FileSys::Mode open_mode = {};
- open_mode.write_flag = 1;
- open_mode.create_flag = 1;
- // Open the file and write the default gamecoin information
- auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
- if (gamecoin_result.Succeeded()) {
- auto gamecoin = gamecoin_result.MoveFrom();
- gamecoin->backend->Write(0, sizeof(GameCoin), 1, reinterpret_cast<const u8*>(&default_game_coin));
- gamecoin->backend->Close();
- }
- }
-}
-
-} // namespace
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 5dce8068e..91f13cd7e 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,26 +11,17 @@
#include "core/hle/service/am_app.h"
#include "core/hle/service/am_net.h"
#include "core/hle/service/am_sys.h"
-#include "core/hle/service/apt_a.h"
-#include "core/hle/service/apt_s.h"
-#include "core/hle/service/apt_u.h"
#include "core/hle/service/boss_p.h"
#include "core/hle/service/boss_u.h"
#include "core/hle/service/cam_u.h"
#include "core/hle/service/cecd_u.h"
#include "core/hle/service/cecd_s.h"
-#include "core/hle/service/cfg/cfg_i.h"
-#include "core/hle/service/cfg/cfg_s.h"
-#include "core/hle/service/cfg/cfg_u.h"
#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/err_f.h"
-#include "core/hle/service/fs/fs_user.h"
#include "core/hle/service/frd_a.h"
#include "core/hle/service/frd_u.h"
#include "core/hle/service/gsp_gpu.h"
-#include "core/hle/service/hid/hid_spvr.h"
-#include "core/hle/service/hid/hid_user.h"
#include "core/hle/service/gsp_lcd.h"
#include "core/hle/service/http_c.h"
#include "core/hle/service/ir_rst.h"
@@ -44,14 +35,17 @@
#include "core/hle/service/ns_s.h"
#include "core/hle/service/nwm_uds.h"
#include "core/hle/service/pm_app.h"
-#include "core/hle/service/ptm_play.h"
-#include "core/hle/service/ptm_u.h"
-#include "core/hle/service/ptm_sysm.h"
#include "core/hle/service/soc_u.h"
#include "core/hle/service/srv.h"
#include "core/hle/service/ssl_c.h"
#include "core/hle/service/y2r_u.h"
+#include "core/hle/service/apt/apt.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/cfg/cfg.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/ptm/ptm.h"
+
namespace Service {
std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports;
@@ -60,12 +54,12 @@ std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Module interface
-static void AddNamedPort(Interface* interface) {
- g_kernel_named_ports.emplace(interface->GetPortName(), interface);
+static void AddNamedPort(Interface* interface_) {
+ g_kernel_named_ports.emplace(interface_->GetPortName(), interface_);
}
-static void AddService(Interface* interface) {
- g_srv_services.emplace(interface->GetPortName(), interface);
+void AddService(Interface* interface_) {
+ g_srv_services.emplace(interface_->GetPortName(), interface_);
}
/// Initialize ServiceManager
@@ -73,31 +67,28 @@ void Init() {
AddNamedPort(new SRV::Interface);
AddNamedPort(new ERR_F::Interface);
+ Service::FS::ArchiveInit();
+ Service::CFG::CFGInit();
+ Service::APT::APTInit();
+ Service::PTM::PTMInit();
+ Service::HID::HIDInit();
+
AddService(new AC_U::Interface);
AddService(new ACT_U::Interface);
AddService(new AM_APP::Interface);
AddService(new AM_NET::Interface);
AddService(new AM_SYS::Interface);
- AddService(new APT_A::Interface);
- AddService(new APT_S::Interface);
- AddService(new APT_U::Interface);
AddService(new BOSS_P::Interface);
AddService(new BOSS_U::Interface);
AddService(new CAM_U::Interface);
AddService(new CECD_S::Interface);
AddService(new CECD_U::Interface);
- AddService(new CFG_I::Interface);
- AddService(new CFG_S::Interface);
- AddService(new CFG_U::Interface);
AddService(new CSND_SND::Interface);
AddService(new DSP_DSP::Interface);
AddService(new FRD_A::Interface);
AddService(new FRD_U::Interface);
- AddService(new FS::FSUserInterface);
AddService(new GSP_GPU::Interface);
AddService(new GSP_LCD::Interface);
- AddService(new HID_User::Interface);
- AddService(new HID_SPVR::Interface);
AddService(new HTTP_C::Interface);
AddService(new IR_RST::Interface);
AddService(new IR_U::Interface);
@@ -110,9 +101,6 @@ void Init() {
AddService(new NS_S::Interface);
AddService(new NWM_UDS::Interface);
AddService(new PM_APP::Interface);
- AddService(new PTM_PLAY::Interface);
- AddService(new PTM_U::Interface);
- AddService(new PTM_SYSM::Interface);
AddService(new SOC_U::Interface);
AddService(new SSL_C::Interface);
AddService(new Y2R_U::Interface);
@@ -122,6 +110,12 @@ void Init() {
/// Shutdown ServiceManager
void Shutdown() {
+ Service::HID::HIDShutdown();
+ Service::PTM::PTMShutdown();
+ Service::APT::APTShutdown();
+ Service::CFG::CFGShutdown();
+ Service::FS::ArchiveShutdown();
+
g_srv_services.clear();
g_kernel_named_ports.clear();
LOG_DEBUG(Service, "shutdown OK");
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 3370f9f9b..bfe16ebad 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -120,4 +120,7 @@ extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_na
/// Map of services registered with the "srv:" service, retrieved using GetServiceHandle.
extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services;
+/// Adds a service to the services table
+void AddService(Interface* interface_);
+
} // namespace
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index a58e04d6d..6607965e1 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -11,6 +11,8 @@
namespace Y2R_U {
+static Kernel::SharedPtr<Kernel::Event> completion_event = 0;
+
/**
* Y2R_U::IsBusyConversion service function
* Outputs:
@@ -26,13 +28,26 @@ static void IsBusyConversion(Service::Interface* self) {
LOG_WARNING(Service, "(STUBBED) called");
}
+/**
+ * Y2R_U::GetTransferEndEvent service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : The handle of the completion event
+ */
+static void GetTransferEndEvent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom();
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, nullptr, "SetInputFormat"},
{0x00030040, nullptr, "SetOutputFormat"},
{0x00050040, nullptr, "SetRotation"},
{0x00070040, nullptr, "SetBlockAlignment"},
{0x000D0040, nullptr, "SetTransferEndInterrupt"},
- {0x000F0000, nullptr, "GetTransferEndEvent"},
+ {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"},
{0x00100102, nullptr, "SetSendingY"},
{0x00110102, nullptr, "SetSendingU"},
{0x00120102, nullptr, "SetSendingV"},
@@ -53,6 +68,8 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
+ completion_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "Y2R:Completed");
+
Register(FunctionTable);
}
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index bd7d92cd1..424ce2ca7 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -18,8 +18,9 @@
#include "core/hw/gpu.h"
#include "video_core/command_processor.h"
+#include "video_core/utils.h"
#include "video_core/video_core.h"
-
+#include <video_core/color.h>
namespace GPU {
@@ -112,61 +113,109 @@ inline void Write(u32 addr, const T data) {
{
const auto& config = g_regs.display_transfer_config;
if (config.trigger & 1) {
- u8* source_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress()));
- u8* dest_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress()));
-
- // Cheap emulation of horizontal scaling: Just skip each second pixel of the
- // input framebuffer. We keep track of this in the pixel_skip variable.
- unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1;
-
- u32 output_width = config.output_width / pixel_skip;
-
- for (u32 y = 0; y < config.output_height; ++y) {
- // TODO: Why does the register seem to hold twice the framebuffer width?
+ u8* src_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress()));
+ u8* dst_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress()));
+
+ unsigned horizontal_scale = (config.scale_horizontally != 0) ? 2 : 1;
+ unsigned vertical_scale = (config.scale_vertically != 0) ? 2 : 1;
+
+ u32 output_width = config.output_width / horizontal_scale;
+ u32 output_height = config.output_height / vertical_scale;
+
+ if (config.raw_copy) {
+ // Raw copies do not perform color conversion nor tiled->linear / linear->tiled conversions
+ // TODO(Subv): Verify if raw copies perform scaling
+ memcpy(dst_pointer, src_pointer, config.output_width * config.output_height *
+ GPU::Regs::BytesPerPixel(config.output_format));
+
+ LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), flags 0x%08X, Raw copy",
+ config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format),
+ config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(),
+ config.GetPhysicalOutputAddress(), config.output_width.Value(), config.output_height.Value(),
+ config.output_format.Value(), config.flags);
+
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
+ break;
+ }
+ // TODO(Subv): Blend the pixels when horizontal / vertical scaling is enabled,
+ // right now we're just skipping the extra pixels.
+ for (u32 y = 0; y < output_height; ++y) {
for (u32 x = 0; x < output_width; ++x) {
- struct {
- int r, g, b, a;
- } source_color = { 0, 0, 0, 0 };
+ Math::Vec4<u8> src_color = { 0, 0, 0, 0 };
+
+ u32 scaled_x = x * horizontal_scale;
+ u32 scaled_y = y * vertical_scale;
+
+ u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format);
+ u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format);
+ u32 src_offset;
+ u32 dst_offset;
+
+ if (config.output_tiled) {
+ // Interpret the input as linear and the output as tiled
+ u32 coarse_y = y & ~7;
+ u32 stride = output_width * dst_bytes_per_pixel;
+
+ src_offset = (scaled_x + scaled_y * config.input_width) * src_bytes_per_pixel;
+ dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride;
+ } else {
+ // Interpret the input as tiled and the output as linear
+ u32 coarse_y = scaled_y & ~7;
+ u32 stride = config.input_width * src_bytes_per_pixel;
+
+ src_offset = VideoCore::GetMortonOffset(scaled_x, scaled_y, src_bytes_per_pixel) + coarse_y * stride;
+ dst_offset = (x + y * output_width) * dst_bytes_per_pixel;
+ }
+ const u8* src_pixel = src_pointer + src_offset;
switch (config.input_format) {
case Regs::PixelFormat::RGBA8:
- {
- // TODO: Most likely got the component order messed up.
- u8* srcptr = source_pointer + (x * pixel_skip + y * config.input_width) * 4;
- source_color.r = srcptr[0]; // blue
- source_color.g = srcptr[1]; // green
- source_color.b = srcptr[2]; // red
- source_color.a = srcptr[3]; // alpha
+ src_color = Color::DecodeRGBA8(src_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB8:
+ src_color = Color::DecodeRGB8(src_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB565:
+ src_color = Color::DecodeRGB565(src_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB5A1:
+ src_color = Color::DecodeRGB5A1(src_pixel);
+ break;
+
+ case Regs::PixelFormat::RGBA4:
+ src_color = Color::DecodeRGBA4(src_pixel);
break;
- }
default:
LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value());
break;
}
+ u8* dst_pixel = dst_pointer + dst_offset;
switch (config.output_format) {
- /*case Regs::PixelFormat::RGBA8:
- {
- // TODO: Untested
- u8* dstptr = (u32*)(dest_pointer + x * 4 + y * config.output_width * 4);
- dstptr[0] = source_color.r;
- dstptr[1] = source_color.g;
- dstptr[2] = source_color.b;
- dstptr[3] = source_color.a;
+ case Regs::PixelFormat::RGBA8:
+ Color::EncodeRGBA8(src_color, dst_pixel);
break;
- }*/
case Regs::PixelFormat::RGB8:
- {
- // TODO: Most likely got the component order messed up.
- u8* dstptr = dest_pointer + (x + y * output_width) * 3;
- dstptr[0] = source_color.r; // blue
- dstptr[1] = source_color.g; // green
- dstptr[2] = source_color.b; // red
+ Color::EncodeRGB8(src_color, dst_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB565:
+ Color::EncodeRGB565(src_color, dst_pixel);
+ break;
+
+ case Regs::PixelFormat::RGB5A1:
+ Color::EncodeRGB5A1(src_color, dst_pixel);
+ break;
+
+ case Regs::PixelFormat::RGBA4:
+ Color::EncodeRGBA4(src_color, dst_pixel);
break;
- }
default:
LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
@@ -175,11 +224,11 @@ inline void Write(u32 addr, const T data) {
}
}
- LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
- config.output_height * output_width * 4,
- config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
- config.GetPhysicalOutputAddress(), (u32)output_width, (u32)config.output_height,
- config.output_format.Value());
+ LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X",
+ config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format),
+ config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(),
+ config.GetPhysicalOutputAddress(), output_width, output_height,
+ config.output_format.Value(), config.flags);
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
}
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index df9aa0d71..737b1e968 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -34,13 +34,6 @@ namespace GPU {
// MMIO region 0x1EFxxxxx
struct Regs {
-// helper macro to properly align structure members.
-// Calling INSERT_PADDING_WORDS will add a new member variable with a name like "pad121",
-// depending on the current source line to make sure variable names are unique.
-#define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y
-#define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y)
-#define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]
-
// helper macro to make sure the defined structures are of the expected size.
#if defined(_MSC_VER)
// TODO: MSVC does not support using sizeof() on non-static data members even though this
@@ -53,7 +46,7 @@ struct Regs {
"Structure size and register block length don't match")
#endif
- // All of those formats are described in reverse byte order, since the 3DS is little-endian.
+ // Components are laid out in reverse byte order, most significant bits first.
enum class PixelFormat : u32 {
RGBA8 = 0,
RGB8 = 1,
@@ -199,12 +192,13 @@ struct Regs {
u32 flags;
BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true
+ BitField< 1, 1, u32> output_tiled; // Converts from linear to tiled format
+ BitField< 3, 1, u32> raw_copy; // Copies the data without performing any processing
BitField< 8, 3, PixelFormat> input_format;
BitField<12, 3, PixelFormat> output_format;
- BitField<16, 1, u32> output_tiled; // stores output in a tiled format
- // TODO: Not really sure if this actually scales, or even resizes at all.
BitField<24, 1, u32> scale_horizontally;
+ BitField<25, 1, u32> scale_vertically;
};
INSERT_PADDING_WORDS(0x1);
@@ -238,10 +232,6 @@ struct Regs {
INSERT_PADDING_WORDS(0x9c3);
-#undef INSERT_PADDING_WORDS_HELPER1
-#undef INSERT_PADDING_WORDS_HELPER2
-#undef INSERT_PADDING_WORDS
-
static inline size_t NumIds() {
return sizeof(Regs) / sizeof(u32);
}
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 4f93c0e64..48f61db4e 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -236,30 +236,12 @@ u8 Read8(const VAddr addr) {
u16 Read16(const VAddr addr) {
u16_le data = 0;
Read<u16_le>(data, addr);
-
- // Check for 16-bit unaligned memory reads...
- if (addr & 1) {
- // TODO(bunnei): Implement 16-bit unaligned memory reads
- LOG_ERROR(HW_Memory, "16-bit unaligned memory reads are not implemented!");
- }
-
return (u16)data;
}
u32 Read32(const VAddr addr) {
u32_le data = 0;
Read<u32_le>(data, addr);
-
- // Check for 32-bit unaligned memory reads...
- if (addr & 3) {
- // ARM allows for unaligned memory reads, however older ARM architectures read out memory
- // from unaligned addresses in a shifted way. Our ARM CPU core (SkyEye) corrects for this,
- // so therefore expects the memory to be read out in this manner.
- // TODO(bunnei): Determine if this is necessary - perhaps it is OK to remove this from both
- // SkyEye and here?
- int shift = (addr & 3) * 8;
- data = (data << shift) | (data >> (32 - shift));
- }
return (u32)data;
}
diff --git a/src/core/settings.h b/src/core/settings.h
index e62dd4358..870eea958 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -16,6 +16,8 @@ struct Values {
int pad_y_key;
int pad_l_key;
int pad_r_key;
+ int pad_zl_key;
+ int pad_zr_key;
int pad_start_key;
int pad_select_key;
int pad_home_key;
@@ -27,6 +29,10 @@ struct Values {
int pad_sdown_key;
int pad_sleft_key;
int pad_sright_key;
+ int pad_cup_key;
+ int pad_cdown_key;
+ int pad_cleft_key;
+ int pad_cright_key;
// Core
int gpu_refresh_rate;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 13c3f7b22..4c1e6449a 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -19,6 +19,7 @@ set(HEADERS
renderer_opengl/gl_shaders.h
renderer_opengl/renderer_opengl.h
clipper.h
+ color.h
command_processor.h
gpu_debugger.h
math.h
diff --git a/src/video_core/color.h b/src/video_core/color.h
index e86ac1265..35da901f2 100644
--- a/src/video_core/color.h
+++ b/src/video_core/color.h
@@ -5,28 +5,152 @@
#pragma once
#include "common/common_types.h"
+#include "video_core/math.h"
namespace Color {
/// Convert a 1-bit color component to 8 bit
-static inline u8 Convert1To8(u8 value) {
+inline u8 Convert1To8(u8 value) {
return value * 255;
}
/// Convert a 4-bit color component to 8 bit
-static inline u8 Convert4To8(u8 value) {
+inline u8 Convert4To8(u8 value) {
return (value << 4) | value;
}
/// Convert a 5-bit color component to 8 bit
-static inline u8 Convert5To8(u8 value) {
+inline u8 Convert5To8(u8 value) {
return (value << 3) | (value >> 2);
}
/// Convert a 6-bit color component to 8 bit
-static inline u8 Convert6To8(u8 value) {
+inline u8 Convert6To8(u8 value) {
return (value << 2) | (value >> 4);
}
+/// Convert a 8-bit color component to 1 bit
+inline u8 Convert8To1(u8 value) {
+ return value >> 7;
+}
+
+/// Convert a 8-bit color component to 4 bit
+inline u8 Convert8To4(u8 value) {
+ return value >> 4;
+}
+
+/// Convert a 8-bit color component to 5 bit
+inline u8 Convert8To5(u8 value) {
+ return value >> 3;
+}
+
+/// Convert a 8-bit color component to 6 bit
+inline u8 Convert8To6(u8 value) {
+ return value >> 2;
+}
+
+/**
+ * Decode a color stored in RGBA8 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
+ return { bytes[3], bytes[2], bytes[1], bytes[0] };
+}
+
+/**
+ * Decode a color stored in RGB8 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
+ return { bytes[2], bytes[1], bytes[0], 255 };
+}
+
+/**
+ * Decode a color stored in RGB565 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
+ const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+ return { Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
+ Convert5To8(pixel & 0x1F), 255 };
+}
+
+/**
+ * Decode a color stored in RGB5A1 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
+ const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+ return { Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
+ Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1) };
+}
+
+/**
+ * Decode a color stored in RGBA4 format
+ * @param bytes Pointer to encoded source color
+ * @return Result color decoded as Math::Vec4<u8>
+ */
+inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
+ const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
+ return { Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
+ Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF) };
+}
+
+/**
+ * Encode a color as RGBA8 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGBA8(const Math::Vec4<u8>& color, u8* bytes) {
+ bytes[3] = color.r();
+ bytes[2] = color.g();
+ bytes[1] = color.b();
+ bytes[0] = color.a();
+}
+
+/**
+ * Encode a color as RGB8 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGB8(const Math::Vec4<u8>& color, u8* bytes) {
+ bytes[2] = color.r();
+ bytes[1] = color.g();
+ bytes[0] = color.b();
+}
+
+/**
+ * Encode a color as RGB565 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
+ *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
+ (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
+}
+
+/**
+ * Encode a color as RGB5A1 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
+ *reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
+ (Convert8To5(color.g()) << 6) | (Convert8To5(color.b()) << 1) | Convert8To1(color.a());
+}
+
+/**
+ * Encode a color as RGBA4 format
+ * @param color Source color to encode
+ * @param bytes Destination pointer to store encoded color
+ */
+inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
+ *reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
+ (Convert8To4(color.g()) << 8) | (Convert8To4(color.b()) << 4) | Convert8To4(color.a());
+}
} // namespace
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 586ad62b6..e031871e8 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -4,6 +4,8 @@
#include <boost/range/algorithm/fill.hpp>
+#include "common/profiler.h"
+
#include "clipper.h"
#include "command_processor.h"
#include "math.h"
@@ -25,6 +27,8 @@ static int float_regs_counter = 0;
static u32 uniform_write_buffer[4];
+Common::Profiling::TimingCategory category_drawing("Drawing");
+
static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
if (id >= registers.NumIds())
@@ -53,6 +57,8 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX(trigger_draw):
case PICA_REG_INDEX(trigger_draw_indexed):
{
+ Common::Profiling::ScopeTimer scope_timer(category_drawing);
+
DebugUtils::DumpTevStageConfig(registers.GetTevStages());
if (g_debug_context)
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 0beb72e6b..745c4f4ed 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -23,6 +23,7 @@
#include "video_core/color.h"
#include "video_core/math.h"
#include "video_core/pica.h"
+#include "video_core/utils.h"
#include "debug_utils.h"
@@ -306,111 +307,69 @@ std::unique_ptr<PicaTrace> FinishPicaTracing()
}
const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info, bool disable_alpha) {
- // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each
- // of which is composed of four 2x2 subtiles each of which is composed of four texels.
- // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g.
- // texels are laid out in a 2x2 subtile like this:
- // 2 3
- // 0 1
- //
- // The full 8x8 tile has the texels arranged like this:
- //
- // 42 43 46 47 58 59 62 63
- // 40 41 44 45 56 57 60 61
- // 34 35 38 39 50 51 54 55
- // 32 33 36 37 48 49 52 53
- // 10 11 14 15 26 27 30 31
- // 08 09 12 13 24 25 28 29
- // 02 03 06 07 18 19 22 23
- // 00 01 04 05 16 17 20 21
-
- const unsigned int block_width = 8;
- const unsigned int block_height = 8;
-
const unsigned int coarse_x = x & ~7;
const unsigned int coarse_y = y & ~7;
- // Interleave the lower 3 bits of each coordinate to get the intra-block offsets, which are
- // arranged in a Z-order curve. More details on the bit manipulation at:
- // https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
- unsigned int i = (x | (y << 8)) & 0x0707; // ---- -210
- i = (i ^ (i << 2)) & 0x1313; // ---2 --10
- i = (i ^ (i << 1)) & 0x1515; // ---2 -1-0
- i = (i | (i >> 7)) & 0x3F;
-
if (info.format != Regs::TextureFormat::ETC1 &&
info.format != Regs::TextureFormat::ETC1A4) {
// TODO(neobrain): Fix code design to unify vertical block offsets!
source += coarse_y * info.stride;
}
- const unsigned int offset = coarse_x * block_height;
-
+
// TODO: Assert that width/height are multiples of block dimensions
switch (info.format) {
case Regs::TextureFormat::RGBA8:
{
- const u8* source_ptr = source + offset * 4 + i * 4;
- return { source_ptr[3], source_ptr[2], source_ptr[1], disable_alpha ? (u8)255 : source_ptr[0] };
+ auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4));
+ return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() };
}
case Regs::TextureFormat::RGB8:
{
- const u8* source_ptr = source + offset * 3 + i * 3;
- return { source_ptr[2], source_ptr[1], source_ptr[0], 255 };
+ auto res = Color::DecodeRGB8(source + VideoCore::GetMortonOffset(x, y, 3));
+ return { res.r(), res.g(), res.b(), 255 };
}
- case Regs::TextureFormat::RGBA5551:
+ case Regs::TextureFormat::RGB5A1:
{
- const u16 source_ptr = *(const u16*)(source + offset * 2 + i * 2);
- u8 r = (source_ptr >> 11) & 0x1F;
- u8 g = ((source_ptr) >> 6) & 0x1F;
- u8 b = (source_ptr >> 1) & 0x1F;
- u8 a = source_ptr & 1;
- return Math::MakeVec<u8>(Color::Convert5To8(r), Color::Convert5To8(g),
- Color::Convert5To8(b), disable_alpha ? 255 : Color::Convert1To8(a));
+ auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2));
+ return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() };
}
case Regs::TextureFormat::RGB565:
{
- const u16 source_ptr = *(const u16*)(source + offset * 2 + i * 2);
- u8 r = Color::Convert5To8((source_ptr >> 11) & 0x1F);
- u8 g = Color::Convert6To8(((source_ptr) >> 5) & 0x3F);
- u8 b = Color::Convert5To8((source_ptr) & 0x1F);
- return Math::MakeVec<u8>(r, g, b, 255);
+ auto res = Color::DecodeRGB565(source + VideoCore::GetMortonOffset(x, y, 2));
+ return { res.r(), res.g(), res.b(), 255 };
}
case Regs::TextureFormat::RGBA4:
{
- const u8* source_ptr = source + offset * 2 + i * 2;
- u8 r = Color::Convert4To8(source_ptr[1] >> 4);
- u8 g = Color::Convert4To8(source_ptr[1] & 0xF);
- u8 b = Color::Convert4To8(source_ptr[0] >> 4);
- u8 a = Color::Convert4To8(source_ptr[0] & 0xF);
- return { r, g, b, disable_alpha ? (u8)255 : a };
+ auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2));
+ return { res.r(), res.g(), res.b(), disable_alpha ? 255 : res.a() };
}
case Regs::TextureFormat::IA8:
{
- const u8* source_ptr = source + offset * 2 + i * 2;
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 2);
if (disable_alpha) {
// Show intensity as red, alpha as green
return { source_ptr[1], source_ptr[0], 0, 255 };
} else {
- return { source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0]};
+ return { source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0] };
}
}
case Regs::TextureFormat::I8:
{
- const u8* source_ptr = source + offset + i;
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
return { *source_ptr, *source_ptr, *source_ptr, 255 };
}
case Regs::TextureFormat::A8:
{
- const u8* source_ptr = source + offset + i;
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
if (disable_alpha) {
return { *source_ptr, *source_ptr, *source_ptr, 255 };
@@ -421,7 +380,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::IA4:
{
- const u8* source_ptr = source + offset + i;
+ const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4);
u8 a = Color::Convert4To8((*source_ptr) & 0xF);
@@ -436,9 +395,10 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::A4:
{
- const u8* source_ptr = source + offset / 2 + i / 2;
+ u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1);
+ const u8* source_ptr = source + morton_offset / 2;
- u8 a = (coarse_x % 2) ? ((*source_ptr)&0xF) : (((*source_ptr) & 0xF0) >> 4);
+ u8 a = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
a = Color::Convert4To8(a);
if (disable_alpha) {
@@ -545,7 +505,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
}
// Add modifier
- unsigned table_index = (x < 2) ? table_index_2.Value() : table_index_1.Value();
+ unsigned table_index = (x < 2) ? table_index_1.Value() : table_index_2.Value();
static const auto etc1_modifier_table = std::array<std::array<u8, 2>, 8>{{
{ 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 },
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index e4a5ef78e..b14de9278 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -39,13 +39,6 @@ namespace Pica {
struct Regs {
-// helper macro to properly align structure members.
-// Calling INSERT_PADDING_WORDS will add a new member variable with a name like "pad121",
-// depending on the current source line to make sure variable names are unique.
-#define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y
-#define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y)
-#define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)];
-
INSERT_PADDING_WORDS(0x10);
u32 trigger_irq;
@@ -152,7 +145,7 @@ struct Regs {
enum class TextureFormat : u32 {
RGBA8 = 0,
RGB8 = 1,
- RGBA5551 = 2,
+ RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
IA8 = 5,
@@ -174,7 +167,7 @@ struct Regs {
case TextureFormat::RGB8:
return 6;
- case TextureFormat::RGBA5551:
+ case TextureFormat::RGB5A1:
case TextureFormat::RGB565:
case TextureFormat::RGBA4:
case TextureFormat::IA8:
@@ -416,10 +409,11 @@ struct Regs {
} output_merger;
struct {
+ // Components are laid out in reverse byte order, most significant bits first.
enum ColorFormat : u32 {
RGBA8 = 0,
RGB8 = 1,
- RGBA5551 = 2,
+ RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
};
@@ -709,10 +703,6 @@ struct Regs {
INSERT_PADDING_WORDS(0x22);
-#undef INSERT_PADDING_WORDS_HELPER1
-#undef INSERT_PADDING_WORDS_HELPER2
-#undef INSERT_PADDING_WORDS
-
// Map register indices to names readable by humans
// Used for debugging purposes, so performance is not an issue here
static std::string GetCommandName(int index) {
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index 81df09baf..5861c1926 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -7,12 +7,14 @@
#include "common/common_types.h"
#include "common/math_util.h"
+#include "core/hw/gpu.h"
+#include "debug_utils/debug_utils.h"
#include "math.h"
+#include "color.h"
#include "pica.h"
#include "rasterizer.h"
#include "vertex_shader.h"
-
-#include "debug_utils/debug_utils.h"
+#include "video_core/utils.h"
namespace Pica {
@@ -20,59 +22,101 @@ namespace Rasterizer {
static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
const PAddr addr = registers.framebuffer.GetColorBufferPhysicalAddress();
- u32* color_buffer = reinterpret_cast<u32*>(Memory::GetPointer(PAddrToVAddr(addr)));
// Similarly to textures, the render framebuffer is laid out from bottom to top, too.
// NOTE: The framebuffer height register contains the actual FB height minus one.
y = (registers.framebuffer.height - y);
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(registers.framebuffer.color_format.Value()));
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * registers.framebuffer.width * bytes_per_pixel;
+ u8* dst_pixel = Memory::GetPointer(PAddrToVAddr(addr)) + dst_offset;
+
switch (registers.framebuffer.color_format) {
case registers.framebuffer.RGBA8:
- {
- u32 value = (color.a() << 24) | (color.r() << 16) | (color.g() << 8) | color.b();
- *(color_buffer + x + y * registers.framebuffer.GetWidth()) = value;
+ Color::EncodeRGBA8(color, dst_pixel);
+ break;
+
+ case registers.framebuffer.RGB8:
+ Color::EncodeRGB8(color, dst_pixel);
+ break;
+
+ case registers.framebuffer.RGB5A1:
+ Color::EncodeRGB5A1(color, dst_pixel);
+ break;
+
+ case registers.framebuffer.RGB565:
+ Color::EncodeRGB565(color, dst_pixel);
+ break;
+
+ case registers.framebuffer.RGBA4:
+ Color::EncodeRGBA4(color, dst_pixel);
break;
- }
default:
- LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", registers.framebuffer.color_format);
+ LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", registers.framebuffer.color_format.Value());
UNIMPLEMENTED();
}
}
static const Math::Vec4<u8> GetPixel(int x, int y) {
const PAddr addr = registers.framebuffer.GetColorBufferPhysicalAddress();
- u32* color_buffer_u32 = reinterpret_cast<u32*>(Memory::GetPointer(PAddrToVAddr(addr)));
y = (registers.framebuffer.height - y);
- u32 value = *(color_buffer_u32 + x + y * registers.framebuffer.GetWidth());
- Math::Vec4<u8> ret;
- ret.a() = value >> 24;
- ret.r() = (value >> 16) & 0xFF;
- ret.g() = (value >> 8) & 0xFF;
- ret.b() = value & 0xFF;
- return ret;
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(registers.framebuffer.color_format.Value()));
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * registers.framebuffer.width * bytes_per_pixel;
+ u8* src_pixel = Memory::GetPointer(PAddrToVAddr(addr)) + src_offset;
+
+ switch (registers.framebuffer.color_format) {
+ case registers.framebuffer.RGBA8:
+ return Color::DecodeRGBA8(src_pixel);
+
+ case registers.framebuffer.RGB8:
+ return Color::DecodeRGB8(src_pixel);
+
+ case registers.framebuffer.RGB5A1:
+ return Color::DecodeRGB5A1(src_pixel);
+
+ case registers.framebuffer.RGB565:
+ return Color::DecodeRGB565(src_pixel);
+
+ case registers.framebuffer.RGBA4:
+ return Color::DecodeRGBA4(src_pixel);
+
+ default:
+ LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", registers.framebuffer.color_format.Value());
+ UNIMPLEMENTED();
+ }
+
+ return {};
}
static u32 GetDepth(int x, int y) {
const PAddr addr = registers.framebuffer.GetDepthBufferPhysicalAddress();
- u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(addr)));
+ u8* depth_buffer = Memory::GetPointer(PAddrToVAddr(addr));
y = (registers.framebuffer.height - y);
+
+ const u32 coarse_y = y & ~7;
+ u32 stride = registers.framebuffer.width * 2;
// Assuming 16-bit depth buffer format until actual format handling is implemented
- return *(depth_buffer + x + y * registers.framebuffer.GetWidth());
+ return *(u16*)(depth_buffer + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * stride);
}
static void SetDepth(int x, int y, u16 value) {
const PAddr addr = registers.framebuffer.GetDepthBufferPhysicalAddress();
- u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(addr)));
+ u8* depth_buffer = Memory::GetPointer(PAddrToVAddr(addr));
y = (registers.framebuffer.height - y);
+ const u32 coarse_y = y & ~7;
+ u32 stride = registers.framebuffer.width * 2;
+
// Assuming 16-bit depth buffer format until actual format handling is implemented
- *(depth_buffer + x + y * registers.framebuffer.GetWidth()) = value;
+ *(u16*)(depth_buffer + VideoCore::GetMortonOffset(x, y, 2) + coarse_y * stride) = value;
}
// NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 272695174..95ab96340 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -4,7 +4,10 @@
#include "core/hw/gpu.h"
#include "core/mem_map.h"
+
#include "common/emu_window.h"
+#include "common/profiler_reporting.h"
+
#include "video_core/video_core.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -75,9 +78,18 @@ void RendererOpenGL::SwapBuffers() {
DrawScreens();
+ auto& profiler = Common::Profiling::GetProfilingManager();
+ profiler.FinishFrame();
+ {
+ auto aggregator = Common::Profiling::GetTimingResultsAggregator();
+ aggregator->AddFrame(profiler.GetPreviousFrameResults());
+ }
+
// Swap buffers
render_window->PollEvents();
render_window->SwapBuffers();
+
+ profiler.BeginFrame();
}
/**
@@ -242,28 +254,26 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x
* Draws the emulated screens to the emulator window.
*/
void RendererOpenGL::DrawScreens() {
- auto viewport_extent = GetViewportExtent();
- glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom?
+ auto layout = render_window->GetFramebufferLayout();
+
+ glViewport(0, 0, layout.width, layout.height);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program_id);
// Set projection matrix
- std::array<GLfloat, 3*2> ortho_matrix = MakeOrthographicMatrix((float)resolution_width, (float)resolution_height);
+ std::array<GLfloat, 3 * 2> ortho_matrix = MakeOrthographicMatrix((float)layout.width,
+ (float)layout.height);
glUniformMatrix3x2fv(uniform_modelview_matrix, 1, GL_FALSE, ortho_matrix.data());
// Bind texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glUniform1i(uniform_color_texture, 0);
- const float max_width = std::max((float)VideoCore::kScreenTopWidth, (float)VideoCore::kScreenBottomWidth);
- const float top_x = 0.5f * (max_width - VideoCore::kScreenTopWidth);
- const float bottom_x = 0.5f * (max_width - VideoCore::kScreenBottomWidth);
-
- DrawSingleScreenRotated(textures[0], top_x, 0,
- (float)VideoCore::kScreenTopWidth, (float)VideoCore::kScreenTopHeight);
- DrawSingleScreenRotated(textures[1], bottom_x, (float)VideoCore::kScreenTopHeight,
- (float)VideoCore::kScreenBottomWidth, (float)VideoCore::kScreenBottomHeight);
+ DrawSingleScreenRotated(textures[0], (float)layout.top_screen.left, (float)layout.top_screen.top,
+ (float)layout.top_screen.GetWidth(), (float)layout.top_screen.GetHeight());
+ DrawSingleScreenRotated(textures[1], (float)layout.bottom_screen.left,(float)layout.bottom_screen.top,
+ (float)layout.bottom_screen.GetWidth(), (float)layout.bottom_screen.GetHeight());
m_current_frame++;
}
@@ -280,34 +290,6 @@ void RendererOpenGL::SetWindow(EmuWindow* window) {
render_window = window;
}
-MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() {
- unsigned framebuffer_width;
- unsigned framebuffer_height;
- std::tie(framebuffer_width, framebuffer_height) = render_window->GetFramebufferSize();
-
- float window_aspect_ratio = static_cast<float>(framebuffer_height) / framebuffer_width;
- float emulation_aspect_ratio = static_cast<float>(resolution_height) / resolution_width;
-
- MathUtil::Rectangle<unsigned> viewport_extent;
- if (window_aspect_ratio > emulation_aspect_ratio) {
- // Window is narrower than the emulation content => apply borders to the top and bottom
- unsigned viewport_height = static_cast<unsigned>(std::round(emulation_aspect_ratio * framebuffer_width));
- viewport_extent.left = 0;
- viewport_extent.top = (framebuffer_height - viewport_height) / 2;
- viewport_extent.right = viewport_extent.left + framebuffer_width;
- viewport_extent.bottom = viewport_extent.top + viewport_height;
- } else {
- // Otherwise, apply borders to the left and right sides of the window.
- unsigned viewport_width = static_cast<unsigned>(std::round(framebuffer_height / emulation_aspect_ratio));
- viewport_extent.left = (framebuffer_width - viewport_width) / 2;
- viewport_extent.top = 0;
- viewport_extent.right = viewport_extent.left + viewport_width;
- viewport_extent.bottom = viewport_extent.top + framebuffer_height;
- }
-
- return viewport_extent;
-}
-
/// Initialize the renderer
void RendererOpenGL::Init() {
render_window->MakeCurrent();
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 6fd640425..bda793fa5 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -35,4 +35,54 @@ struct TGAHeader {
*/
void DumpTGA(std::string filename, short width, short height, u8* raw_data);
+/**
+ * Interleave the lower 3 bits of each coordinate to get the intra-block offsets, which are
+ * arranged in a Z-order curve. More details on the bit manipulation at:
+ * https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
+ */
+static inline u32 MortonInterleave(u32 x, u32 y) {
+ u32 i = (x & 7) | ((y & 7) << 8); // ---- -210
+ i = (i ^ (i << 2)) & 0x1313; // ---2 --10
+ i = (i ^ (i << 1)) & 0x1515; // ---2 -1-0
+ i = (i | (i >> 7)) & 0x3F;
+ return i;
+}
+
+/**
+ * Calculates the offset of the position of the pixel in Morton order
+ */
+static inline u32 GetMortonOffset(u32 x, u32 y, u32 bytes_per_pixel) {
+ // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each
+ // of which is composed of four 2x2 subtiles each of which is composed of four texels.
+ // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g.
+ // texels are laid out in a 2x2 subtile like this:
+ // 2 3
+ // 0 1
+ //
+ // The full 8x8 tile has the texels arranged like this:
+ //
+ // 42 43 46 47 58 59 62 63
+ // 40 41 44 45 56 57 60 61
+ // 34 35 38 39 50 51 54 55
+ // 32 33 36 37 48 49 52 53
+ // 10 11 14 15 26 27 30 31
+ // 08 09 12 13 24 25 28 29
+ // 02 03 06 07 18 19 22 23
+ // 00 01 04 05 16 17 20 21
+ //
+ // This pattern is what's called Z-order curve, or Morton order.
+
+ const unsigned int block_width = 8;
+ const unsigned int block_height = 8;
+
+ const unsigned int coarse_x = x & ~7;
+ const unsigned int coarse_y = y & ~7;
+
+ u32 i = VideoCore::MortonInterleave(x, y);
+
+ const unsigned int offset = coarse_x * block_height;
+
+ return (i + offset) * bytes_per_pixel;
+}
+
} // namespace
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp
index def868ac7..4eb3e743e 100644
--- a/src/video_core/vertex_shader.cpp
+++ b/src/video_core/vertex_shader.cpp
@@ -17,6 +17,7 @@
#include "vertex_shader.h"
#include "debug_utils/debug_utils.h"
+using nihstro::OpCode;
using nihstro::Instruction;
using nihstro::RegisterType;
using nihstro::SourceRegister;
@@ -90,6 +91,7 @@ struct VertexShaderState {
u8 repeat_counter; // How often to repeat until this call stack element is removed
u8 loop_increment; // Which value to add to the loop counter after an iteration
// TODO: Should this be a signed value? Does it even matter?
+ u32 loop_address; // The address where we'll return to after each loop iteration
};
// TODO: Is there a maximal size for this?
@@ -115,6 +117,8 @@ static void ProcessShaderCode(VertexShaderState& state) {
if (top.repeat_counter-- == 0) {
state.program_counter = &shader_memory[top.return_address];
state.call_stack.pop();
+ } else {
+ state.program_counter = &shader_memory[top.loop_address];
}
// TODO: Is "trying again" accurate to hardware?
@@ -129,7 +133,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
static auto call = [](VertexShaderState& state, u32 offset, u32 num_instructions,
u32 return_offset, u8 repeat_count, u8 loop_increment) {
state.program_counter = &shader_memory[offset] - 1; // -1 to make sure when incrementing the PC we end up at the correct offset
- state.call_stack.push({ offset + num_instructions, return_offset, repeat_count, loop_increment });
+ state.call_stack.push({ offset + num_instructions, return_offset, repeat_count, loop_increment, offset });
};
u32 binary_offset = state.program_counter - shader_memory.data();
@@ -151,10 +155,10 @@ static void ProcessShaderCode(VertexShaderState& state) {
}
};
- switch (instr.opcode.GetInfo().type) {
- case Instruction::OpCodeType::Arithmetic:
+ switch (instr.opcode.Value().GetInfo().type) {
+ case OpCode::Type::Arithmetic:
{
- bool is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed);
+ bool is_inverted = 0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed);
// TODO: We don't really support this properly: For instance, the address register
// offset needs to be applied to SRC2 instead, etc.
// For now, we just abort in this situation.
@@ -194,15 +198,15 @@ static void ProcessShaderCode(VertexShaderState& state) {
src2[3] = src2[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.common.dest < 0x08) ? state.output_register_table[4*instr.common.dest.GetIndex()]
- : (instr.common.dest < 0x10) ? dummy_vec4_float24
- : (instr.common.dest < 0x20) ? &state.temporary_registers[instr.common.dest.GetIndex()][0]
+ float24* dest = (instr.common.dest.Value() < 0x08) ? state.output_register_table[4*instr.common.dest.Value().GetIndex()]
+ : (instr.common.dest.Value() < 0x10) ? dummy_vec4_float24
+ : (instr.common.dest.Value() < 0x20) ? &state.temporary_registers[instr.common.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id);
- switch (instr.opcode.EffectiveOpCode()) {
- case Instruction::OpCode::ADD:
+ switch (instr.opcode.Value().EffectiveOpCode()) {
+ case OpCode::Id::ADD:
{
for (int i = 0; i < 4; ++i) {
if (!swizzle.DestComponentEnabled(i))
@@ -214,7 +218,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
- case Instruction::OpCode::MUL:
+ case OpCode::Id::MUL:
{
for (int i = 0; i < 4; ++i) {
if (!swizzle.DestComponentEnabled(i))
@@ -226,7 +230,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
- case Instruction::OpCode::MAX:
+ case OpCode::Id::MAX:
for (int i = 0; i < 4; ++i) {
if (!swizzle.DestComponentEnabled(i))
continue;
@@ -235,11 +239,11 @@ static void ProcessShaderCode(VertexShaderState& state) {
}
break;
- case Instruction::OpCode::DP3:
- case Instruction::OpCode::DP4:
+ case OpCode::Id::DP3:
+ case OpCode::Id::DP4:
{
float24 dot = float24::FromFloat32(0.f);
- int num_components = (instr.opcode == Instruction::OpCode::DP3) ? 3 : 4;
+ int num_components = (instr.opcode.Value() == OpCode::Id::DP3) ? 3 : 4;
for (int i = 0; i < num_components; ++i)
dot = dot + src1[i] * src2[i];
@@ -253,7 +257,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
}
// Reciprocal
- case Instruction::OpCode::RCP:
+ case OpCode::Id::RCP:
{
for (int i = 0; i < 4; ++i) {
if (!swizzle.DestComponentEnabled(i))
@@ -268,7 +272,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
}
// Reciprocal Square Root
- case Instruction::OpCode::RSQ:
+ case OpCode::Id::RSQ:
{
for (int i = 0; i < 4; ++i) {
if (!swizzle.DestComponentEnabled(i))
@@ -282,7 +286,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
- case Instruction::OpCode::MOVA:
+ case OpCode::Id::MOVA:
{
for (int i = 0; i < 2; ++i) {
if (!swizzle.DestComponentEnabled(i))
@@ -295,7 +299,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
- case Instruction::OpCode::MOV:
+ case OpCode::Id::MOV:
{
for (int i = 0; i < 4; ++i) {
if (!swizzle.DestComponentEnabled(i))
@@ -306,7 +310,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
- case Instruction::OpCode::CMP:
+ case OpCode::Id::CMP:
for (int i = 0; i < 2; ++i) {
// TODO: Can you restrict to one compare via dest masking?
@@ -347,7 +351,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
default:
LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x",
- (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex);
+ (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex);
DEBUG_ASSERT(false);
break;
}
@@ -355,9 +359,9 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
- case Instruction::OpCodeType::MultiplyAdd:
+ case OpCode::Type::MultiplyAdd:
{
- if (instr.opcode.EffectiveOpCode() == Instruction::OpCode::MAD) {
+ if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD) {
const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.mad.operand_desc_id];
const float24* src1_ = LookupSourceRegister(instr.mad.src1);
@@ -405,9 +409,9 @@ static void ProcessShaderCode(VertexShaderState& state) {
src3[3] = src3[3] * float24::FromFloat32(-1);
}
- float24* dest = (instr.mad.dest < 0x08) ? state.output_register_table[4*instr.mad.dest.GetIndex()]
- : (instr.mad.dest < 0x10) ? dummy_vec4_float24
- : (instr.mad.dest < 0x20) ? &state.temporary_registers[instr.mad.dest.GetIndex()][0]
+ float24* dest = (instr.mad.dest.Value() < 0x08) ? state.output_register_table[4*instr.mad.dest.Value().GetIndex()]
+ : (instr.mad.dest.Value() < 0x10) ? dummy_vec4_float24
+ : (instr.mad.dest.Value() < 0x20) ? &state.temporary_registers[instr.mad.dest.Value().GetIndex()][0]
: dummy_vec4_float24;
for (int i = 0; i < 4; ++i) {
@@ -418,7 +422,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
}
} else {
LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x",
- (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex);
+ (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex);
}
break;
}
@@ -445,31 +449,31 @@ static void ProcessShaderCode(VertexShaderState& state) {
};
// Handle each instruction on its own
- switch (instr.opcode) {
- case Instruction::OpCode::END:
+ switch (instr.opcode.Value()) {
+ case OpCode::Id::END:
exit_loop = true;
break;
- case Instruction::OpCode::JMPC:
+ case OpCode::Id::JMPC:
if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
state.program_counter = &shader_memory[instr.flow_control.dest_offset] - 1;
}
break;
- case Instruction::OpCode::JMPU:
+ case OpCode::Id::JMPU:
if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) {
state.program_counter = &shader_memory[instr.flow_control.dest_offset] - 1;
}
break;
- case Instruction::OpCode::CALL:
+ case OpCode::Id::CALL:
call(state,
instr.flow_control.dest_offset,
instr.flow_control.num_instructions,
binary_offset + 1, 0, 0);
break;
- case Instruction::OpCode::CALLU:
+ case OpCode::Id::CALLU:
if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) {
call(state,
instr.flow_control.dest_offset,
@@ -478,7 +482,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
}
break;
- case Instruction::OpCode::CALLC:
+ case OpCode::Id::CALLC:
if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
call(state,
instr.flow_control.dest_offset,
@@ -487,10 +491,10 @@ static void ProcessShaderCode(VertexShaderState& state) {
}
break;
- case Instruction::OpCode::NOP:
+ case OpCode::Id::NOP:
break;
- case Instruction::OpCode::IFU:
+ case OpCode::Id::IFU:
if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) {
call(state,
binary_offset + 1,
@@ -505,7 +509,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
- case Instruction::OpCode::IFC:
+ case OpCode::Id::IFC:
{
// TODO: Do we need to consider swizzlers here?
@@ -524,7 +528,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
- case Instruction::OpCode::LOOP:
+ case OpCode::Id::LOOP:
{
state.address_registers[2] = shader_uniforms.i[instr.flow_control.int_uniform_id].y;
@@ -539,7 +543,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
default:
LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
- (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex);
+ (int)instr.opcode.Value().EffectiveOpCode(), instr.opcode.Value().GetInfo().name, instr.hex);
break;
}
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 0a236595c..b9d4ede3a 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -18,7 +18,6 @@ namespace VideoCore {
EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
RendererBase* g_renderer = nullptr; ///< Renderer plugin
-int g_current_frame = 0;
/// Initialize the video core
void Init(EmuWindow* emu_window) {
@@ -27,8 +26,6 @@ void Init(EmuWindow* emu_window) {
g_renderer->SetWindow(g_emu_window);
g_renderer->Init();
- g_current_frame = 0;
-
LOG_DEBUG(Render, "initialized OK");
}
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index b782f17bd..1b51d39bf 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -30,7 +30,6 @@ static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height
// ---------------------
extern RendererBase* g_renderer; ///< Renderer plugin
-extern int g_current_frame; ///< Current frame
extern EmuWindow* g_emu_window; ///< Emu window
/// Start the video core