aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/citra.cpp21
-rw-r--r--src/citra/config.cpp22
-rw-r--r--src/citra/config.h4
-rw-r--r--src/citra/default_ini.h5
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp82
-rw-r--r--src/citra/emu_window/emu_window_glfw.h18
-rw-r--r--src/citra_qt/CMakeLists.txt7
-rw-r--r--src/citra_qt/bootmanager.cpp122
-rw-r--r--src/citra_qt/bootmanager.hxx22
-rw-r--r--src/citra_qt/config.cpp44
-rw-r--r--src/citra_qt/config.h8
-rw-r--r--src/citra_qt/config/controller_config_util.cpp4
-rw-r--r--src/citra_qt/debugger/callstack.cpp6
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints.cpp261
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints.hxx53
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints_p.hxx38
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp241
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.hxx37
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp282
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.hxx92
-rw-r--r--src/citra_qt/debugger/registers.cpp28
-rw-r--r--src/citra_qt/debugger/registers.hxx10
-rw-r--r--src/citra_qt/hotkeys.cpp4
-rw-r--r--src/citra_qt/main.cpp63
-rw-r--r--src/citra_qt/util/spinbox.cpp303
-rw-r--r--src/citra_qt/util/spinbox.hxx88
-rw-r--r--src/common/CMakeLists.txt13
-rw-r--r--src/common/break_points.cpp2
-rw-r--r--src/common/chunk_file.h73
-rw-r--r--src/common/common_funcs.h21
-rw-r--r--src/common/common_paths.h18
-rw-r--r--src/common/common_types.h8
-rw-r--r--src/common/concurrent_ring_buffer.h164
-rw-r--r--src/common/console_listener.cpp319
-rw-r--r--src/common/console_listener.h38
-rw-r--r--src/common/emu_window.h128
-rw-r--r--src/common/extended_trace.cpp46
-rw-r--r--src/common/fifo_queue.h6
-rw-r--r--src/common/file_search.cpp2
-rw-r--r--src/common/file_util.cpp156
-rw-r--r--src/common/file_util.h188
-rw-r--r--src/common/hash.cpp32
-rw-r--r--src/common/linear_disk_cache.h8
-rw-r--r--src/common/log.h126
-rw-r--r--src/common/log_manager.cpp199
-rw-r--r--src/common/log_manager.h166
-rw-r--r--src/common/logging/backend.cpp151
-rw-r--r--src/common/logging/backend.h134
-rw-r--r--src/common/logging/filter.cpp132
-rw-r--r--src/common/logging/filter.h63
-rw-r--r--src/common/logging/log.h115
-rw-r--r--src/common/logging/text_formatter.cpp136
-rw-r--r--src/common/logging/text_formatter.h41
-rw-r--r--src/common/math_util.cpp14
-rw-r--r--src/common/math_util.h9
-rw-r--r--src/common/mem_arena.cpp46
-rw-r--r--src/common/memory_util.cpp20
-rw-r--r--src/common/misc.cpp4
-rw-r--r--src/common/msg_handler.cpp2
-rw-r--r--src/common/msg_handler.h42
-rw-r--r--src/common/platform.h2
-rw-r--r--src/common/scope_exit.h37
-rw-r--r--src/common/string_util.cpp190
-rw-r--r--src/common/string_util.h25
-rw-r--r--src/common/symbols.cpp4
-rw-r--r--src/common/symbols.h2
-rw-r--r--src/common/thread.cpp6
-rw-r--r--src/common/thread.h11
-rw-r--r--src/common/thread_queue_list.h16
-rw-r--r--src/common/timer.cpp6
-rw-r--r--src/common/utf8.cpp24
-rw-r--r--src/core/CMakeLists.txt54
-rw-r--r--src/core/arm/arm_interface.h8
-rw-r--r--src/core/arm/disassembler/load_symbol_map.cpp4
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp13
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h4
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp369
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.h4
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp4
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h4
-rw-r--r--src/core/arm/interpreter/armemu.cpp1069
-rw-r--r--src/core/arm/interpreter/armsupp.cpp8
-rw-r--r--src/core/arm/interpreter/thumbemu.cpp2
-rw-r--r--src/core/arm/skyeye_common/armcpu.h2
-rw-r--r--src/core/arm/skyeye_common/armdefs.h510
-rw-r--r--src/core/arm/skyeye_common/armemu.h22
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp14
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/core_timing.cpp70
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/file_sys/archive.h104
-rw-r--r--src/core/file_sys/archive_backend.h225
-rw-r--r--src/core/file_sys/archive_romfs.cpp78
-rw-r--r--src/core/file_sys/archive_romfs.h67
-rw-r--r--src/core/file_sys/archive_savedata.cpp33
-rw-r--r--src/core/file_sys/archive_savedata.h32
-rw-r--r--src/core/file_sys/archive_sdmc.cpp102
-rw-r--r--src/core/file_sys/archive_sdmc.h72
-rw-r--r--src/core/file_sys/directory_backend.h (renamed from src/core/file_sys/directory.h)12
-rw-r--r--src/core/file_sys/directory_romfs.cpp4
-rw-r--r--src/core/file_sys/directory_romfs.h10
-rw-r--r--src/core/file_sys/directory_sdmc.cpp81
-rw-r--r--src/core/file_sys/directory_sdmc.h48
-rw-r--r--src/core/file_sys/disk_archive.cpp167
-rw-r--r--src/core/file_sys/disk_archive.h101
-rw-r--r--src/core/file_sys/file_backend.h (renamed from src/core/file_sys/file.h)11
-rw-r--r--src/core/file_sys/file_romfs.cpp19
-rw-r--r--src/core/file_sys/file_romfs.h14
-rw-r--r--src/core/file_sys/file_sdmc.cpp107
-rw-r--r--src/core/file_sys/file_sdmc.h75
-rw-r--r--src/core/hle/config_mem.cpp5
-rw-r--r--src/core/hle/coprocessor.cpp2
-rw-r--r--src/core/hle/function_wrappers.h18
-rw-r--r--src/core/hle/hle.cpp19
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp21
-rw-r--r--src/core/hle/kernel/address_arbiter.h4
-rw-r--r--src/core/hle/kernel/archive.cpp436
-rw-r--r--src/core/hle/kernel/archive.h70
-rw-r--r--src/core/hle/kernel/event.cpp47
-rw-r--r--src/core/hle/kernel/event.h14
-rw-r--r--src/core/hle/kernel/kernel.cpp29
-rw-r--r--src/core/hle/kernel/kernel.h83
-rw-r--r--src/core/hle/kernel/mutex.cpp124
-rw-r--r--src/core/hle/kernel/mutex.h11
-rw-r--r--src/core/hle/kernel/semaphore.cpp94
-rw-r--r--src/core/hle/kernel/semaphore.h32
-rw-r--r--src/core/hle/kernel/session.h58
-rw-r--r--src/core/hle/kernel/shared_memory.cpp53
-rw-r--r--src/core/hle/kernel/shared_memory.h7
-rw-r--r--src/core/hle/kernel/thread.cpp215
-rw-r--r--src/core/hle/kernel/thread.h28
-rw-r--r--src/core/hle/result.h402
-rw-r--r--src/core/hle/service/ac_u.cpp26
-rw-r--r--src/core/hle/service/ac_u.h4
-rw-r--r--src/core/hle/service/am_app.cpp24
-rw-r--r--src/core/hle/service/am_app.h27
-rw-r--r--src/core/hle/service/am_net.cpp47
-rw-r--r--src/core/hle/service/am_net.h27
-rw-r--r--src/core/hle/service/apt_u.cpp203
-rw-r--r--src/core/hle/service/apt_u.h4
-rw-r--r--src/core/hle/service/boss_u.cpp28
-rw-r--r--src/core/hle/service/boss_u.h27
-rw-r--r--src/core/hle/service/cecd_u.cpp24
-rw-r--r--src/core/hle/service/cecd_u.h27
-rw-r--r--src/core/hle/service/cfg_i.cpp59
-rw-r--r--src/core/hle/service/cfg_i.h27
-rw-r--r--src/core/hle/service/cfg_u.cpp92
-rw-r--r--src/core/hle/service/cfg_u.h2
-rw-r--r--src/core/hle/service/csnd_snd.cpp39
-rw-r--r--src/core/hle/service/csnd_snd.h27
-rw-r--r--src/core/hle/service/dsp_dsp.cpp194
-rw-r--r--src/core/hle/service/dsp_dsp.h4
-rw-r--r--src/core/hle/service/err_f.cpp4
-rw-r--r--src/core/hle/service/err_f.h4
-rw-r--r--src/core/hle/service/frd_u.cpp35
-rw-r--r--src/core/hle/service/frd_u.h27
-rw-r--r--src/core/hle/service/fs/archive.cpp431
-rw-r--r--src/core/hle/service/fs/archive.h125
-rw-r--r--src/core/hle/service/fs/fs_user.cpp529
-rw-r--r--src/core/hle/service/fs/fs_user.h (renamed from src/core/hle/service/fs_user.h)12
-rw-r--r--src/core/hle/service/fs_user.cpp354
-rw-r--r--src/core/hle/service/gsp_gpu.cpp80
-rw-r--r--src/core/hle/service/hid_user.cpp17
-rw-r--r--src/core/hle/service/hid_user.h6
-rw-r--r--src/core/hle/service/ir_rst.cpp36
-rw-r--r--src/core/hle/service/ir_rst.h27
-rw-r--r--src/core/hle/service/ir_u.cpp45
-rw-r--r--src/core/hle/service/ir_u.h27
-rw-r--r--src/core/hle/service/ldr_ro.cpp28
-rw-r--r--src/core/hle/service/ldr_ro.h27
-rw-r--r--src/core/hle/service/mic_u.cpp2
-rw-r--r--src/core/hle/service/mic_u.h2
-rw-r--r--src/core/hle/service/nim_aoc.cpp31
-rw-r--r--src/core/hle/service/nim_aoc.h27
-rw-r--r--src/core/hle/service/nwm_uds.h2
-rw-r--r--src/core/hle/service/pm_app.cpp35
-rw-r--r--src/core/hle/service/pm_app.h27
-rw-r--r--src/core/hle/service/ptm_u.cpp98
-rw-r--r--src/core/hle/service/ptm_u.h2
-rw-r--r--src/core/hle/service/service.cpp36
-rw-r--r--src/core/hle/service/service.h74
-rw-r--r--src/core/hle/service/soc_u.h2
-rw-r--r--src/core/hle/service/srv.cpp28
-rw-r--r--src/core/hle/svc.cpp215
-rw-r--r--src/core/hle/svc.h2
-rw-r--r--src/core/hw/gpu.cpp23
-rw-r--r--src/core/hw/gpu.h2
-rw-r--r--src/core/hw/hw.cpp21
-rw-r--r--src/core/hw/ndma.cpp47
-rw-r--r--src/core/hw/ndma.h26
-rw-r--r--src/core/loader/3dsx.cpp236
-rw-r--r--src/core/loader/3dsx.h32
-rw-r--r--src/core/loader/elf.cpp16
-rw-r--r--src/core/loader/loader.cpp18
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/ncch.cpp48
-rw-r--r--src/core/loader/ncch.h8
-rw-r--r--src/core/mem_map.cpp64
-rw-r--r--src/core/mem_map.h33
-rw-r--r--src/core/mem_map_funcs.cpp90
-rw-r--r--src/core/settings.h4
-rw-r--r--src/core/system.cpp12
-rw-r--r--src/core/system.h22
-rw-r--r--src/video_core/clipper.cpp6
-rw-r--r--src/video_core/command_processor.cpp30
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp138
-rw-r--r--src/video_core/debug_utils/debug_utils.h148
-rw-r--r--src/video_core/gpu_debugger.h2
-rw-r--r--src/video_core/pica.h42
-rw-r--r--src/video_core/primitive_assembly.cpp2
-rw-r--r--src/video_core/rasterizer.cpp12
-rw-r--r--src/video_core/renderer_base.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp34
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp45
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h11
-rw-r--r--src/video_core/utils.cpp4
-rw-r--r--src/video_core/utils.h20
-rw-r--r--src/video_core/vertex_shader.cpp22
-rw-r--r--src/video_core/vertex_shader.h2
-rw-r--r--src/video_core/video_core.cpp8
222 files changed, 9504 insertions, 5175 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 6ac5c5dc..d6e8a4ec 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -2,9 +2,15 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
+#include <thread>
+
#include "common/common.h"
-#include "common/log_manager.h"
+#include "common/logging/text_formatter.h"
+#include "common/logging/backend.h"
+#include "common/logging/filter.h"
+#include "common/scope_exit.h"
+#include "core/settings.h"
#include "core/system.h"
#include "core/core.h"
#include "core/loader/loader.h"
@@ -14,14 +20,21 @@
/// Application entry point
int __cdecl main(int argc, char **argv) {
- LogManager::Init();
+ std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
+ Log::Filter log_filter(Log::Level::Debug);
+ std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter);
+ SCOPE_EXIT({
+ logger->Close();
+ logging_thread.join();
+ });
if (argc < 2) {
- ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified");
+ LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return -1;
}
Config config;
+ log_filter.ParseFilterString(Settings::values.log_filter);
std::string boot_filename = argv[1];
EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
@@ -30,7 +43,7 @@ int __cdecl main(int argc, char **argv) {
Loader::ResultStatus load_result = Loader::LoadFile(boot_filename);
if (Loader::ResultStatus::Success != load_result) {
- ERROR_LOG(BOOT, "Failed to load ROM (Error %i)!", load_result);
+ LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result);
return -1;
}
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index c5ce8a16..92764809 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -22,21 +22,22 @@ Config::Config() {
bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) {
if (config->ParseError() < 0) {
if (retry) {
- ERROR_LOG(CONFIG, "Failed to load %s. Creating file from defaults...", location);
+ LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
FileUtil::CreateFullPath(location);
FileUtil::WriteStringToFile(true, default_contents, location);
*config = INIReader(location); // Reopen file
return LoadINI(config, location, default_contents, false);
}
- ERROR_LOG(CONFIG, "Failed.");
+ LOG_ERROR(Config, "Failed.");
return false;
}
- INFO_LOG(CONFIG, "Successfully loaded %s", location);
+ LOG_INFO(Config, "Successfully loaded %s", location);
return true;
}
-void Config::ReadControls() {
+void Config::ReadValues() {
+ // Controls
Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A);
Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S);
Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z);
@@ -54,22 +55,21 @@ void Config::ReadControls() {
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);
-}
-void Config::ReadCore() {
+ // Core
Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter);
Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 60);
-}
-void Config::ReadData() {
+ // Data Storage
Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true);
+
+ // Miscellaneous
+ Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info");
}
void Config::Reload() {
LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file);
- ReadControls();
- ReadCore();
- ReadData();
+ ReadValues();
}
Config::~Config() {
diff --git a/src/citra/config.h b/src/citra/config.h
index 4f655187..2b46fa8a 100644
--- a/src/citra/config.h
+++ b/src/citra/config.h
@@ -15,9 +15,7 @@ class Config {
std::string glfw_config_loc;
bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true);
- void ReadControls();
- void ReadCore();
- void ReadData();
+ void ReadValues();
public:
Config();
~Config();
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 7352c70c..7cf543e0 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -28,10 +28,13 @@ pad_sright =
[Core]
cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental)
-gpu_refresh_rate = ## 60 (default), 1024 or 2048 may work better on the FastInterpreter
+gpu_refresh_rate = ## 60 (default)
[Data Storage]
use_virtual_sd =
+
+[Miscellaneous]
+log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
)";
}
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
index 0c774bbc..929e09f4 100644
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ b/src/citra/emu_window/emu_window_glfw.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
+#include <GLFW/glfw3.h>
+
#include "common/common.h"
#include "video_core/video_core.h"
@@ -10,22 +12,21 @@
#include "citra/emu_window/emu_window_glfw.h"
+EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
+ return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
+}
+
/// Called by GLFW when a key event occurs
void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
- if (!VideoCore::g_emu_window) {
- return;
- }
-
- int keyboard_id = ((EmuWindow_GLFW*)VideoCore::g_emu_window)->keyboard_id;
+ int keyboard_id = GetEmuWindow(win)->keyboard_id;
if (action == GLFW_PRESS) {
EmuWindow::KeyPressed({key, keyboard_id});
- }
-
- if (action == GLFW_RELEASE) {
+ } else if (action == GLFW_RELEASE) {
EmuWindow::KeyReleased({key, keyboard_id});
}
+
HID_User::PadUpdateComplete();
}
@@ -34,15 +35,36 @@ const bool EmuWindow_GLFW::IsOpen() {
return glfwWindowShouldClose(m_render_window) == 0;
}
+void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
+ _dbg_assert_(Frontend, width > 0);
+ _dbg_assert_(Frontend, height > 0);
+
+ GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height));
+}
+
+void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
+ _dbg_assert_(Frontend, width > 0);
+ _dbg_assert_(Frontend, 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));
+}
+
/// EmuWindow_GLFW constructor
EmuWindow_GLFW::EmuWindow_GLFW() {
keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps();
+ glfwSetErrorCallback([](int error, const char *desc){
+ LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc);
+ });
+
// Initialize the window
if(glfwInit() != GL_TRUE) {
- printf("Failed to initialize GLFW! Exiting...");
+ LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting...");
exit(1);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
@@ -50,19 +72,31 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
// GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-
- m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
- (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
- m_window_title.c_str(), NULL, NULL);
- if (m_render_window == NULL) {
- printf("Failed to create GLFW window! Exiting...");
+ std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
+ m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
+ (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
+ window_title.c_str(), nullptr, nullptr);
+
+ if (m_render_window == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
exit(1);
}
-
- // Setup callbacks
+
glfwSetWindowUserPointer(m_render_window, this);
+
+ // Notify base interface about window state
+ int width, height;
+ glfwGetFramebufferSize(m_render_window, &width, &height);
+ OnFramebufferResizeEvent(m_render_window, width, height);
+
+ glfwGetWindowSize(m_render_window, &width, &height);
+ OnClientAreaResizeEvent(m_render_window, width, height);
+
+ // Setup callbacks
glfwSetKeyCallback(m_render_window, OnKeyEvent);
+ glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
+ glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);
DoneCurrent();
}
@@ -89,7 +123,7 @@ void EmuWindow_GLFW::MakeCurrent() {
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
void EmuWindow_GLFW::DoneCurrent() {
- glfwMakeContextCurrent(NULL);
+ glfwMakeContextCurrent(nullptr);
}
void EmuWindow_GLFW::ReloadSetKeymaps() {
@@ -110,3 +144,15 @@ void EmuWindow_GLFW::ReloadSetKeymaps() {
KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP);
KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN);
}
+
+void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
+ std::pair<int,int> current_size;
+ glfwGetWindowSize(m_render_window, &current_size.first, &current_size.second);
+
+ _dbg_assert_(Frontend, (int)minimal_size.first > 0 && (int)minimal_size.second > 0);
+ int new_width = std::max(current_size.first, (int)minimal_size.first);
+ int new_height = std::max(current_size.second, (int)minimal_size.second);
+
+ if (current_size != std::make_pair(new_width, new_height))
+ glfwSetWindowSize(m_render_window, new_width, new_height);
+}
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h
index 7c307214..5b04e87b 100644
--- a/src/citra/emu_window/emu_window_glfw.h
+++ b/src/citra/emu_window/emu_window_glfw.h
@@ -4,10 +4,10 @@
#pragma once
-#include <GLFW/glfw3.h>
-
#include "common/emu_window.h"
+struct GLFWwindow;
+
class EmuWindow_GLFW : public EmuWindow {
public:
EmuWindow_GLFW();
@@ -16,12 +16,12 @@ public:
/// Swap buffers to display the next frame
void SwapBuffers() override;
- /// Polls window events
- void PollEvents() override;
+ /// Polls window events
+ void PollEvents() override;
/// Makes the graphics context current for the caller thread
void MakeCurrent() override;
-
+
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
void DoneCurrent() override;
@@ -30,9 +30,17 @@ public:
/// Whether the window is still open, and a close request hasn't yet been sent
const bool IsOpen();
+ static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height);
+
+ static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height);
+
void ReloadSetKeymaps() override;
private:
+ void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
+
+ static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win);
+
GLFWwindow* m_render_window; ///< Internal GLFW render window
/// Device id of keyboard for use with KeyMap
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 98a48a69..90e5c6aa 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -8,9 +8,12 @@ set(SRCS
debugger/callstack.cpp
debugger/disassembler.cpp
debugger/graphics.cpp
+ debugger/graphics_breakpoints.cpp
debugger/graphics_cmdlists.cpp
+ debugger/graphics_framebuffer.cpp
debugger/ramview.cpp
debugger/registers.cpp
+ util/spinbox.cpp
bootmanager.cpp
hotkeys.cpp
main.cpp
@@ -23,9 +26,13 @@ set(HEADERS
debugger/callstack.hxx
debugger/disassembler.hxx
debugger/graphics.hxx
+ debugger/graphics_breakpoints.hxx
+ debugger/graphics_breakpoints_p.hxx
debugger/graphics_cmdlists.hxx
+ debugger/graphics_framebuffer.hxx
debugger/ramview.hxx
debugger/registers.hxx
+ util/spinbox.hxx
bootmanager.hxx
hotkeys.hxx
main.hxx
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 8f379935..6d08d6af 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -2,12 +2,20 @@
#include <QKeyEvent>
#include <QApplication>
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+// Required for screen DPI information
+#include <QScreen>
+#include <QWindow>
+#endif
+
#include "common/common.h"
#include "bootmanager.hxx"
#include "core/core.h"
#include "core/settings.h"
+#include "video_core/debug_utils/debug_utils.h"
+
#include "video_core/video_core.h"
#include "citra_qt/version.h"
@@ -17,7 +25,7 @@
#define APP_TITLE APP_NAME " " APP_VERSION
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
-EmuThread::EmuThread(GRenderWindow* render_window) :
+EmuThread::EmuThread(GRenderWindow* render_window) :
filename(""), exec_cpu_step(false), cpu_running(false),
stop_run(false), render_window(render_window)
{
@@ -33,19 +41,16 @@ void EmuThread::run()
stop_run = false;
while (!stop_run)
{
- for (int tight_loop = 0; tight_loop < 10000; ++tight_loop)
+ if (cpu_running)
{
- if (cpu_running || exec_cpu_step)
- {
- if (exec_cpu_step)
- exec_cpu_step = false;
-
- Core::SingleStep();
- if (!cpu_running) {
- emit CPUStepped();
- yieldCurrentThread();
- }
- }
+ Core::RunLoop();
+ }
+ else if (exec_cpu_step)
+ {
+ exec_cpu_step = false;
+ Core::SingleStep();
+ emit CPUStepped();
+ yieldCurrentThread();
}
}
render_window->moveContext();
@@ -57,26 +62,33 @@ void EmuThread::Stop()
{
if (!isRunning())
{
- INFO_LOG(MASTER_LOG, "EmuThread::Stop called while emu thread wasn't running, returning...");
+ LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
return;
}
stop_run = true;
+ // Release emu threads from any breakpoints, so that this doesn't hang forever.
+ Pica::g_debug_context->ClearBreakpoints();
+
//core::g_state = core::SYS_DIE;
- wait(500);
+ // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
+ wait(1000);
if (isRunning())
{
- WARN_LOG(MASTER_LOG, "EmuThread still running, terminating...");
+ LOG_WARNING(Frontend, "EmuThread still running, terminating...");
quit();
- wait(1000);
+
+ // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
+ // queued... This should be fixed.
+ wait(50000);
if (isRunning())
{
- WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here...");
+ LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
terminate();
}
}
- INFO_LOG(MASTER_LOG, "EmuThread stopped");
+ LOG_INFO(Frontend, "EmuThread stopped");
}
@@ -85,20 +97,20 @@ void EmuThread::Stop()
class GGLWidgetInternal : public QGLWidget
{
public:
- GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) : QGLWidget(fmt, parent)
- {
- parent_ = parent;
+ GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
+ : QGLWidget(fmt, parent), parent(parent) {
}
- void paintEvent(QPaintEvent* ev) override
- {
+ void paintEvent(QPaintEvent* ev) override {
}
+
void resizeEvent(QResizeEvent* ev) override {
- parent_->SetClientAreaWidth(size().width());
- parent_->SetClientAreaHeight(size().height());
+ parent->OnClientAreaResized(ev->size().width(), ev->size().height());
+ parent->OnFramebufferSizeChanged();
}
+
private:
- GRenderWindow* parent_;
+ GRenderWindow* parent;
};
EmuThread& GRenderWindow::GetEmuThread()
@@ -108,6 +120,9 @@ EmuThread& GRenderWindow::GetEmuThread()
GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0)
{
+ std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
+ setWindowTitle(QString::fromStdString(window_title));
+
keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps();
@@ -117,16 +132,25 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this
fmt.setProfile(QGLFormat::CoreProfile);
// Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
fmt.setOption(QGL::NoDeprecatedFunctions);
-
+
child = new GGLWidgetInternal(fmt, this);
QBoxLayout* layout = new QHBoxLayout(this);
resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
layout->addWidget(child);
layout->setMargin(0);
setLayout(layout);
- QObject::connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext()));
+ connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext()));
+
+ OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
+
+ OnFramebufferSizeChanged();
+ NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(child->width(), child->height()));
BackupGeometry();
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()));
+#endif
}
void GRenderWindow::moveContext()
@@ -169,14 +193,28 @@ void GRenderWindow::DoneCurrent()
}
void GRenderWindow::PollEvents() {
- // TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title
- // from the main thread, but this should probably be in an event handler...
- /*
- static char title[128];
- sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(),
- video_core::g_renderer->current_fps());
- setWindowTitle(title);
- */
+}
+
+// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
+//
+// Older versions get the window size (density independent pixels),
+// and hence, do not support DPI scaling ("retina" displays).
+// The result will be a viewport that is smaller than the extent of the window.
+void GRenderWindow::OnFramebufferSizeChanged()
+{
+ // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ // windowHandle() might not be accessible until the window is displayed to screen.
+ auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0;
+
+ unsigned width = child->QPaintDevice::width() * pixel_ratio;
+ unsigned height = child->QPaintDevice::height() * pixel_ratio;
+#else
+ unsigned width = child->QPaintDevice::width();
+ unsigned height = child->QPaintDevice::height();
+#endif
+
+ NotifyFramebufferSizeChanged(std::make_pair(width, height));
}
void GRenderWindow::BackupGeometry()
@@ -201,7 +239,7 @@ QByteArray GRenderWindow::saveGeometry()
{
// If we are a top-level widget, store the current geometry
// otherwise, store the last backup
- if (parent() == NULL)
+ if (parent() == nullptr)
return ((QGLWidget*)this)->saveGeometry();
else
return geometry;
@@ -239,3 +277,11 @@ void GRenderWindow::ReloadSetKeymaps()
KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN);
}
+void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height)
+{
+ NotifyClientAreaSizeChanged(std::make_pair(width, height));
+}
+
+void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
+ setMinimumSize(minimal_size.first, minimal_size.second);
+}
diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx
index f8afc403..5f69f15e 100644
--- a/src/citra_qt/bootmanager.hxx
+++ b/src/citra_qt/bootmanager.hxx
@@ -1,12 +1,16 @@
+#include <atomic>
+
#include <QThread>
#include <QGLWidget>
-#include <atomic>
+
#include "common/common.h"
#include "common/emu_window.h"
-class GRenderWindow;
+class QScreen;
class QKeyEvent;
+class GRenderWindow;
+
class EmuThread : public QThread
{
Q_OBJECT
@@ -14,7 +18,7 @@ class EmuThread : public QThread
public:
/**
* Set image filename
- *
+ *
* @param filename
* @warning Only call when not running!
*/
@@ -74,7 +78,7 @@ private:
signals:
/**
* Emitted when CPU when we've finished processing a single Gekko instruction
- *
+ *
* @warning This will only be emitted when the CPU is not running (SetCpuRunning(false))
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/
@@ -100,7 +104,7 @@ public:
void BackupGeometry();
void RestoreGeometry();
void restoreGeometry(const QByteArray& geometry); // overridden
- QByteArray saveGeometry(); // overridden
+ QByteArray saveGeometry(); // overridden
EmuThread& GetEmuThread();
@@ -109,10 +113,16 @@ public:
void ReloadSetKeymaps() override;
+ void OnClientAreaResized(unsigned width, unsigned height);
+
+ void OnFramebufferSizeChanged();
+
public slots:
- void moveContext();
+ void moveContext(); // overridden
private:
+ void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
+
QGLWidget* child;
EmuThread emu_thread;
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 63d39643..0ae6b8b2 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -21,7 +21,7 @@ Config::Config() {
Reload();
}
-void Config::ReadControls() {
+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();
@@ -41,9 +41,22 @@ void Config::ReadControls() {
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();
qt_config->endGroup();
+
+ qt_config->beginGroup("Core");
+ Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt();
+ Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt();
+ qt_config->endGroup();
+
+ qt_config->beginGroup("Data Storage");
+ Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
+ qt_config->endGroup();
+
+ qt_config->beginGroup("Miscellaneous");
+ Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
+ qt_config->endGroup();
}
-void Config::SaveControls() {
+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);
@@ -63,44 +76,27 @@ void Config::SaveControls() {
qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key);
qt_config->setValue("pad_sright", Settings::values.pad_sright_key);
qt_config->endGroup();
-}
-
-void Config::ReadCore() {
- qt_config->beginGroup("Core");
- Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt();
- Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt();
- qt_config->endGroup();
-}
-void Config::SaveCore() {
qt_config->beginGroup("Core");
qt_config->setValue("cpu_core", Settings::values.cpu_core);
qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate);
qt_config->endGroup();
-}
-void Config::ReadData() {
qt_config->beginGroup("Data Storage");
- Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
+ qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
qt_config->endGroup();
-}
-void Config::SaveData() {
- qt_config->beginGroup("Data Storage");
- qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
+ qt_config->beginGroup("Miscellaneous");
+ qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
qt_config->endGroup();
}
void Config::Reload() {
- ReadControls();
- ReadCore();
- ReadData();
+ ReadValues();
}
void Config::Save() {
- SaveControls();
- SaveCore();
- SaveData();
+ SaveValues();
}
Config::~Config() {
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h
index 782c2628..4c95d0cb 100644
--- a/src/citra_qt/config.h
+++ b/src/citra_qt/config.h
@@ -12,12 +12,8 @@ class Config {
QSettings* qt_config;
std::string qt_config_loc;
- void ReadControls();
- void SaveControls();
- void ReadCore();
- void SaveCore();
- void ReadData();
- void SaveData();
+ void ReadValues();
+ void SaveValues();
public:
Config();
~Config();
diff --git a/src/citra_qt/config/controller_config_util.cpp b/src/citra_qt/config/controller_config_util.cpp
index c5426570..aee3f861 100644
--- a/src/citra_qt/config/controller_config_util.cpp
+++ b/src/citra_qt/config/controller_config_util.cpp
@@ -48,7 +48,7 @@ void GKeyConfigButton::OnActivePortChanged(const common::Config::ControllerPort&
else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control");
else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt");
else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta");
- setText(text);
+ setText(text);
}
void GKeyConfigButton::OnClicked()
@@ -118,4 +118,4 @@ GButtonConfigGroup::GButtonConfigGroup(const QString& name, common::Config::Cont
setLayout(layout);
}
-*/ \ No newline at end of file
+*/
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index 77fb0c9e..895851be 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -28,7 +28,7 @@ void CallstackWidget::OnCPUStepped()
u32 sp = app_core->GetReg(13); //stack pointer
u32 addr, ret_addr, call_addr, func_addr;
-
+
int counter = 0;
for (int addr = 0x10000000; addr >= sp; addr -= 4)
{
@@ -55,7 +55,7 @@ void CallstackWidget::OnCPUStepped()
callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0'))));
-
+
name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown";
callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name))
.arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0')))));
@@ -63,4 +63,4 @@ void CallstackWidget::OnCPUStepped()
counter++;
}
}
-} \ No newline at end of file
+}
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp
new file mode 100644
index 00000000..53394b6e
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints.cpp
@@ -0,0 +1,261 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <QMetaType>
+#include <QPushButton>
+#include <QTreeWidget>
+#include <QVBoxLayout>
+#include <QLabel>
+
+#include "graphics_breakpoints.hxx"
+#include "graphics_breakpoints_p.hxx"
+
+BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
+ : QAbstractListModel(parent), context_weak(debug_context),
+ at_breakpoint(debug_context->at_breakpoint),
+ active_breakpoint(debug_context->active_breakpoint)
+{
+
+}
+
+int BreakPointModel::columnCount(const QModelIndex& parent) const
+{
+ return 2;
+}
+
+int BreakPointModel::rowCount(const QModelIndex& parent) const
+{
+ return static_cast<int>(Pica::DebugContext::Event::NumEvents);
+}
+
+QVariant BreakPointModel::data(const QModelIndex& index, int role) const
+{
+ const auto event = static_cast<Pica::DebugContext::Event>(index.row());
+
+ switch (role) {
+ case Qt::DisplayRole:
+ {
+ switch (index.column()) {
+ case 0:
+ {
+ std::map<Pica::DebugContext::Event, QString> map;
+ map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")});
+ map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")});
+ map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")});
+ map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")});
+
+ _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
+
+ return map[event];
+ }
+
+ case 1:
+ return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled");
+
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ case Qt::BackgroundRole:
+ {
+ if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
+ return QBrush(QColor(0xE0, 0xE0, 0x10));
+ }
+ break;
+ }
+
+ case Role_IsEnabled:
+ {
+ auto context = context_weak.lock();
+ return context && context->breakpoints[event].enabled;
+ }
+
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ switch(role) {
+ case Qt::DisplayRole:
+ {
+ if (section == 0) {
+ return tr("Event");
+ } else if (section == 1) {
+ return tr("Status");
+ }
+
+ break;
+ }
+ }
+
+ return QVariant();
+}
+
+bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
+{
+ const auto event = static_cast<Pica::DebugContext::Event>(index.row());
+
+ switch (role) {
+ case Role_IsEnabled:
+ {
+ auto context = context_weak.lock();
+ if (!context)
+ return false;
+
+ context->breakpoints[event].enabled = value.toBool();
+ QModelIndex changed_index = createIndex(index.row(), 1);
+ emit dataChanged(changed_index, changed_index);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
+{
+ auto context = context_weak.lock();
+ if (!context)
+ return;
+
+ active_breakpoint = context->active_breakpoint;
+ at_breakpoint = context->at_breakpoint;
+ emit dataChanged(createIndex(static_cast<int>(event), 0),
+ createIndex(static_cast<int>(event), 1));
+}
+
+void BreakPointModel::OnResumed()
+{
+ auto context = context_weak.lock();
+ if (!context)
+ return;
+
+ at_breakpoint = context->at_breakpoint;
+ emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
+ createIndex(static_cast<int>(active_breakpoint), 1));
+ active_breakpoint = context->active_breakpoint;
+}
+
+
+GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
+ QWidget* parent)
+ : QDockWidget(tr("Pica Breakpoints"), parent),
+ Pica::DebugContext::BreakPointObserver(debug_context)
+{
+ setObjectName("PicaBreakPointsWidget");
+
+ status_text = new QLabel(tr("Emulation running"));
+ resume_button = new QPushButton(tr("Resume"));
+ resume_button->setEnabled(false);
+
+ breakpoint_model = new BreakPointModel(debug_context, this);
+ breakpoint_list = new QTreeView;
+ breakpoint_list->setModel(breakpoint_model);
+
+ toggle_breakpoint_button = new QPushButton(tr("Enable"));
+ toggle_breakpoint_button->setEnabled(false);
+
+ qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
+
+ connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
+
+ connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
+ this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
+ Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
+
+ connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
+ breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)),
+ Qt::BlockingQueuedConnection);
+ connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
+
+ connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)),
+ breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)));
+
+ connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(OnBreakpointSelectionChanged(QModelIndex)));
+
+ connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled()));
+
+ QWidget* main_widget = new QWidget;
+ auto main_layout = new QVBoxLayout;
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(status_text);
+ sub_layout->addWidget(resume_button);
+ main_layout->addLayout(sub_layout);
+ }
+ main_layout->addWidget(breakpoint_list);
+ main_layout->addWidget(toggle_breakpoint_button);
+ main_widget->setLayout(main_layout);
+
+ setWidget(main_widget);
+}
+
+void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data)
+{
+ // Process in GUI thread
+ emit BreakPointHit(event, data);
+}
+
+void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
+{
+ status_text->setText(tr("Emulation halted at breakpoint"));
+ resume_button->setEnabled(true);
+}
+
+void GraphicsBreakPointsWidget::OnPicaResume()
+{
+ // Process in GUI thread
+ emit Resumed();
+}
+
+void GraphicsBreakPointsWidget::OnResumed()
+{
+ status_text->setText(tr("Emulation running"));
+ resume_button->setEnabled(false);
+}
+
+void GraphicsBreakPointsWidget::OnResumeRequested()
+{
+ if (auto context = context_weak.lock())
+ context->Resume();
+}
+
+void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index)
+{
+ if (!index.isValid()) {
+ toggle_breakpoint_button->setEnabled(false);
+ return;
+ }
+
+ toggle_breakpoint_button->setEnabled(true);
+ UpdateToggleBreakpointButton(index);
+}
+
+void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled()
+{
+ QModelIndex index = breakpoint_list->selectionModel()->currentIndex();
+ bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool());
+
+ breakpoint_model->setData(index, new_state,
+ BreakPointModel::Role_IsEnabled);
+ UpdateToggleBreakpointButton(index);
+}
+
+void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index)
+{
+ if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) {
+ toggle_breakpoint_button->setText(tr("Disable"));
+ } else {
+ toggle_breakpoint_button->setText(tr("Enable"));
+ }
+}
diff --git a/src/citra_qt/debugger/graphics_breakpoints.hxx b/src/citra_qt/debugger/graphics_breakpoints.hxx
new file mode 100644
index 00000000..2142c6fa
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints.hxx
@@ -0,0 +1,53 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include <QAbstractListModel>
+#include <QDockWidget>
+
+#include "video_core/debug_utils/debug_utils.h"
+
+class QLabel;
+class QPushButton;
+class QTreeView;
+
+class BreakPointModel;
+
+class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver {
+ Q_OBJECT
+
+ using Event = Pica::DebugContext::Event;
+
+public:
+ GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
+ QWidget* parent = nullptr);
+
+ void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
+ void OnPicaResume() override;
+
+public slots:
+ void OnBreakPointHit(Pica::DebugContext::Event event, void* data);
+ void OnResumeRequested();
+ void OnResumed();
+ void OnBreakpointSelectionChanged(const QModelIndex&);
+ void OnToggleBreakpointEnabled();
+
+signals:
+ void Resumed();
+ void BreakPointHit(Pica::DebugContext::Event event, void* data);
+ void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
+private:
+ void UpdateToggleBreakpointButton(const QModelIndex& index);
+
+ QLabel* status_text;
+ QPushButton* resume_button;
+ QPushButton* toggle_breakpoint_button;
+
+ BreakPointModel* breakpoint_model;
+ QTreeView* breakpoint_list;
+};
diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.hxx b/src/citra_qt/debugger/graphics_breakpoints_p.hxx
new file mode 100644
index 00000000..bf5daf73
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints_p.hxx
@@ -0,0 +1,38 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include <QAbstractListModel>
+
+#include "video_core/debug_utils/debug_utils.h"
+
+class BreakPointModel : public QAbstractListModel {
+ Q_OBJECT
+
+public:
+ enum {
+ Role_IsEnabled = Qt::UserRole,
+ };
+
+ BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent);
+
+ 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;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+ bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
+
+public slots:
+ void OnBreakPointHit(Pica::DebugContext::Event event);
+ void OnResumed();
+
+private:
+ std::weak_ptr<Pica::DebugContext> context_weak;
+ bool at_breakpoint;
+ Pica::DebugContext::Event active_breakpoint;
+};
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index 71dd166c..7f97cf14 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -2,30 +2,171 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
+#include <QLabel>
#include <QListView>
+#include <QMainWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QTreeView>
+#include <QSpinBox>
+#include <QComboBox>
+
+#include "video_core/pica.h"
+#include "video_core/math.h"
+
+#include "video_core/debug_utils/debug_utils.h"
#include "graphics_cmdlists.hxx"
-GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent)
-{
+#include "util/spinbox.hxx"
+
+QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
+ QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
+ for (int y = 0; y < info.height; ++y) {
+ for (int x = 0; x < info.width; ++x) {
+ Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info);
+ decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
+ }
+ }
+
+ return decoded_image;
+}
+
+class TextureInfoWidget : public QWidget {
+public:
+ TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) {
+ QLabel* image_widget = new QLabel;
+ QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
+ image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ image_widget->setPixmap(image_pixmap);
+
+ QVBoxLayout* layout = new QVBoxLayout;
+ layout->addWidget(image_widget);
+ setLayout(layout);
+ }
+};
+
+TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent)
+ : QDockWidget(tr("Texture 0x%1").arg(info.address, 8, 16, QLatin1Char('0'))),
+ info(info) {
+
+ QWidget* main_widget = new QWidget;
+
+ QLabel* image_widget = new QLabel;
+
+ connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&)));
+
+ CSpinBox* phys_address_spinbox = new CSpinBox;
+ phys_address_spinbox->SetBase(16);
+ phys_address_spinbox->SetRange(0, 0xFFFFFFFF);
+ phys_address_spinbox->SetPrefix("0x");
+ phys_address_spinbox->SetValue(info.address);
+ connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64)));
+
+ QComboBox* format_choice = new QComboBox;
+ format_choice->addItem(tr("RGBA8"));
+ format_choice->addItem(tr("RGB8"));
+ format_choice->addItem(tr("RGBA5551"));
+ format_choice->addItem(tr("RGB565"));
+ format_choice->addItem(tr("RGBA4"));
+ format_choice->setCurrentIndex(static_cast<int>(info.format));
+ connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int)));
+
+ QSpinBox* width_spinbox = new QSpinBox;
+ width_spinbox->setMaximum(65535);
+ width_spinbox->setValue(info.width);
+ connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int)));
+
+ QSpinBox* height_spinbox = new QSpinBox;
+ height_spinbox->setMaximum(65535);
+ height_spinbox->setValue(info.height);
+ connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int)));
+
+ QSpinBox* stride_spinbox = new QSpinBox;
+ stride_spinbox->setMaximum(65535 * 4);
+ stride_spinbox->setValue(info.stride);
+ connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int)));
+
+ QVBoxLayout* main_layout = new QVBoxLayout;
+ main_layout->addWidget(image_widget);
+
+ {
+ QHBoxLayout* sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Source Address:")));
+ sub_layout->addWidget(phys_address_spinbox);
+ main_layout->addLayout(sub_layout);
+ }
+
+ {
+ QHBoxLayout* sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Format")));
+ sub_layout->addWidget(format_choice);
+ main_layout->addLayout(sub_layout);
+ }
+
+ {
+ QHBoxLayout* sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Width:")));
+ sub_layout->addWidget(width_spinbox);
+ sub_layout->addStretch();
+ sub_layout->addWidget(new QLabel(tr("Height:")));
+ sub_layout->addWidget(height_spinbox);
+ sub_layout->addStretch();
+ sub_layout->addWidget(new QLabel(tr("Stride:")));
+ sub_layout->addWidget(stride_spinbox);
+ main_layout->addLayout(sub_layout);
+ }
+
+ main_widget->setLayout(main_layout);
+
+ emit UpdatePixmap(ReloadPixmap());
+
+ setWidget(main_widget);
+}
+
+void TextureInfoDockWidget::OnAddressChanged(qint64 value) {
+ info.address = value;
+ emit UpdatePixmap(ReloadPixmap());
+}
+
+void TextureInfoDockWidget::OnFormatChanged(int value) {
+ info.format = static_cast<Pica::Regs::TextureFormat>(value);
+ emit UpdatePixmap(ReloadPixmap());
+}
+
+void TextureInfoDockWidget::OnWidthChanged(int value) {
+ info.width = value;
+ emit UpdatePixmap(ReloadPixmap());
+}
+
+void TextureInfoDockWidget::OnHeightChanged(int value) {
+ info.height = value;
+ emit UpdatePixmap(ReloadPixmap());
+}
+void TextureInfoDockWidget::OnStrideChanged(int value) {
+ info.stride = value;
+ emit UpdatePixmap(ReloadPixmap());
}
-int GPUCommandListModel::rowCount(const QModelIndex& parent) const
-{
+QPixmap TextureInfoDockWidget::ReloadPixmap() const {
+ u8* src = Memory::GetPointer(info.address);
+ return QPixmap::fromImage(LoadTexture(src, info));
+}
+
+GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
+
+}
+
+int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
return pica_trace.writes.size();
}
-int GPUCommandListModel::columnCount(const QModelIndex& parent) const
-{
+int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
return 2;
}
-QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const
-{
+QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
if (!index.isValid())
return QVariant();
@@ -36,21 +177,39 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const
if (role == Qt::DisplayRole) {
QString content;
if (index.column() == 0) {
- content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str());
+ QString content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str());
content.append(" ");
+ return content;
} else if (index.column() == 1) {
- content.append(QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0')));
+ QString content = QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0'));
content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0')));
+ return content;
}
+ } else if (role == CommandIdRole) {
+ return QVariant::fromValue<int>(cmd.cmd_id.Value());
+ }
- return QVariant(content);
+ return QVariant();
+}
+
+QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const {
+ switch(role) {
+ case Qt::DisplayRole:
+ {
+ if (section == 0) {
+ return tr("Command Name");
+ } else if (section == 1) {
+ return tr("Data");
+ }
+
+ break;
+ }
}
return QVariant();
}
-void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace)
-{
+void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) {
beginResetModel();
pica_trace = trace;
@@ -58,38 +217,82 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
endResetModel();
}
+#define COMMAND_IN_RANGE(cmd_id, reg_name) \
+ (cmd_id >= PICA_REG_INDEX(reg_name) && \
+ cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::registers.reg_name)) / 4)
+
+void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
+ const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt();
+ if (COMMAND_IN_RANGE(command_id, texture0)) {
+ auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0,
+ Pica::registers.texture0_format);
+
+ // TODO: Instead, emit a signal here to be caught by the main window widget.
+ auto main_window = static_cast<QMainWindow*>(parent());
+ main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window));
+ }
+}
+
+void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
+ QWidget* new_info_widget;
+
+ const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt();
+ if (COMMAND_IN_RANGE(command_id, texture0)) {
+ u8* src = Memory::GetPointer(Pica::registers.texture0.GetPhysicalAddress());
+ auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0,
+ Pica::registers.texture0_format);
+ new_info_widget = new TextureInfoWidget(src, info);
+ } else {
+ new_info_widget = new QWidget;
+ }
+
+ widget()->layout()->removeWidget(command_info_widget);
+ delete command_info_widget;
+ widget()->layout()->addWidget(new_info_widget);
+ command_info_widget = new_info_widget;
+}
+#undef COMMAND_IN_RANGE
-GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent)
-{
+GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) {
+ setObjectName("Pica Command List");
GPUCommandListModel* model = new GPUCommandListModel(this);
QWidget* main_widget = new QWidget;
- QTreeView* list_widget = new QTreeView;
+ list_widget = new QTreeView;
list_widget->setModel(model);
list_widget->setFont(QFont("monospace"));
list_widget->setRootIsDecorated(false);
- QPushButton* toggle_tracing = new QPushButton(tr("Start Tracing"));
+ connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),
+ this, SLOT(SetCommandInfo(const QModelIndex&)));
+ connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)),
+ this, SLOT(OnCommandDoubleClicked(const QModelIndex&)));
+
+ toggle_tracing = new QPushButton(tr("Start Tracing"));
connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)),
model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
+ command_info_widget = new QWidget;
+
QVBoxLayout* main_layout = new QVBoxLayout;
main_layout->addWidget(list_widget);
main_layout->addWidget(toggle_tracing);
+ main_layout->addWidget(command_info_widget);
main_widget->setLayout(main_layout);
setWidget(main_widget);
}
-void GPUCommandListWidget::OnToggleTracing()
-{
+void GPUCommandListWidget::OnToggleTracing() {
if (!Pica::DebugUtils::IsPicaTracing()) {
Pica::DebugUtils::StartPicaTracing();
+ toggle_tracing->setText(tr("Finish Tracing"));
} else {
pica_trace = Pica::DebugUtils::FinishPicaTracing();
emit TracingFinished(*pica_trace);
+ toggle_tracing->setText(tr("Start Tracing"));
}
}
diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx
index 1523e724..a459bba6 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.hxx
+++ b/src/citra_qt/debugger/graphics_cmdlists.hxx
@@ -10,16 +10,24 @@
#include "video_core/gpu_debugger.h"
#include "video_core/debug_utils/debug_utils.h"
+class QPushButton;
+class QTreeView;
+
class GPUCommandListModel : public QAbstractListModel
{
Q_OBJECT
public:
+ enum {
+ CommandIdRole = Qt::UserRole,
+ };
+
GPUCommandListModel(QObject* parent);
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;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
public slots:
void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
@@ -37,10 +45,39 @@ public:
public slots:
void OnToggleTracing();
+ void OnCommandDoubleClicked(const QModelIndex&);
+
+ void SetCommandInfo(const QModelIndex&);
signals:
void TracingFinished(const Pica::DebugUtils::PicaTrace&);
private:
std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
+
+ QTreeView* list_widget;
+ QWidget* command_info_widget;
+ QPushButton* toggle_tracing;
+};
+
+class TextureInfoDockWidget : public QDockWidget {
+ Q_OBJECT
+
+public:
+ TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr);
+
+signals:
+ void UpdatePixmap(const QPixmap& pixmap);
+
+private slots:
+ void OnAddressChanged(qint64 value);
+ void OnFormatChanged(int value);
+ void OnWidthChanged(int value);
+ void OnHeightChanged(int value);
+ void OnStrideChanged(int value);
+
+private:
+ QPixmap ReloadPixmap() const;
+
+ Pica::DebugUtils::TextureInfo info;
};
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
new file mode 100644
index 00000000..ac47f298
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_framebuffer.cpp
@@ -0,0 +1,282 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <QBoxLayout>
+#include <QComboBox>
+#include <QDebug>
+#include <QLabel>
+#include <QMetaType>
+#include <QPushButton>
+#include <QSpinBox>
+
+#include "video_core/pica.h"
+
+#include "graphics_framebuffer.hxx"
+
+#include "util/spinbox.hxx"
+
+BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
+ const QString& title, QWidget* parent)
+ : QDockWidget(title, parent), BreakPointObserver(debug_context)
+{
+ qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
+
+ connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
+
+ // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
+ // care of delaying its handling to the GUI thread.
+ connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
+ this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
+ Qt::BlockingQueuedConnection);
+}
+
+void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data)
+{
+ emit BreakPointHit(event, data);
+}
+
+void BreakPointObserverDock::OnPicaResume()
+{
+ emit Resumed();
+}
+
+
+GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
+ QWidget* parent)
+ : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
+ framebuffer_source(Source::PicaTarget)
+{
+ setObjectName("PicaFramebuffer");
+
+ framebuffer_source_list = new QComboBox;
+ framebuffer_source_list->addItem(tr("Active Render Target"));
+ framebuffer_source_list->addItem(tr("Custom"));
+ framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
+
+ framebuffer_address_control = new CSpinBox;
+ framebuffer_address_control->SetBase(16);
+ framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
+ framebuffer_address_control->SetPrefix("0x");
+
+ framebuffer_width_control = new QSpinBox;
+ framebuffer_width_control->setMinimum(1);
+ framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
+
+ framebuffer_height_control = new QSpinBox;
+ framebuffer_height_control->setMinimum(1);
+ framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
+
+ 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("RGB565"));
+ framebuffer_format_control->addItem(tr("RGBA4"));
+
+ // TODO: This QLabel should shrink the image to the available space rather than just expanding...
+ framebuffer_picture_label = new QLabel;
+
+ auto enlarge_button = new QPushButton(tr("Enlarge"));
+
+ // Connections
+ connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
+ connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
+ connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
+ connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
+ connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
+ connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
+
+ auto main_widget = new QWidget;
+ auto main_layout = new QVBoxLayout;
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Source:")));
+ sub_layout->addWidget(framebuffer_source_list);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
+ sub_layout->addWidget(framebuffer_address_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Width:")));
+ sub_layout->addWidget(framebuffer_width_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Height:")));
+ sub_layout->addWidget(framebuffer_height_control);
+ main_layout->addLayout(sub_layout);
+ }
+ {
+ auto sub_layout = new QHBoxLayout;
+ sub_layout->addWidget(new QLabel(tr("Format:")));
+ sub_layout->addWidget(framebuffer_format_control);
+ main_layout->addLayout(sub_layout);
+ }
+ main_layout->addWidget(framebuffer_picture_label);
+ main_layout->addWidget(enlarge_button);
+ main_widget->setLayout(main_layout);
+ setWidget(main_widget);
+
+ // Load current data - TODO: Make sure this works when emulation is not running
+ emit Update();
+ widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
+}
+
+void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
+{
+ emit Update();
+ widget()->setEnabled(true);
+}
+
+void GraphicsFramebufferWidget::OnResumed()
+{
+ widget()->setEnabled(false);
+}
+
+void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
+{
+ framebuffer_source = static_cast<Source>(new_value);
+ emit Update();
+}
+
+void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
+{
+ if (framebuffer_address != new_value) {
+ framebuffer_address = static_cast<unsigned>(new_value);
+
+ framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
+{
+ if (framebuffer_width != new_value) {
+ framebuffer_width = new_value;
+
+ framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
+{
+ if (framebuffer_height != new_value) {
+ framebuffer_height = new_value;
+
+ framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
+{
+ if (framebuffer_format != static_cast<Format>(new_value)) {
+ framebuffer_format = static_cast<Format>(new_value);
+
+ framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
+ emit Update();
+ }
+}
+
+void GraphicsFramebufferWidget::OnUpdate()
+{
+ QPixmap pixmap;
+
+ switch (framebuffer_source) {
+ case Source::PicaTarget:
+ {
+ // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
+
+ auto framebuffer = Pica::registers.framebuffer;
+ using Framebuffer = decltype(framebuffer);
+
+ framebuffer_address = framebuffer.GetColorBufferAddress();
+ framebuffer_width = framebuffer.GetWidth();
+ framebuffer_height = framebuffer.GetHeight();
+ framebuffer_format = static_cast<Format>(framebuffer.color_format);
+
+ break;
+ }
+
+ case Source::Custom:
+ {
+ // Keep user-specified values
+ break;
+ }
+
+ default:
+ qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
+ break;
+ }
+
+ // 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(framebuffer_address);
+ for (int y = 0; y < framebuffer_height; ++y) {
+ for (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*/));
+ }
+ }
+ pixmap = QPixmap::fromImage(decoded_image);
+ break;
+ }
+
+ case Format::RGB8:
+ {
+ QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
+ u8* color_buffer = Memory::GetPointer(framebuffer_address);
+ for (int y = 0; y < framebuffer_height; ++y) {
+ for (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*/));
+ }
+ }
+ 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(framebuffer_address);
+ for (int y = 0; y < framebuffer_height; ++y) {
+ for (int x = 0; x < framebuffer_width; ++x) {
+ u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2);
+ u8 r = (value >> 11) & 0x1F;
+ u8 g = (value >> 6) & 0x1F;
+ u8 b = (value >> 1) & 0x1F;
+ u8 a = 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;
+ }
+
+ framebuffer_address_control->SetValue(framebuffer_address);
+ framebuffer_width_control->setValue(framebuffer_width);
+ framebuffer_height_control->setValue(framebuffer_height);
+ framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
+ framebuffer_picture_label->setPixmap(pixmap);
+}
diff --git a/src/citra_qt/debugger/graphics_framebuffer.hxx b/src/citra_qt/debugger/graphics_framebuffer.hxx
new file mode 100644
index 00000000..1151ee7a
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_framebuffer.hxx
@@ -0,0 +1,92 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QDockWidget>
+
+#include "video_core/debug_utils/debug_utils.h"
+
+class QComboBox;
+class QLabel;
+class QSpinBox;
+
+class CSpinBox;
+
+// Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots.
+// This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
+// the widget usually wants to perform reactions in the GUI thread.
+class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver {
+ Q_OBJECT
+
+public:
+ BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title,
+ QWidget* parent = nullptr);
+
+ void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
+ void OnPicaResume() override;
+
+private slots:
+ virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0;
+ virtual void OnResumed() = 0;
+
+signals:
+ void Resumed();
+ void BreakPointHit(Pica::DebugContext::Event event, void* data);
+};
+
+class GraphicsFramebufferWidget : public BreakPointObserverDock {
+ Q_OBJECT
+
+ using Event = Pica::DebugContext::Event;
+
+ enum class Source {
+ PicaTarget = 0,
+ Custom = 1,
+
+ // TODO: Add GPU framebuffer sources!
+ };
+
+ enum class Format {
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGBA5551 = 2,
+ RGB565 = 3,
+ RGBA4 = 4,
+ };
+
+public:
+ GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
+
+public slots:
+ void OnFramebufferSourceChanged(int new_value);
+ void OnFramebufferAddressChanged(qint64 new_value);
+ void OnFramebufferWidthChanged(int new_value);
+ void OnFramebufferHeightChanged(int new_value);
+ void OnFramebufferFormatChanged(int new_value);
+ void OnUpdate();
+
+private slots:
+ void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
+ void OnResumed() override;
+
+signals:
+ void Update();
+
+private:
+
+ QComboBox* framebuffer_source_list;
+ CSpinBox* framebuffer_address_control;
+ QSpinBox* framebuffer_width_control;
+ QSpinBox* framebuffer_height_control;
+ QComboBox* framebuffer_format_control;
+
+ QLabel* framebuffer_picture_label;
+
+ Source framebuffer_source;
+ unsigned framebuffer_address;
+ unsigned framebuffer_width;
+ unsigned framebuffer_height;
+ Format framebuffer_format;
+};
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp
index 96ceed48..ed17ee4b 100644
--- a/src/citra_qt/debugger/registers.cpp
+++ b/src/citra_qt/debugger/registers.cpp
@@ -46,18 +46,18 @@ void RegistersWidget::OnCPUStepped()
CSPR->setText(1, QString("0x%1").arg(app_core->GetCPSR(), 8, 16, QLatin1Char('0')));
CSPR->child(0)->setText(1, QString("b%1").arg(app_core->GetCPSR() & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
- CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State
- CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable
- CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable
- CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort
- CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess
- CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM)
- CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal
- CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify
- CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state
- CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow
- CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow
- CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend
- CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero
- CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
+ CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State
+ CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable
+ CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable
+ CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort
+ CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess
+ CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM)
+ CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal
+ CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify
+ CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state
+ CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow
+ CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow
+ CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend
+ CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero
+ CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
}
diff --git a/src/citra_qt/debugger/registers.hxx b/src/citra_qt/debugger/registers.hxx
index 9645feb2..4cca957c 100644
--- a/src/citra_qt/debugger/registers.hxx
+++ b/src/citra_qt/debugger/registers.hxx
@@ -16,10 +16,10 @@ public slots:
void OnCPUStepped();
private:
- Ui::ARMRegisters cpu_regs_ui;
+ Ui::ARMRegisters cpu_regs_ui;
- QTreeWidget* tree;
-
- QTreeWidgetItem* registers;
- QTreeWidgetItem* CSPR;
+ QTreeWidget* tree;
+
+ QTreeWidgetItem* registers;
+ QTreeWidgetItem* CSPR;
};
diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp
index bbaa4a8d..5d0b52e4 100644
--- a/src/citra_qt/hotkeys.cpp
+++ b/src/citra_qt/hotkeys.cpp
@@ -5,7 +5,7 @@
struct Hotkey
{
- Hotkey() : shortcut(NULL), context(Qt::WindowShortcut) {}
+ Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
QKeySequence keyseq;
QShortcut* shortcut;
@@ -81,7 +81,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut)
- hk.shortcut = new QShortcut(hk.keyseq, widget, NULL, NULL, hk.context);
+ hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
return hk.shortcut;
}
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 304c169b..23d4925b 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -1,3 +1,5 @@
+#include <thread>
+
#include <QtGui>
#include <QDesktopWidget>
#include <QFileDialog>
@@ -5,8 +7,13 @@
#include "main.hxx"
#include "common/common.h"
+#include "common/logging/text_formatter.h"
+#include "common/logging/log.h"
+#include "common/logging/backend.h"
+#include "common/logging/filter.h"
#include "common/platform.h"
-#include "common/log_manager.h"
+#include "common/scope_exit.h"
+
#if EMU_PLATFORM == PLATFORM_LINUX
#include <unistd.h>
#endif
@@ -20,8 +27,11 @@
#include "debugger/callstack.hxx"
#include "debugger/ramview.hxx"
#include "debugger/graphics.hxx"
+#include "debugger/graphics_breakpoints.hxx"
#include "debugger/graphics_cmdlists.hxx"
+#include "debugger/graphics_framebuffer.hxx"
+#include "core/settings.h"
#include "core/system.h"
#include "core/core.h"
#include "core/loader/loader.h"
@@ -30,10 +40,10 @@
#include "version.h"
-
GMainWindow::GMainWindow()
{
- LogManager::Init();
+ Pica::g_debug_context = Pica::DebugContext::Construct();
+
Config config;
ui.setupUi(this);
@@ -62,12 +72,22 @@ GMainWindow::GMainWindow()
addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
graphicsCommandsWidget->hide();
+ auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
+ addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
+ graphicsBreakpointsWidget->hide();
+
+ auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this);
+ addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget);
+ graphicsFramebufferWidget->hide();
+
QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
debug_menu->addAction(disasmWidget->toggleViewAction());
debug_menu->addAction(registersWidget->toggleViewAction());
debug_menu->addAction(callstackWidget->toggleViewAction());
debug_menu->addAction(graphicsWidget->toggleViewAction());
debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
+ debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
+ debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
// Set default UI state
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
@@ -112,7 +132,8 @@ GMainWindow::GMainWindow()
connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile()));
connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame()));
- setWindowTitle(render_window->GetWindowTitle().c_str());
+ std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
+ setWindowTitle(window_title.c_str());
show();
@@ -125,24 +146,20 @@ GMainWindow::GMainWindow()
GMainWindow::~GMainWindow()
{
// will get automatically deleted otherwise
- if (render_window->parent() == NULL)
+ if (render_window->parent() == nullptr)
delete render_window;
+
+ Pica::g_debug_context.reset();
}
void GMainWindow::BootGame(std::string filename)
{
- NOTICE_LOG(MASTER_LOG, "Citra starting...\n");
+ LOG_INFO(Frontend, "Citra starting...\n");
System::Init(render_window);
- if (Core::Init()) {
- ERROR_LOG(MASTER_LOG, "Core initialization failed, exiting...");
- Core::Stop();
- exit(1);
- }
-
// Load a game or die...
if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
- ERROR_LOG(BOOT, "Failed to load ROM!");
+ LOG_CRITICAL(Frontend, "Failed to load ROM!");
}
disasmWidget->Init();
@@ -158,7 +175,7 @@ void GMainWindow::BootGame(std::string filename)
void GMainWindow::OnMenuLoadFile()
{
- QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.elf *.axf *.bin *.cci *.cxi)"));
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
if (filename.size())
BootGame(filename.toLatin1().data());
}
@@ -207,14 +224,14 @@ void GMainWindow::OnOpenHotkeysDialog()
void GMainWindow::ToggleWindowMode()
{
bool enable = ui.action_Popout_Window_Mode->isChecked();
- if (enable && render_window->parent() != NULL)
+ if (enable && render_window->parent() != nullptr)
{
ui.horizontalLayout->removeWidget(render_window);
- render_window->setParent(NULL);
+ render_window->setParent(nullptr);
render_window->setVisible(true);
render_window->RestoreGeometry();
}
- else if (!enable && render_window->parent() == NULL)
+ else if (!enable && render_window->parent() == nullptr)
{
render_window->BackupGeometry();
ui.horizontalLayout->addWidget(render_window);
@@ -249,9 +266,21 @@ void GMainWindow::closeEvent(QCloseEvent* event)
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);
+ SCOPE_EXIT({
+ logger->Close();
+ logging_thread.join();
+ });
+
QApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication app(argc, argv);
+
GMainWindow main_window;
+ // After settings have been loaded by GMainWindow, apply the filter
+ log_filter.ParseFilterString(Settings::values.log_filter);
+
main_window.show();
return app.exec();
}
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp
new file mode 100644
index 00000000..9672168f
--- /dev/null
+++ b/src/citra_qt/util/spinbox.cpp
@@ -0,0 +1,303 @@
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <QLineEdit>
+#include <QRegExpValidator>
+
+#include "common/log.h"
+
+#include "spinbox.hxx"
+
+CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), base(10), min_value(-100), max_value(100), value(0), num_digits(0)
+{
+ // TODO: Might be nice to not immediately call the slot.
+ // Think of an address that is being replaced by a different one, in which case a lot
+ // invalid intermediate addresses would be read from during editing.
+ connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(OnEditingFinished()));
+
+ UpdateText();
+}
+
+void CSpinBox::SetValue(qint64 val)
+{
+ auto old_value = value;
+ value = std::max(std::min(val, max_value), min_value);
+
+ if (old_value != value) {
+ UpdateText();
+ emit ValueChanged(value);
+ }
+}
+
+void CSpinBox::SetRange(qint64 min, qint64 max)
+{
+ min_value = min;
+ max_value = max;
+
+ SetValue(value);
+ UpdateText();
+}
+
+void CSpinBox::stepBy(int steps)
+{
+ auto new_value = value;
+ // Scale number of steps by the currently selected digit
+ // TODO: Move this code elsewhere and enable it.
+ // TODO: Support for num_digits==0, too
+ // TODO: Support base!=16, too
+ // TODO: Make the cursor not jump back to the end of the line...
+ /*if (base == 16 && num_digits > 0) {
+ int digit = num_digits - (lineEdit()->cursorPosition() - prefix.length()) - 1;
+ digit = std::max(0, std::min(digit, num_digits - 1));
+ steps <<= digit * 4;
+ }*/
+
+ // Increment "new_value" by "steps", and perform annoying overflow checks, too.
+ if (steps < 0 && new_value + steps > new_value) {
+ new_value = std::numeric_limits<qint64>::min();
+ } else if (steps > 0 && new_value + steps < new_value) {
+ new_value = std::numeric_limits<qint64>::max();
+ } else {
+ new_value += steps;
+ }
+
+ SetValue(new_value);
+ UpdateText();
+}
+
+QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
+{
+ StepEnabled ret = StepNone;
+
+ if (value > min_value)
+ ret |= StepDownEnabled;
+
+ if (value < max_value)
+ ret |= StepUpEnabled;
+
+ return ret;
+}
+
+void CSpinBox::SetBase(int base)
+{
+ this->base = base;
+
+ UpdateText();
+}
+
+void CSpinBox::SetNumDigits(int num_digits)
+{
+ this->num_digits = num_digits;
+
+ UpdateText();
+}
+
+void CSpinBox::SetPrefix(const QString& prefix)
+{
+ this->prefix = prefix;
+
+ UpdateText();
+}
+
+void CSpinBox::SetSuffix(const QString& suffix)
+{
+ this->suffix = suffix;
+
+ UpdateText();
+}
+
+static QString StringToInputMask(const QString& input) {
+ QString mask = input;
+
+ // ... replace any special characters by their escaped counterparts ...
+ mask.replace("\\", "\\\\");
+ mask.replace("A", "\\A");
+ mask.replace("a", "\\a");
+ mask.replace("N", "\\N");
+ mask.replace("n", "\\n");
+ mask.replace("X", "\\X");
+ mask.replace("x", "\\x");
+ mask.replace("9", "\\9");
+ mask.replace("0", "\\0");
+ mask.replace("D", "\\D");
+ mask.replace("d", "\\d");
+ mask.replace("#", "\\#");
+ mask.replace("H", "\\H");
+ mask.replace("h", "\\h");
+ mask.replace("B", "\\B");
+ mask.replace("b", "\\b");
+ mask.replace(">", "\\>");
+ mask.replace("<", "\\<");
+ mask.replace("!", "\\!");
+
+ return mask;
+}
+
+void CSpinBox::UpdateText()
+{
+ // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
+ // input mask.
+ QString mask;
+ if (num_digits != 0) {
+ mask += StringToInputMask(prefix);
+
+ // For base 10 and negative range, demand a single sign character
+ if (HasSign())
+ mask += "X"; // identified as "-" or "+" in the validator
+
+ // Uppercase digits greater than 9.
+ mask += ">";
+
+ // The greatest signed 64-bit number has 19 decimal digits.
+ // TODO: Could probably make this more generic with some logarithms.
+ // For reference, unsigned 64-bit can have up to 20 decimal digits.
+ int digits = (num_digits != 0) ? num_digits
+ : (base == 16) ? 16
+ : (base == 10) ? 19
+ : 0xFF; // fallback case...
+
+ // Match num_digits digits
+ // Digits irrelevant to the chosen number base are filtered in the validator
+ mask += QString("H").repeated(std::max(num_digits, 1));
+
+ // Switch off case conversion
+ mask += "!";
+
+ mask += StringToInputMask(suffix);
+ }
+ lineEdit()->setInputMask(mask);
+
+ // Set new text without changing the cursor position. This will cause the cursor to briefly
+ // appear at the end of the line and then to jump back to its original position. That's
+ // a bit ugly, but better than having setText() move the cursor permanently all the time.
+ int cursor_position = lineEdit()->cursorPosition();
+ lineEdit()->setText(TextFromValue());
+ lineEdit()->setCursorPosition(cursor_position);
+}
+
+QString CSpinBox::TextFromValue()
+{
+ return prefix
+ + QString(HasSign() ? ((value < 0) ? "-" : "+") : "")
+ + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper()
+ + suffix;
+}
+
+qint64 CSpinBox::ValueFromText()
+{
+ unsigned strpos = prefix.length();
+
+ QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
+ return num_string.toLongLong(nullptr, base);
+}
+
+bool CSpinBox::HasSign() const
+{
+ return base == 10 && min_value < 0;
+}
+
+void CSpinBox::OnEditingFinished()
+{
+ // Only update for valid input
+ QString input = lineEdit()->text();
+ int pos = 0;
+ if (QValidator::Acceptable == validate(input, pos))
+ SetValue(ValueFromText());
+}
+
+QValidator::State CSpinBox::validate(QString& input, int& pos) const
+{
+ if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
+ return QValidator::Invalid;
+
+ unsigned strpos = prefix.length();
+
+ // Empty "numbers" allowed as intermediate values
+ if (strpos >= input.length() - HasSign() - suffix.length())
+ return QValidator::Intermediate;
+
+ _dbg_assert_(Frontend, base <= 10 || base == 16);
+ QString regexp;
+
+ // Demand sign character for negative ranges
+ if (HasSign())
+ regexp += "[+\\-]";
+
+ // Match digits corresponding to the chosen number base.
+ regexp += QString("[0-%1").arg(std::min(base, 9));
+ if (base == 16) {
+ regexp += "a-fA-F";
+ }
+ regexp += "]";
+
+ // Specify number of digits
+ if (num_digits > 0) {
+ regexp += QString("{%1}").arg(num_digits);
+ } else {
+ regexp += "+";
+ }
+
+ // Match string
+ QRegExp num_regexp(regexp);
+ int num_pos = strpos;
+ QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length());
+
+ if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0)
+ return QValidator::Invalid;
+
+ sub_input = sub_input.left(num_regexp.matchedLength());
+ bool ok;
+ qint64 val = sub_input.toLongLong(&ok, base);
+
+ if (!ok)
+ return QValidator::Invalid;
+
+ // Outside boundaries => don't accept
+ if (val < min_value || val > max_value)
+ return QValidator::Invalid;
+
+ // Make sure we are actually at the end of this string...
+ strpos += num_regexp.matchedLength();
+
+ if (!suffix.isEmpty() && input.mid(strpos) != suffix) {
+ return QValidator::Invalid;
+ } else {
+ strpos += suffix.length();
+ }
+
+ if (strpos != input.length())
+ return QValidator::Invalid;
+
+ // At this point we can say for sure that the input is fine. Let's fix it up a bit though
+ input.replace(num_pos, sub_input.length(), sub_input.toUpper());
+
+ return QValidator::Acceptable;
+}
diff --git a/src/citra_qt/util/spinbox.hxx b/src/citra_qt/util/spinbox.hxx
new file mode 100644
index 00000000..68f5b989
--- /dev/null
+++ b/src/citra_qt/util/spinbox.hxx
@@ -0,0 +1,88 @@
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+
+// Copyright 2014 Tony Wasserka
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the owner nor the names of its contributors may
+// be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#pragma once
+
+#include <QAbstractSpinBox>
+#include <QtGlobal>
+
+class QVariant;
+
+/**
+ * A custom spin box widget with enhanced functionality over Qt's QSpinBox
+ */
+class CSpinBox : public QAbstractSpinBox {
+ Q_OBJECT
+
+public:
+ CSpinBox(QWidget* parent = nullptr);
+
+ void stepBy(int steps) override;
+ StepEnabled stepEnabled() const override;
+
+ void SetValue(qint64 val);
+
+ void SetRange(qint64 min, qint64 max);
+
+ void SetBase(int base);
+
+ void SetPrefix(const QString& prefix);
+ void SetSuffix(const QString& suffix);
+
+ void SetNumDigits(int num_digits);
+
+ QValidator::State validate(QString& input, int& pos) const override;
+
+signals:
+ void ValueChanged(qint64 val);
+
+private slots:
+ void OnEditingFinished();
+
+private:
+ void UpdateText();
+
+ bool HasSign() const;
+
+ QString TextFromValue();
+ qint64 ValueFromText();
+
+ qint64 min_value, max_value;
+
+ qint64 value;
+
+ QString prefix, suffix;
+
+ int base;
+
+ int num_digits;
+};
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 9d5a9076..15989708 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -3,14 +3,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
set(SRCS
break_points.cpp
- console_listener.cpp
emu_window.cpp
extended_trace.cpp
file_search.cpp
file_util.cpp
hash.cpp
key_map.cpp
- log_manager.cpp
+ logging/filter.cpp
+ logging/text_formatter.cpp
+ logging/backend.cpp
math_util.cpp
mem_arena.cpp
memory_util.cpp
@@ -32,7 +33,7 @@ set(HEADERS
common_funcs.h
common_paths.h
common_types.h
- console_listener.h
+ concurrent_ring_buffer.h
cpu_detect.h
debug_interface.h
emu_window.h
@@ -44,13 +45,17 @@ set(HEADERS
key_map.h
linear_disk_cache.h
log.h
- log_manager.h
+ logging/text_formatter.h
+ logging/filter.h
+ logging/log.h
+ logging/backend.h
math_util.h
mem_arena.h
memory_util.h
msg_handler.h
platform.h
scm_rev.h
+ scope_exit.h
string_util.h
swap.h
symbols.h
diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp
index 25528b86..587dbf40 100644
--- a/src/common/break_points.cpp
+++ b/src/common/break_points.cpp
@@ -180,7 +180,7 @@ void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr,
{
if (Log)
{
- INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
+ LOG_DEBUG(Debug_Breakpoint, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
pc, debug_interface->getDescription(pc).c_str(),
write ? "Write" : "Read", size*8, size*2, iValue, addr,
debug_interface->getDescription(addr).c_str()
diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h
index dc8ac1fd..39a14dc8 100644
--- a/src/common/chunk_file.h
+++ b/src/common/chunk_file.h
@@ -51,7 +51,7 @@ public:
PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) {
}
~PointerWrapSection();
-
+
bool operator == (const int &v) const { return ver_ == v; }
bool operator != (const int &v) const { return ver_ != v; }
bool operator <= (const int &v) const { return ver_ <= v; }
@@ -154,7 +154,7 @@ public:
Do(foundVersion);
if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
- WARN_LOG(COMMON, "Savestate failure: wrong version %d found for %s", foundVersion, title);
+ LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title);
SetError(ERROR_FAILURE);
return PointerWrapSection(*this, -1, title);
}
@@ -178,7 +178,14 @@ public:
case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break;
case MODE_WRITE: memcpy(*ptr, data, size); break;
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
- case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break;
+ case MODE_VERIFY:
+ for (int i = 0; i < size; i++) {
+ _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
+ "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
+ ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
+ (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
+ }
+ break;
default: break; // throw an error?
}
(*ptr) += size;
@@ -191,12 +198,19 @@ public:
case MODE_READ: memcpy(data, *ptr, size); break;
case MODE_WRITE: memcpy(*ptr, data, size); break;
case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
- case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break;
+ case MODE_VERIFY:
+ for (int i = 0; i < size; i++) {
+ _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
+ "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
+ ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
+ (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
+ }
+ break;
default: break; // throw an error?
}
(*ptr) += size;
}
-
+
template<class K, class T>
void Do(std::map<K, T *> &x)
{
@@ -204,11 +218,11 @@ public:
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
- if (it->second != NULL)
+ if (it->second != nullptr)
delete it->second;
}
}
- T *dv = NULL;
+ T *dv = nullptr;
DoMap(x, dv);
}
@@ -264,11 +278,11 @@ public:
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
- if (it->second != NULL)
+ if (it->second != nullptr)
delete it->second;
}
}
- T *dv = NULL;
+ T *dv = nullptr;
DoMultimap(x, dv);
}
@@ -320,7 +334,7 @@ public:
template<class T>
void Do(std::vector<T *> &x)
{
- T *dv = NULL;
+ T *dv = nullptr;
DoVector(x, dv);
}
@@ -364,12 +378,12 @@ public:
if (vec_size > 0)
DoArray(&x[0], vec_size);
}
-
+
// Store deques.
template<class T>
void Do(std::deque<T *> &x)
{
- T *dv = NULL;
+ T *dv = nullptr;
DoDeque(x, dv);
}
@@ -395,7 +409,7 @@ public:
template<class T>
void Do(std::list<T *> &x)
{
- T *dv = NULL;
+ T *dv = nullptr;
Do(x, dv);
}
@@ -433,7 +447,7 @@ public:
{
for (auto it = x.begin(), end = x.end(); it != end; ++it)
{
- if (*it != NULL)
+ if (*it != nullptr)
delete *it;
}
}
@@ -476,26 +490,31 @@ public:
break;
default:
- ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode);
+ LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
}
}
// Store strings.
- void Do(std::string &x)
+ void Do(std::string &x)
{
int stringLen = (int)x.length() + 1;
Do(stringLen);
-
+
switch (mode) {
case MODE_READ: x = (char*)*ptr; break;
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
case MODE_MEASURE: break;
- case MODE_VERIFY: _dbg_assert_msg_(COMMON, !strcmp(x.c_str(), (char*)*ptr), "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (char*)*ptr, ptr); break;
+ case MODE_VERIFY:
+ _dbg_assert_msg_(Common,
+ !strcmp(x.c_str(), (char*)*ptr),
+ "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
+ x.c_str(), (char*)*ptr, ptr);
+ break;
}
(*ptr) += stringLen;
}
- void Do(std::wstring &x)
+ void Do(std::wstring &x)
{
int stringLen = sizeof(wchar_t)*((int)x.length() + 1);
Do(stringLen);
@@ -504,7 +523,11 @@ public:
case MODE_READ: x = (wchar_t*)*ptr; break;
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
case MODE_MEASURE: break;
- case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break;
+ case MODE_VERIFY:
+ _dbg_assert_msg_(Common, x == (wchar_t*)*ptr,
+ "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
+ x.c_str(), (wchar_t*)*ptr, ptr);
+ break;
}
(*ptr) += stringLen;
}
@@ -518,7 +541,7 @@ public:
void DoClass(T *&x) {
if (mode == MODE_READ)
{
- if (x != NULL)
+ if (x != nullptr)
delete x;
x = new T();
}
@@ -534,7 +557,7 @@ public:
void Do(T &x) {
DoHelper<T>::Do(this, x);
}
-
+
template<class T>
void DoPOD(T &x) {
DoHelper<T>::Do(this, x);
@@ -567,7 +590,7 @@ public:
{
if (mode == MODE_READ)
{
- cur->next = 0;
+ cur->next = nullptr;
list_cur = cur;
if (prev)
prev->next = cur;
@@ -586,13 +609,13 @@ public:
if (mode == MODE_READ)
{
if (prev)
- prev->next = 0;
+ prev->next = nullptr;
if (list_end)
*list_end = prev;
if (list_cur)
{
if (list_start == list_cur)
- list_start = 0;
+ list_start = nullptr;
do
{
LinkedListItem<T>* next = list_cur->next;
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index c18afe11..67b3679b 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -4,6 +4,9 @@
#pragma once
+#include "common_types.h"
+#include <cstdlib>
+
#ifdef _WIN32
#define SLEEP(x) Sleep(x)
#else
@@ -17,7 +20,7 @@ template<> struct CompileTimeAssert<true> {};
#define b2(x) ( (x) | ( (x) >> 1) )
#define b4(x) ( b2(x) | ( b2(x) >> 2) )
#define b8(x) ( b4(x) | ( b4(x) >> 4) )
-#define b16(x) ( b8(x) | ( b8(x) >> 8) )
+#define b16(x) ( b8(x) | ( b8(x) >> 8) )
#define b32(x) (b16(x) | (b16(x) >>16) )
#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
@@ -73,18 +76,20 @@ inline u64 _rotr64(u64 x, unsigned int shift){
}
#else // WIN32
+#include <locale.h>
+
// Function Cross-Compatibility
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define unlink _unlink
#define snprintf _snprintf
#define vscprintf _vscprintf
-
+
// Locale Cross-Compatibility
#define locale_t _locale_t
#define freelocale _free_locale
#define newlocale(mask, locale, base) _create_locale(mask, locale)
-
+
#define LC_GLOBAL_LOCALE ((locale_t)-1)
#define LC_ALL_MASK LC_ALL
#define LC_COLLATE_MASK LC_COLLATE
@@ -92,7 +97,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){
#define LC_MONETARY_MASK LC_MONETARY
#define LC_NUMERIC_MASK LC_NUMERIC
#define LC_TIME_MASK LC_TIME
-
+
inline locale_t uselocale(locale_t new_locale)
{
// Retrieve the current per thread locale setting
@@ -106,7 +111,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){
// Restore the global locale
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
}
- else if(new_locale != NULL)
+ else if(new_locale != nullptr)
{
// Configure the thread to set the locale only for this thread
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
@@ -168,8 +173,8 @@ 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 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);}
@@ -226,7 +231,7 @@ 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;
}
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index ae08d082..a8688975 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -29,19 +29,6 @@
#endif
#endif
-// Shared data dirs (Sys and shared User for linux)
-#ifdef _WIN32
- #define SYSDATA_DIR "sys"
-#else
- #ifdef DATA_DIR
- #define SYSDATA_DIR DATA_DIR "sys"
- #define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP
- #else
- #define SYSDATA_DIR "sys"
- #define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP
- #endif
-#endif
-
// Dirs in both User and Sys
#define EUR_DIR "EUR"
#define USA_DIR "USA"
@@ -53,6 +40,8 @@
#define MAPS_DIR "maps"
#define CACHE_DIR "cache"
#define SDMC_DIR "sdmc"
+#define SAVEDATA_DIR "savedata"
+#define SYSDATA_DIR "sysdata"
#define SHADERCACHE_DIR "shader_cache"
#define STATESAVES_DIR "state_saves"
#define SCREENSHOTS_DIR "screenShots"
@@ -70,6 +59,9 @@
#define DEBUGGER_CONFIG "debugger.ini"
#define LOGGER_CONFIG "logger.ini"
+// Sys files
+#define SHARED_FONT "shared_font.bin"
+
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
#define MAIN_LOG "emu.log"
diff --git a/src/common/common_types.h b/src/common/common_types.h
index 7ce6b224..c74c74f0 100644
--- a/src/common/common_types.h
+++ b/src/common/common_types.h
@@ -22,7 +22,7 @@
* http://code.google.com/p/gekko-gc-emu/
*/
-#pragma once
+#pragma once
#include <cmath>
#include <cstdint>
@@ -41,12 +41,10 @@ typedef std::int64_t s64; ///< 64-bit signed int
typedef float f32; ///< 32-bit floating point
typedef double f64; ///< 64-bit floating point
-#include "common/common.h"
-
/// Union for fast 16-bit type casting
union t16 {
- u8 _u8[2]; ///< 8-bit unsigned char(s)
- u16 _u16; ///< 16-bit unsigned shorts(s)
+ u8 _u8[2]; ///< 8-bit unsigned char(s)
+ u16 _u16; ///< 16-bit unsigned shorts(s)
};
/// Union for fast 32-bit type casting
diff --git a/src/common/concurrent_ring_buffer.h b/src/common/concurrent_ring_buffer.h
new file mode 100644
index 00000000..2951d93d
--- /dev/null
+++ b/src/common/concurrent_ring_buffer.h
@@ -0,0 +1,164 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <condition_variable>
+#include <cstdint>
+#include <mutex>
+#include <thread>
+
+#include "common/common.h" // for NonCopyable
+#include "common/log.h" // for _dbg_assert_
+
+namespace Common {
+
+/**
+ * A MPMC (Multiple-Producer Multiple-Consumer) concurrent ring buffer. This data structure permits
+ * multiple threads to push and pop from a queue of bounded size.
+ */
+template <typename T, size_t ArraySize>
+class ConcurrentRingBuffer : private NonCopyable {
+public:
+ /// Value returned by the popping functions when the queue has been closed.
+ static const size_t QUEUE_CLOSED = -1;
+
+ ConcurrentRingBuffer() {}
+
+ ~ConcurrentRingBuffer() {
+ // If for whatever reason the queue wasn't completely drained, destroy the left over items.
+ for (size_t i = reader_index, end = writer_index; i != end; i = (i + 1) % ArraySize) {
+ Data()[i].~T();
+ }
+ }
+
+ /**
+ * Pushes a value to the queue. If the queue is full, this method will block. Does nothing if
+ * the queue is closed.
+ */
+ void Push(T val) {
+ std::unique_lock<std::mutex> lock(mutex);
+ if (closed) {
+ return;
+ }
+
+ // If the buffer is full, wait
+ writer.wait(lock, [&]{
+ return (writer_index + 1) % ArraySize != reader_index;
+ });
+
+ T* item = &Data()[writer_index];
+ new (item) T(std::move(val));
+
+ writer_index = (writer_index + 1) % ArraySize;
+
+ // Wake up waiting readers
+ lock.unlock();
+ reader.notify_one();
+ }
+
+ /**
+ * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will not
+ * block, and might return 0 values if there are no elements in the queue when it is called.
+ *
+ * @return The number of elements stored in `dest`. If the queue has been closed, returns
+ * `QUEUE_CLOSED`.
+ */
+ size_t Pop(T* dest, size_t dest_len) {
+ std::unique_lock<std::mutex> lock(mutex);
+ if (closed && !CanRead()) {
+ return QUEUE_CLOSED;
+ }
+ return PopInternal(dest, dest_len);
+ }
+
+ /**
+ * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will block
+ * if there are no elements in the queue when it is called.
+ *
+ * @return The number of elements stored in `dest`. If the queue has been closed, returns
+ * `QUEUE_CLOSED`.
+ */
+ size_t BlockingPop(T* dest, size_t dest_len) {
+ std::unique_lock<std::mutex> lock(mutex);
+ if (closed && !CanRead()) {
+ return QUEUE_CLOSED;
+ }
+
+ while (!CanRead()) {
+ reader.wait(lock);
+ if (closed && !CanRead()) {
+ return QUEUE_CLOSED;
+ }
+ }
+ _dbg_assert_(Common, CanRead());
+ return PopInternal(dest, dest_len);
+ }
+
+ /**
+ * Closes the queue. After calling this method, `Push` operations won't have any effect, and
+ * `PopMany` and `PopManyBlock` will start returning `QUEUE_CLOSED`. This is intended to allow
+ * a graceful shutdown of all consumers.
+ */
+ void Close() {
+ std::unique_lock<std::mutex> lock(mutex);
+ closed = true;
+ // We need to wake up any reader that are waiting for an item that will never come.
+ lock.unlock();
+ reader.notify_all();
+ }
+
+ /// Returns true if `Close()` has been called.
+ bool IsClosed() const {
+ return closed;
+ }
+
+private:
+ size_t PopInternal(T* dest, size_t dest_len) {
+ size_t output_count = 0;
+ while (output_count < dest_len && CanRead()) {
+ _dbg_assert_(Common, CanRead());
+
+ T* item = &Data()[reader_index];
+ T out_val = std::move(*item);
+ item->~T();
+
+ size_t prev_index = (reader_index + ArraySize - 1) % ArraySize;
+ reader_index = (reader_index + 1) % ArraySize;
+ if (writer_index == prev_index) {
+ writer.notify_one();
+ }
+ dest[output_count++] = std::move(out_val);
+ }
+ return output_count;
+ }
+
+ bool CanRead() const {
+ return reader_index != writer_index;
+ }
+
+ T* Data() {
+ return static_cast<T*>(static_cast<void*>(&storage));
+ }
+
+ /// Storage for entries
+ typename std::aligned_storage<ArraySize * sizeof(T),
+ std::alignment_of<T>::value>::type storage;
+
+ /// Data is valid in the half-open interval [reader, writer). If they are `QUEUE_CLOSED` then the
+ /// queue has been closed.
+ size_t writer_index = 0, reader_index = 0;
+ // True if the queue has been closed.
+ bool closed = false;
+
+ /// Mutex that protects the entire data structure.
+ std::mutex mutex;
+ /// Signaling wakes up reader which is waiting for storage to be non-empty.
+ std::condition_variable reader;
+ /// Signaling wakes up writer which is waiting for storage to be non-full.
+ std::condition_variable writer;
+};
+
+} // namespace
diff --git a/src/common/console_listener.cpp b/src/common/console_listener.cpp
deleted file mode 100644
index 53f20d75..00000000
--- a/src/common/console_listener.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include <algorithm>
-
-#ifdef _WIN32
-#include <windows.h>
-#include <array>
-#endif
-
-#include "common/common.h"
-#include "common/log_manager.h" // Common
-#include "common/console_listener.h" // Common
-
-ConsoleListener::ConsoleListener()
-{
-#ifdef _WIN32
- hConsole = NULL;
- bUseColor = true;
-#else
- bUseColor = isatty(fileno(stdout));
-#endif
-}
-
-ConsoleListener::~ConsoleListener()
-{
- Close();
-}
-
-// 100, 100, "Dolphin Log Console"
-// Open console window - width and height is the size of console window
-// Name is the window title
-void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title)
-{
-#ifdef _WIN32
- if (!GetConsoleWindow())
- {
- // Open the console window and create the window handle for GetStdHandle()
- AllocConsole();
- // Hide
- if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE);
- // Save the window handle that AllocConsole() created
- hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
- // Set the console window title
- SetConsoleTitle(Common::UTF8ToTStr(Title).c_str());
- // Set letter space
- LetterSpace(80, 4000);
- //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true);
- }
- else
- {
- hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
- }
-#endif
-}
-
-void ConsoleListener::UpdateHandle()
-{
-#ifdef _WIN32
- hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
-#endif
-}
-
-// Close the console window and close the eventual file handle
-void ConsoleListener::Close()
-{
-#ifdef _WIN32
- if (hConsole == NULL)
- return;
- FreeConsole();
- hConsole = NULL;
-#else
- fflush(NULL);
-#endif
-}
-
-bool ConsoleListener::IsOpen()
-{
-#ifdef _WIN32
- return (hConsole != NULL);
-#else
- return true;
-#endif
-}
-
-/*
- LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are
- dependent on each other, that's the reason for the additional checks.
-*/
-void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst)
-{
-#ifdef _WIN32
- BOOL SB, SW;
- if (BufferFirst)
- {
- // Change screen buffer size
- COORD Co = {BufferWidth, BufferHeight};
- SB = SetConsoleScreenBufferSize(hConsole, Co);
- // Change the screen buffer window size
- SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom
- SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
- }
- else
- {
- // Change the screen buffer window size
- SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom
- SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
- // Change screen buffer size
- COORD Co = {BufferWidth, BufferHeight};
- SB = SetConsoleScreenBufferSize(hConsole, Co);
- }
-#endif
-}
-void ConsoleListener::LetterSpace(int Width, int Height)
-{
-#ifdef _WIN32
- // Get console info
- CONSOLE_SCREEN_BUFFER_INFO ConInfo;
- GetConsoleScreenBufferInfo(hConsole, &ConInfo);
-
- //
- int OldBufferWidth = ConInfo.dwSize.X;
- int OldBufferHeight = ConInfo.dwSize.Y;
- int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left);
- int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top);
- //
- int NewBufferWidth = Width;
- int NewBufferHeight = Height;
- int NewScreenWidth = NewBufferWidth - 1;
- int NewScreenHeight = OldScreenHeight;
-
- // Width
- BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1));
- // Height
- BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1));
-
- // Resize the window too
- //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true);
-#endif
-}
-#ifdef _WIN32
-COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth)
-{
- COORD Ret = {0, 0};
- // Full rows
- int Step = (int)floor((float)BytesRead / (float)BufferWidth);
- Ret.Y += Step;
- // Partial row
- Ret.X = BytesRead - (BufferWidth * Step);
- return Ret;
-}
-#endif
-void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize)
-{
-#ifdef _WIN32
- // Check size
- if (Width < 8 || Height < 12) return;
-
- bool DBef = true;
- bool DAft = true;
- std::string SLog = "";
-
- const HWND hWnd = GetConsoleWindow();
- const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
-
- // Get console info
- CONSOLE_SCREEN_BUFFER_INFO ConInfo;
- GetConsoleScreenBufferInfo(hConsole, &ConInfo);
- DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y;
-
- // ---------------------------------------------------------------------
- // Save the current text
- // ------------------------
- DWORD cCharsRead = 0;
- COORD coordScreen = { 0, 0 };
-
- static const int MAX_BYTES = 1024 * 16;
-
- std::vector<std::array<TCHAR, MAX_BYTES>> Str;
- std::vector<std::array<WORD, MAX_BYTES>> Attr;
-
- // ReadConsoleOutputAttribute seems to have a limit at this level
- static const int ReadBufferSize = MAX_BYTES - 32;
-
- DWORD cAttrRead = ReadBufferSize;
- DWORD BytesRead = 0;
- while (BytesRead < BufferSize)
- {
- Str.resize(Str.size() + 1);
- if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead))
- SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
-
- Attr.resize(Attr.size() + 1);
- if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead))
- SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
-
- // Break on error
- if (cAttrRead == 0) break;
- BytesRead += cAttrRead;
- coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X);
- }
- // Letter space
- int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f);
- int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f);
- int LBufWidth = LWidth + 1;
- int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth);
- // Change screen buffer size
- LetterSpace(LBufWidth, LBufHeight);
-
-
- ClearScreen(true);
- coordScreen.Y = 0;
- coordScreen.X = 0;
- DWORD cCharsWritten = 0;
-
- int BytesWritten = 0;
- DWORD cAttrWritten = 0;
- for (size_t i = 0; i < Attr.size(); i++)
- {
- if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten))
- SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
- if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten))
- SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
-
- BytesWritten += cAttrWritten;
- coordScreen = GetCoordinates(BytesWritten, LBufWidth);
- }
-
- const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X;
- COORD Coo = GetCoordinates(OldCursor, LBufWidth);
- SetConsoleCursorPosition(hConsole, Coo);
-
- if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str());
-
- // Resize the window too
- if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true);
-#endif
-}
-
-void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
-{
-#if defined(_WIN32)
- WORD Color;
-
- switch (Level)
- {
- case OS_LEVEL: // light yellow
- Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
- break;
- case NOTICE_LEVEL: // light green
- Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
- break;
- case ERROR_LEVEL: // light red
- Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
- break;
- case WARNING_LEVEL: // light purple
- Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
- break;
- case INFO_LEVEL: // cyan
- Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
- break;
- case DEBUG_LEVEL: // gray
- Color = FOREGROUND_INTENSITY;
- break;
- default: // off-white
- Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
- break;
- }
- SetConsoleTextAttribute(hConsole, Color);
- printf(Text);
-#else
- char ColorAttr[16] = "";
- char ResetAttr[16] = "";
-
- if (bUseColor)
- {
- strcpy(ResetAttr, "\033[0m");
- switch (Level)
- {
- case NOTICE_LEVEL: // light green
- strcpy(ColorAttr, "\033[92m");
- break;
- case ERROR_LEVEL: // light red
- strcpy(ColorAttr, "\033[91m");
- break;
- case WARNING_LEVEL: // light yellow
- strcpy(ColorAttr, "\033[93m");
- break;
- default:
- break;
- }
- }
- fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr);
-#endif
-}
-// Clear console screen
-void ConsoleListener::ClearScreen(bool Cursor)
-{
-#if defined(_WIN32)
- COORD coordScreen = { 0, 0 };
- DWORD cCharsWritten;
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- DWORD dwConSize;
-
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
-
- GetConsoleScreenBufferInfo(hConsole, &csbi);
- dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
- // Write space to the entire console
- FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
- GetConsoleScreenBufferInfo(hConsole, &csbi);
- FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
- // Reset cursor
- if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen);
-#endif
-}
-
-
diff --git a/src/common/console_listener.h b/src/common/console_listener.h
deleted file mode 100644
index ebd90a10..00000000
--- a/src/common/console_listener.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/log_manager.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-class ConsoleListener : public LogListener
-{
-public:
- ConsoleListener();
- ~ConsoleListener();
-
- void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console");
- void UpdateHandle();
- void Close();
- bool IsOpen();
- void LetterSpace(int Width, int Height);
- void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst);
- void PixelSpace(int Left, int Top, int Width, int Height, bool);
-#ifdef _WIN32
- COORD GetCoordinates(int BytesRead, int BufferWidth);
-#endif
- void Log(LogTypes::LOG_LEVELS, const char *Text) override;
- void ClearScreen(bool Cursor = true);
-
-private:
-#ifdef _WIN32
- HWND GetHwnd(void);
- HANDLE hConsole;
-#endif
- bool bUseColor;
-};
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index 4d09acb8..4cb94fed 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -9,17 +9,33 @@
#include "common/string_util.h"
#include "common/key_map.h"
-// Abstraction class used to provide an interface between emulation code and the frontend (e.g. SDL,
-// QGLWidget, GLFW, etc...)
+/**
+ * Abstraction class used to provide an interface between emulation code and the frontend
+ * (e.g. SDL, QGLWidget, GLFW, etc...).
+ *
+ * Design notes on the interaction between EmuWindow and the emulation core:
+ * - Generally, decisions on anything visible to the user should be left up to the GUI.
+ * For example, the emulation core should not try to dictate some window title or size.
+ * This stuff is not the core's business and only causes problems with regards to thread-safety
+ * anyway.
+ * - Under certain circumstances, it may be desirable for the core to politely request the GUI
+ * to set e.g. a minimum window size. However, the GUI should always be free to ignore any
+ * such hints.
+ * - EmuWindow may expose some of its state as read-only to the emulation core, however care
+ * should be taken to make sure the provided information is self-consistent. This requires
+ * some sort of synchronization (most of this is still a TODO).
+ * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
+ * re-read the upper points again and think about it if you don't see this.
+ */
class EmuWindow
{
-
public:
- /// Data structure to store an emuwindow configuration
+ /// Data structure to store emuwindow configuration
struct WindowConfig {
bool fullscreen;
int res_width;
int res_height;
+ std::pair<unsigned,unsigned> min_client_area_size;
};
/// Swap buffers to display the next frame
@@ -42,52 +58,96 @@ public:
/// Signals a key release action to the HID module
static void KeyReleased(KeyMap::HostDeviceKey key);
- WindowConfig GetConfig() const {
- return m_config;
+ /**
+ * Returns currently active configuration.
+ * @note Accesses to the returned object need not be consistent because it may be modified in another thread
+ */
+ const WindowConfig& GetActiveConfig() const {
+ return active_config;
}
+ /**
+ * Requests the internal configuration to be replaced by the specified argument at some point in the future.
+ * @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active.
+ */
void SetConfig(const WindowConfig& val) {
- m_config = val;
- }
-
- int GetClientAreaWidth() const {
- return m_client_area_width;
+ config = val;
}
- void SetClientAreaWidth(const int val) {
- m_client_area_width = val;
+ /**
+ * Gets the framebuffer size in pixels.
+ * @note This method is thread-safe
+ */
+ const std::pair<unsigned,unsigned> GetFramebufferSize() const {
+ return framebuffer_size;
}
- int GetClientAreaHeight() const {
- return m_client_area_height;
+ /**
+ * Gets window client area width in logical coordinates.
+ * @note For high-DPI systems, this is smaller than the framebuffer size.
+ * @note This method is thread-safe
+ */
+ std::pair<unsigned,unsigned> GetClientAreaSize() const {
+ return std::make_pair(client_area_width, client_area_height);
}
- void SetClientAreaHeight(const int val) {
- m_client_area_height = val;
+protected:
+ EmuWindow()
+ {
+ // TODO: Find a better place to set this.
+ config.min_client_area_size = std::make_pair(400u, 480u);
+ active_config = config;
}
+ virtual ~EmuWindow() {}
- std::string GetWindowTitle() const {
- return m_window_title;
+ /**
+ * Processes any pending configuration changes from the last SetConfig call.
+ * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
+ * field changed.
+ * @note Implementations will usually want to call this from the GUI thread.
+ * @todo Actually call this in existing implementations.
+ */
+ void ProcessConfigurationChanges() {
+ // TODO: For proper thread safety, we should eventually implement a proper
+ // multiple-writer/single-reader queue...
+
+ if (config.min_client_area_size != active_config.min_client_area_size) {
+ OnMinimalClientAreaChangeRequest(config.min_client_area_size);
+ config.min_client_area_size = active_config.min_client_area_size;
+ }
}
-
- void SetWindowTitle(std::string val) {
- m_window_title = val;
+
+ /**
+ * Update internal framebuffer size 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;
}
-protected:
- EmuWindow():
- m_client_area_width(640),
- m_client_area_height(480),
- m_window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc))
- {}
- virtual ~EmuWindow() {}
+ /**
+ * Update internal client area size with the given parameter.
+ * @note EmuWindow implementations will usually use this in window resize event handlers.
+ */
+ void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) {
+ client_area_width = size.first;
+ client_area_height = size.second;
+ }
- std::string m_window_title; ///< Current window title, should be used by window impl.
+private:
+ /**
+ * Handler called when the minimal client area was requested to be changed via SetConfig.
+ * For the request to be honored, EmuWindow implementations will usually reimplement this function.
+ */
+ virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
+ // By default, ignore this request and do nothing.
+ }
- int m_client_area_width; ///< Current client width, should be set by window impl.
- int m_client_area_height; ///< Current client height, should be set by window impl.
+ std::pair<unsigned,unsigned> framebuffer_size;
-private:
- WindowConfig m_config; ///< Internal configuration
+ 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.
+ WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
+ WindowConfig active_config; ///< Internal active configuration
};
diff --git a/src/common/extended_trace.cpp b/src/common/extended_trace.cpp
index 9cd0398e..cf7c346d 100644
--- a/src/common/extended_trace.cpp
+++ b/src/common/extended_trace.cpp
@@ -29,7 +29,7 @@ using namespace std;
void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
{
#if defined(UNICODE)||defined(_UNICODE)
- ULONG index = 0;
+ ULONG index = 0;
PCSTR lpAct = lpszIn;
for( ; ; lpAct++ )
@@ -37,7 +37,7 @@ void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
lpszOut[index++] = (TCHAR)(*lpAct);
if ( *lpAct == 0 )
break;
- }
+ }
#else
// This is trivial :)
strcpy( lpszOut, lpszIn );
@@ -82,7 +82,7 @@ static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath )
}
// Add user defined path
- if ( lpszIniPath != NULL )
+ if ( lpszIniPath != nullptr )
if ( lpszIniPath[0] != '\0' )
{
strcat( lpszSymbolPath, ";" );
@@ -101,7 +101,7 @@ BOOL InitSymInfo( PCSTR lpszInitialSymbolPath )
CHAR lpszSymbolPath[BUFFERSIZE];
DWORD symOptions = SymGetOptions();
- symOptions |= SYMOPT_LOAD_LINES;
+ symOptions |= SYMOPT_LOAD_LINES;
symOptions &= ~SYMOPT_UNDNAME;
SymSetOptions( symOptions );
InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath );
@@ -138,7 +138,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
DWORD dwSymSize = 10000;
TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
- LPTSTR lpszParamSep = NULL;
+ LPTSTR lpszParamSep = nullptr;
LPTSTR lpszParsed = lpszUnDSymbol;
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
@@ -153,15 +153,15 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
#ifndef _M_X64
DWORD dwDisp = 0;
if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
-#else
+#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 |
+ UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE,
+ UNDNAME_COMPLETE |
UNDNAME_NO_THISTYPE |
UNDNAME_NO_SPECIAL_SYMS |
UNDNAME_NO_MEMBER_TYPE |
@@ -187,13 +187,13 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
// 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)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL)
+ if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr)
{
ULONG index = 0;
for( ; ; index++ )
{
lpszParamSep = _tcschr( lpszParsed, _T(',') );
- if ( lpszParamSep == NULL )
+ if ( lpszParamSep == nullptr )
break;
*lpszParamSep = _T('\0');
@@ -205,7 +205,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
}
lpszParamSep = _tcschr( lpszParsed, _T(')') );
- if ( lpszParamSep != NULL )
+ if ( lpszParamSep != nullptr )
{
*lpszParamSep = _T('\0');
@@ -219,7 +219,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
_tcscat( lpszSymbol, lpszParsed );
ret = TRUE;
- }
+ }
GlobalFree( pSym );
return ret;
@@ -248,7 +248,7 @@ static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
TCHAR fname[_MAX_FNAME];
TCHAR ext[_MAX_EXT];
- _tsplitpath(lpszFileName, NULL, NULL, fname, ext);
+ _tsplitpath(lpszFileName, nullptr, nullptr, fname, ext);
_stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber );
ret = TRUE;
}
@@ -325,23 +325,23 @@ void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file )
PrintFunctionAndSourceInfo(file, callStack);
- for( ULONG index = 0; ; index++ )
+ for( ULONG index = 0; ; index++ )
{
bResult = StackWalk(
IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&callStack,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
SymFunctionTableAccess,
SymGetModuleBase,
- NULL);
+ nullptr);
if ( index == 0 )
continue;
- if( !bResult || callStack.AddrFrame.Offset == 0 )
+ if( !bResult || callStack.AddrFrame.Offset == 0 )
break;
PrintFunctionAndSourceInfo(file, callStack);
@@ -382,23 +382,23 @@ void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip,
PrintFunctionAndSourceInfo(file, callStack);
- for( ULONG index = 0; ; index++ )
+ for( ULONG index = 0; ; index++ )
{
bResult = StackWalk(
IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&callStack,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
SymFunctionTableAccess,
SymGetModuleBase,
- NULL);
+ nullptr);
if ( index == 0 )
continue;
- if( !bResult || callStack.AddrFrame.Offset == 0 )
+ if( !bResult || callStack.AddrFrame.Offset == 0 )
break;
PrintFunctionAndSourceInfo(file, callStack);
diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h
index 78a8f561..b426e659 100644
--- a/src/common/fifo_queue.h
+++ b/src/common/fifo_queue.h
@@ -45,7 +45,7 @@ public:
// create the element, add it to the queue
m_write_ptr->current = new T(std::forward<Arg>(t));
// set the next pointer to a new element ptr
- // then advance the write pointer
+ // then advance the write pointer
m_write_ptr = m_write_ptr->next = new ElementPtr();
Common::AtomicIncrement(m_size);
}
@@ -57,7 +57,7 @@ public:
// advance the read pointer
m_read_ptr = m_read_ptr->next;
// set the next element to NULL to stop the recursive deletion
- tmpptr->next = NULL;
+ tmpptr->next = nullptr;
delete tmpptr; // this also deletes the element
}
@@ -86,7 +86,7 @@ private:
class ElementPtr
{
public:
- ElementPtr() : current(NULL), next(NULL) {}
+ ElementPtr() : current(nullptr), next(nullptr) {}
~ElementPtr()
{
diff --git a/src/common/file_search.cpp b/src/common/file_search.cpp
index 63580f68..bfb54ce7 100644
--- a/src/common/file_search.cpp
+++ b/src/common/file_search.cpp
@@ -43,7 +43,7 @@ void CFileSearch::FindFiles(const std::string& _searchString, const std::string&
bool bkeepLooping = true;
while (bkeepLooping)
- {
+ {
if (findData.cFileName[0] != '.')
{
std::string strFilename;
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 35da0730..42cdf326 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -88,7 +88,7 @@ bool IsDirectory(const std::string &filename)
#endif
if (result < 0) {
- WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s",
+ LOG_WARNING(Common_Filesystem, "stat failed on %s: %s",
filename.c_str(), GetLastErrorMsg());
return false;
}
@@ -100,33 +100,33 @@ bool IsDirectory(const std::string &filename)
// Doesn't supports deleting a directory
bool Delete(const std::string &filename)
{
- INFO_LOG(COMMON, "Delete: file %s", filename.c_str());
+ LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
- // Return true because we care about the file no
+ // Return true because we care about the file no
// being there, not the actual delete.
if (!Exists(filename))
{
- WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str());
+ LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
return true;
}
// We can't delete a directory
if (IsDirectory(filename))
{
- WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str());
+ LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
return false;
}
#ifdef _WIN32
if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
{
- WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s",
+ LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
filename.c_str(), GetLastErrorMsg());
return false;
}
#else
if (unlink(filename.c_str()) == -1) {
- WARN_LOG(COMMON, "Delete: unlink failed on %s: %s",
+ LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s",
filename.c_str(), GetLastErrorMsg());
return false;
}
@@ -138,17 +138,17 @@ bool Delete(const std::string &filename)
// Returns true if successful, or path already exists.
bool CreateDir(const std::string &path)
{
- INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str());
+ LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
#ifdef _WIN32
- if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), NULL))
+ if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
return true;
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS)
{
- WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str());
+ LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
return true;
}
- ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error);
+ LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
return false;
#else
if (mkdir(path.c_str(), 0755) == 0)
@@ -158,11 +158,11 @@ bool CreateDir(const std::string &path)
if (err == EEXIST)
{
- WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str());
+ LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
return true;
}
- ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err));
+ LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err));
return false;
#endif
}
@@ -171,11 +171,11 @@ bool CreateDir(const std::string &path)
bool CreateFullPath(const std::string &fullPath)
{
int panicCounter = 100;
- INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str());
+ LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
if (FileUtil::Exists(fullPath))
{
- INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str());
+ LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
return true;
}
@@ -192,7 +192,7 @@ bool CreateFullPath(const std::string &fullPath)
// Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
std::string const subPath(fullPath.substr(0, position + 1));
if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
- ERROR_LOG(COMMON, "CreateFullPath: directory creation failed");
+ LOG_ERROR(Common, "CreateFullPath: directory creation failed");
return false;
}
@@ -200,7 +200,7 @@ bool CreateFullPath(const std::string &fullPath)
panicCounter--;
if (panicCounter <= 0)
{
- ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep");
+ LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
return false;
}
position++;
@@ -211,12 +211,12 @@ bool CreateFullPath(const std::string &fullPath)
// Deletes a directory filename, returns true on success
bool DeleteDir(const std::string &filename)
{
- INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str());
+ LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
// check if a directory
if (!FileUtil::IsDirectory(filename))
{
- ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str());
+ LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
return false;
}
@@ -227,33 +227,33 @@ bool DeleteDir(const std::string &filename)
if (rmdir(filename.c_str()) == 0)
return true;
#endif
- ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg());
+ LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
return false;
}
-// renames file srcFilename to destFilename, returns true on success
+// renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string &srcFilename, const std::string &destFilename)
{
- INFO_LOG(COMMON, "Rename: %s --> %s",
+ LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
return true;
- ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s",
+ LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
return false;
}
-// copies file srcFilename to destFilename, returns true on success
+// copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string &srcFilename, const std::string &destFilename)
{
- INFO_LOG(COMMON, "Copy: %s --> %s",
+ LOG_TRACE(Common_Filesystem, "%s --> %s",
srcFilename.c_str(), destFilename.c_str());
#ifdef _WIN32
if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
return true;
- ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s",
+ LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
return false;
#else
@@ -267,7 +267,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
FILE *input = fopen(srcFilename.c_str(), "rb");
if (!input)
{
- ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s",
+ LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
return false;
}
@@ -277,7 +277,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
if (!output)
{
fclose(input);
- ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s",
+ LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
return false;
}
@@ -291,8 +291,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
{
if (ferror(input) != 0)
{
- ERROR_LOG(COMMON,
- "Copy: failed reading from source, %s --> %s: %s",
+ LOG_ERROR(Common_Filesystem,
+ "failed reading from source, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
goto bail;
}
@@ -302,8 +302,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
int wnum = fwrite(buffer, sizeof(char), rnum, output);
if (wnum != rnum)
{
- ERROR_LOG(COMMON,
- "Copy: failed writing to output, %s --> %s: %s",
+ LOG_ERROR(Common_Filesystem,
+ "failed writing to output, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
goto bail;
}
@@ -326,16 +326,16 @@ u64 GetSize(const std::string &filename)
{
if (!Exists(filename))
{
- WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str());
+ LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
return 0;
}
if (IsDirectory(filename))
{
- WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str());
+ LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
return 0;
}
-
+
struct stat64 buf;
#ifdef _WIN32
if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0)
@@ -343,12 +343,12 @@ u64 GetSize(const std::string &filename)
if (stat64(filename.c_str(), &buf) == 0)
#endif
{
- DEBUG_LOG(COMMON, "GetSize: %s: %lld",
+ LOG_TRACE(Common_Filesystem, "%s: %lld",
filename.c_str(), (long long)buf.st_size);
return buf.st_size;
}
- ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s",
+ LOG_ERROR(Common_Filesystem, "Stat failed %s: %s",
filename.c_str(), GetLastErrorMsg());
return 0;
}
@@ -358,7 +358,7 @@ u64 GetSize(const int fd)
{
struct stat64 buf;
if (fstat64(fd, &buf) != 0) {
- ERROR_LOG(COMMON, "GetSize: stat failed %i: %s",
+ LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s",
fd, GetLastErrorMsg());
return 0;
}
@@ -371,27 +371,27 @@ u64 GetSize(FILE *f)
// can't use off_t here because it can be 32-bit
u64 pos = ftello(f);
if (fseeko(f, 0, SEEK_END) != 0) {
- ERROR_LOG(COMMON, "GetSize: seek failed %p: %s",
+ LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
f, GetLastErrorMsg());
return 0;
}
u64 size = ftello(f);
if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
- ERROR_LOG(COMMON, "GetSize: seek failed %p: %s",
+ LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
f, GetLastErrorMsg());
return 0;
}
return size;
}
-// creates an empty file filename, returns true on success
+// creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string &filename)
{
- INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str());
+ LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
if (!FileUtil::IOFile(filename, "wb"))
{
- ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s",
+ LOG_ERROR(Common_Filesystem, "failed %s: %s",
filename.c_str(), GetLastErrorMsg());
return false;
}
@@ -404,7 +404,7 @@ bool CreateEmptyFile(const std::string &filename)
// results into parentEntry. Returns the number of files+directories found
u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
{
- INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str());
+ LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
// How many files + directories we found
u32 foundEntries = 0;
#ifdef _WIN32
@@ -423,7 +423,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
FSTEntry entry;
const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
#else
- struct dirent dirent, *result = NULL;
+ struct dirent dirent, *result = nullptr;
DIR *dirp = opendir(directory.c_str());
if (!dirp)
@@ -437,7 +437,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
#endif
// check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
- ((virtualName[0] == '.') && (virtualName[1] == '.') &&
+ ((virtualName[0] == '.') && (virtualName[1] == '.') &&
(virtualName[2] == '\0')))
continue;
entry.virtualName = virtualName;
@@ -452,14 +452,14 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
foundEntries += (u32)entry.size;
}
else
- { // is a file
+ { // is a file
entry.isDirectory = false;
entry.size = GetSize(entry.physicalName.c_str());
}
++foundEntries;
// Push into the tree
- parentEntry.children.push_back(entry);
-#ifdef _WIN32
+ parentEntry.children.push_back(entry);
+#ifdef _WIN32
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
#else
@@ -474,7 +474,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
// Deletes the given directory and anything under it. Returns true on success.
bool DeleteDirRecursively(const std::string &directory)
{
- INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str());
+ LOG_TRACE(Common_Filesystem, "%s", directory.c_str());
#ifdef _WIN32
// Find the first file in the directory.
WIN32_FIND_DATA ffd;
@@ -491,7 +491,7 @@ bool DeleteDirRecursively(const std::string &directory)
{
const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
#else
- struct dirent dirent, *result = NULL;
+ struct dirent dirent, *result = nullptr;
DIR *dirp = opendir(directory.c_str());
if (!dirp)
return false;
@@ -504,7 +504,7 @@ bool DeleteDirRecursively(const std::string &directory)
// check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
- ((virtualName[0] == '.') && (virtualName[1] == '.') &&
+ ((virtualName[0] == '.') && (virtualName[1] == '.') &&
(virtualName[2] == '\0')))
continue;
@@ -540,7 +540,7 @@ bool DeleteDirRecursively(const std::string &directory)
closedir(dirp);
#endif
FileUtil::DeleteDir(directory);
-
+
return true;
}
@@ -552,7 +552,7 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
if (!FileUtil::Exists(source_path)) return;
if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path);
- struct dirent dirent, *result = NULL;
+ struct dirent dirent, *result = nullptr;
DIR *dirp = opendir(source_path.c_str());
if (!dirp) return;
@@ -585,12 +585,12 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
std::string GetCurrentDir()
{
char *dir;
- // Get the current working directory (getcwd uses malloc)
- if (!(dir = __getcwd(NULL, 0))) {
+ // Get the current working directory (getcwd uses malloc)
+ if (!(dir = __getcwd(nullptr, 0))) {
- ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s",
+ LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
GetLastErrorMsg());
- return NULL;
+ return nullptr;
}
std::string strDir = dir;
free(dir);
@@ -604,7 +604,7 @@ bool SetCurrentDir(const std::string &directory)
}
#if defined(__APPLE__)
-std::string GetBundleDirectory()
+std::string GetBundleDirectory()
{
CFURLRef BundleRef;
char AppBundlePath[MAXPATHLEN];
@@ -626,7 +626,7 @@ std::string& GetExeDirectory()
if (DolphinPath.empty())
{
TCHAR Dolphin_exe_Path[2048];
- GetModuleFileName(NULL, Dolphin_exe_Path, 2048);
+ GetModuleFileName(nullptr, Dolphin_exe_Path, 2048);
DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path);
DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\'));
}
@@ -647,7 +647,7 @@ std::string GetSysDirectory()
#endif
sysDir += DIR_SEP;
- INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str());
+ LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str());
return sysDir;
}
@@ -666,8 +666,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
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") ?
+ paths[D_USER_IDX] = std::string(getenv("HOME") ?
+ getenv("HOME") : getenv("PWD") ?
getenv("PWD") : "") + DIR_SEP EMU_DATA_DIR DIR_SEP;
#endif
@@ -676,6 +676,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
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_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
+ paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
@@ -694,7 +696,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
{
if (!FileUtil::IsDirectory(newPath))
{
- WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str());
+ LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
return paths[DirIDX];
}
else
@@ -717,6 +719,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
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_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
@@ -749,23 +752,10 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
}
}
-
+
return paths[DirIDX];
}
-//std::string GetThemeDir(const std::string& theme_name)
-//{
-// std::string dir = FileUtil::GetUserPath(D_THEMES_IDX) + theme_name + "/";
-//
-//#if !defined(_WIN32)
-// // If theme does not exist in user's dir load from shared directory
-// if (!FileUtil::Exists(dir))
-// dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/";
-//#endif
-//
-// return dir;
-//}
-
size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename)
{
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
@@ -826,7 +816,7 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
}
IOFile::IOFile()
- : m_file(NULL), m_good(true)
+ : m_file(nullptr), m_good(true)
{}
IOFile::IOFile(std::FILE* file)
@@ -834,7 +824,7 @@ IOFile::IOFile(std::FILE* file)
{}
IOFile::IOFile(const std::string& filename, const char openmode[])
- : m_file(NULL), m_good(true)
+ : m_file(nullptr), m_good(true)
{
Open(filename, openmode);
}
@@ -845,7 +835,7 @@ IOFile::~IOFile()
}
IOFile::IOFile(IOFile&& other)
- : m_file(NULL), m_good(true)
+ : m_file(nullptr), m_good(true)
{
Swap(other);
}
@@ -880,14 +870,14 @@ bool IOFile::Close()
if (!IsOpen() || 0 != std::fclose(m_file))
m_good = false;
- m_file = NULL;
+ m_file = nullptr;
return m_good;
}
std::FILE* IOFile::ReleaseHandle()
{
std::FILE* const ret = m_file;
- m_file = NULL;
+ m_file = nullptr;
return ret;
}
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 173ce662..e691b613 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -16,33 +16,35 @@
// User directory indices for GetUserPath
enum {
- D_USER_IDX,
- D_ROOT_IDX,
- D_CONFIG_IDX,
- D_GAMECONFIG_IDX,
- D_MAPS_IDX,
- D_CACHE_IDX,
- D_SHADERCACHE_IDX,
- D_SHADERS_IDX,
- D_STATESAVES_IDX,
- D_SCREENSHOTS_IDX,
- D_SDMC_IDX,
- D_HIRESTEXTURES_IDX,
- D_DUMP_IDX,
- D_DUMPFRAMES_IDX,
- D_DUMPAUDIO_IDX,
- D_DUMPTEXTURES_IDX,
- D_DUMPDSP_IDX,
- D_LOGS_IDX,
- D_SYSCONF_IDX,
- F_EMUCONFIG_IDX,
- F_DEBUGGERCONFIG_IDX,
- F_LOGGERCONFIG_IDX,
- F_MAINLOG_IDX,
- F_RAMDUMP_IDX,
- F_ARAMDUMP_IDX,
- F_SYSCONF_IDX,
- NUM_PATH_INDICES
+ D_USER_IDX,
+ D_ROOT_IDX,
+ D_CONFIG_IDX,
+ D_GAMECONFIG_IDX,
+ D_MAPS_IDX,
+ D_CACHE_IDX,
+ D_SHADERCACHE_IDX,
+ D_SHADERS_IDX,
+ D_STATESAVES_IDX,
+ D_SCREENSHOTS_IDX,
+ D_SDMC_IDX,
+ D_SAVEDATA_IDX,
+ D_SYSDATA_IDX,
+ D_HIRESTEXTURES_IDX,
+ D_DUMP_IDX,
+ D_DUMPFRAMES_IDX,
+ D_DUMPAUDIO_IDX,
+ D_DUMPTEXTURES_IDX,
+ D_DUMPDSP_IDX,
+ D_LOGS_IDX,
+ D_SYSCONF_IDX,
+ F_EMUCONFIG_IDX,
+ F_DEBUGGERCONFIG_IDX,
+ F_LOGGERCONFIG_IDX,
+ F_MAINLOG_IDX,
+ F_RAMDUMP_IDX,
+ F_ARAMDUMP_IDX,
+ F_SYSCONF_IDX,
+ NUM_PATH_INDICES
};
namespace FileUtil
@@ -51,11 +53,11 @@ namespace FileUtil
// FileSystem tree node/
struct FSTEntry
{
- bool isDirectory;
- u64 size; // file length or number of entries from children
- std::string physicalName; // name on disk
- std::string virtualName; // name in FST names table
- std::vector<FSTEntry> children;
+ bool isDirectory;
+ u64 size; // file length or number of entries from children
+ std::string physicalName; // name on disk
+ std::string virtualName; // name in FST names table
+ std::vector<FSTEntry> children;
};
// Returns true if file filename exists
@@ -148,86 +150,86 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
class IOFile : public NonCopyable
{
public:
- IOFile();
- IOFile(std::FILE* file);
- IOFile(const std::string& filename, const char openmode[]);
+ IOFile();
+ IOFile(std::FILE* file);
+ IOFile(const std::string& filename, const char openmode[]);
- ~IOFile();
+ ~IOFile();
- IOFile(IOFile&& other);
- IOFile& operator=(IOFile&& other);
+ IOFile(IOFile&& other);
+ IOFile& operator=(IOFile&& other);
- void Swap(IOFile& other);
+ void Swap(IOFile& other);
- bool Open(const std::string& filename, const char openmode[]);
- bool Close();
+ bool Open(const std::string& filename, const char openmode[]);
+ bool Close();
- template <typename T>
- size_t ReadArray(T* data, size_t length)
- {
- if (!IsOpen()) {
- m_good = false;
- return -1;
- }
+ template <typename T>
+ size_t ReadArray(T* data, size_t length)
+ {
+ if (!IsOpen()) {
+ m_good = false;
+ return -1;
+ }
- size_t items_read = std::fread(data, sizeof(T), length, m_file);
- if (items_read != length)
- m_good = false;
+ size_t items_read = std::fread(data, sizeof(T), length, m_file);
+ if (items_read != length)
+ m_good = false;
- return items_read;
- }
+ return items_read;
+ }
- template <typename T>
- size_t WriteArray(const T* data, size_t length)
- {
- if (!IsOpen()) {
- m_good = false;
- return -1;
- }
+ template <typename T>
+ size_t WriteArray(const T* data, size_t length)
+ {
+ if (!IsOpen()) {
+ m_good = false;
+ return -1;
+ }
- size_t items_written = std::fwrite(data, sizeof(T), length, m_file);
- if (items_written != length)
- m_good = false;
+ size_t items_written = std::fwrite(data, sizeof(T), length, m_file);
+ if (items_written != length)
+ m_good = false;
- return items_written;
- }
+ return items_written;
+ }
- size_t ReadBytes(void* data, size_t length)
- {
- return ReadArray(reinterpret_cast<char*>(data), length);
- }
+ size_t ReadBytes(void* data, size_t length)
+ {
+ return ReadArray(reinterpret_cast<char*>(data), length);
+ }
- size_t WriteBytes(const void* data, size_t length)
- {
- return WriteArray(reinterpret_cast<const char*>(data), length);
- }
+ size_t WriteBytes(const void* data, size_t length)
+ {
+ return WriteArray(reinterpret_cast<const char*>(data), length);
+ }
- bool IsOpen() { return NULL != m_file; }
+ bool IsOpen() { return nullptr != m_file; }
- // m_good is set to false when a read, write or other function fails
- bool IsGood() { return m_good; }
- operator void*() { return m_good ? m_file : NULL; }
+ // m_good is set to false when a read, write or other function fails
+ bool IsGood() { return m_good; }
+ operator void*() { return m_good ? m_file : nullptr; }
- std::FILE* ReleaseHandle();
+ std::FILE* ReleaseHandle();
- std::FILE* GetHandle() { return m_file; }
+ std::FILE* GetHandle() { return m_file; }
- void SetHandle(std::FILE* file);
+ void SetHandle(std::FILE* file);
- bool Seek(s64 off, int origin);
- u64 Tell();
- u64 GetSize();
- bool Resize(u64 size);
- bool Flush();
+ bool Seek(s64 off, int origin);
+ u64 Tell();
+ u64 GetSize();
+ bool Resize(u64 size);
+ bool Flush();
- // clear error state
- void Clear() { m_good = true; std::clearerr(m_file); }
+ // clear error state
+ void Clear() { m_good = true; std::clearerr(m_file); }
- std::FILE* m_file;
- bool m_good;
+ std::FILE* m_file;
+ bool m_good;
private:
- IOFile(IOFile&);
- IOFile& operator=(IOFile& other);
+ IOFile(IOFile&);
+ IOFile& operator=(IOFile& other);
};
} // namespace
@@ -237,8 +239,8 @@ template <typename T>
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode)
{
#ifdef _WIN32
- fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
+ fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
#else
- fstream.open(filename.c_str(), openmode);
+ fstream.open(filename.c_str(), openmode);
#endif
}
diff --git a/src/common/hash.cpp b/src/common/hash.cpp
index d2ebc734..2ddcfe6b 100644
--- a/src/common/hash.cpp
+++ b/src/common/hash.cpp
@@ -115,15 +115,15 @@ inline u64 getblock(const u64 * p, int i)
inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2)
{
- k1 *= c1;
- k1 = _rotl64(k1,23);
+ k1 *= c1;
+ k1 = _rotl64(k1,23);
k1 *= c2;
h1 ^= k1;
h1 += h2;
h2 = _rotl64(h2,41);
- k2 *= c2;
+ k2 *= c2;
k2 = _rotl64(k2,23);
k2 *= c1;
h2 ^= k2;
@@ -250,7 +250,7 @@ u64 GetCRC32(const u8 *src, int len, u32 samples)
}
-/*
+/*
* NOTE: This hash function is used for custom texture loading/dumping, so
* it should not be changed, which would require all custom textures to be
* recalculated for their new hash values. If the hashing function is
@@ -273,7 +273,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
u64 k = data[0];
data+=Step;
k *= m;
- k ^= k >> r;
+ k ^= k >> r;
k *= m;
h ^= k;
h *= m;
@@ -292,13 +292,13 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
case 1: h ^= u64(data2[0]);
h *= m;
};
-
+
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
-}
+}
#else
// CRC32 hash using the SSE4.2 instruction
u64 GetCRC32(const u8 *src, int len, u32 samples)
@@ -351,15 +351,15 @@ inline u32 fmix32(u32 h)
inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2)
{
- k1 *= c1;
- k1 = _rotl(k1,11);
+ k1 *= c1;
+ k1 = _rotl(k1,11);
k1 *= c2;
h1 ^= k1;
h1 += h2;
h2 = _rotl(h2,17);
- k2 *= c2;
+ k2 *= c2;
k2 = _rotl(k2,11);
k2 *= c1;
h2 ^= k2;
@@ -405,7 +405,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples)
//----------
// tail
-
+
const u8 * tail = (const u8*)(data + nblocks*8);
u32 k1 = 0;
@@ -439,7 +439,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples)
out[0] = h1;
out[1] = h2;
-
+
return *((u64 *)&out);
}
@@ -463,11 +463,11 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
{
u64 k = data[0];
data+=Step;
- k *= m;
- k ^= k >> r;
+ k *= m;
+ k ^= k >> r;
k *= m;
h ^= k;
- h *= m;
+ h *= m;
}
const u8 * data2 = (const u8*)end;
@@ -483,7 +483,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
case 1: h ^= u64(data2[0]);
h *= m;
};
-
+
h ^= h >> r;
h *= m;
h ^= h >> r;
diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h
index 96dce315..bb1b5174 100644
--- a/src/common/linear_disk_cache.h
+++ b/src/common/linear_disk_cache.h
@@ -64,13 +64,13 @@ public:
m_file.seekg(0, std::ios::beg);
std::fstream::pos_type start_pos = m_file.tellg();
std::streamoff file_size = end_pos - start_pos;
-
+
if (m_file.is_open() && ValidateHeader())
{
// good header, read some key/value pairs
K key;
- V *value = NULL;
+ V *value = nullptr;
u32 value_size;
u32 entry_number;
@@ -87,7 +87,7 @@ public:
// read key/value and pass to reader
if (Read(&key) &&
- Read(value, value_size) &&
+ Read(value, value_size) &&
Read(&entry_number) &&
entry_number == m_num_entries+1)
{
@@ -115,7 +115,7 @@ public:
WriteHeader();
return 0;
}
-
+
void Sync()
{
m_file.flush();
diff --git a/src/common/log.h b/src/common/log.h
index bfd73f8a..663eda9a 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -4,104 +4,9 @@
#pragma once
-#ifndef LOGGING
-#define LOGGING
-#endif
-
-enum {
- OS_LEVEL, // Printed by the emulated operating system
- NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports.
- ERROR_LEVEL, // Critical errors
- WARNING_LEVEL, // Something is suspicious.
- INFO_LEVEL, // General information.
- DEBUG_LEVEL, // Detailed debugging - might make things slow.
-};
-
-namespace LogTypes
-{
-
-enum LOG_TYPE {
- ACTIONREPLAY,
- AUDIO,
- AUDIO_INTERFACE,
- BOOT,
- COMMANDPROCESSOR,
- COMMON,
- CONSOLE,
- CONFIG,
- DISCIO,
- FILEMON,
- DSPHLE,
- DSPLLE,
- DSP_MAIL,
- DSPINTERFACE,
- DVDINTERFACE,
- DYNA_REC,
- EXPANSIONINTERFACE,
- GDB_STUB,
- ARM11,
- GSP,
- OSHLE,
- MASTER_LOG,
- MEMMAP,
- MEMCARD_MANAGER,
- OSREPORT,
- PAD,
- PROCESSORINTERFACE,
- PIXELENGINE,
- SERIALINTERFACE,
- SP1,
- STREAMINGINTERFACE,
- VIDEO,
- VIDEOINTERFACE,
- LOADER,
- FILESYS,
- WII_IPC_DVD,
- WII_IPC_ES,
- WII_IPC_FILEIO,
- WII_IPC_HID,
- KERNEL,
- SVC,
- NDMA,
- HLE,
- RENDER,
- GPU,
- HW,
- TIME,
- NETPLAY,
-
- NUMBER_OF_LOGS // Must be last
-};
-
-// FIXME: should this be removed?
-enum LOG_LEVELS {
- LOS = OS_LEVEL,
- LNOTICE = NOTICE_LEVEL,
- LERROR = ERROR_LEVEL,
- LWARNING = WARNING_LEVEL,
- LINFO = INFO_LEVEL,
- LDEBUG = DEBUG_LEVEL,
-};
-
-#define LOGTYPES_LEVELS LogTypes::LOG_LEVELS
-#define LOGTYPES_TYPE LogTypes::LOG_TYPE
-
-} // namespace
-
-void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line,
- const char* function, const char* fmt, ...)
-#ifdef __GNUC__
- __attribute__((format(printf, 6, 7)))
-#endif
- ;
-
-#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
-#define MAX_LOGLEVEL LDEBUG
-#else
-#ifndef MAX_LOGLEVEL
-#define MAX_LOGLEVEL LWARNING
-#endif // loglevel
-#endif // logging
+#include "common/common_funcs.h"
+#include "common/msg_handler.h"
+#include "common/logging/log.h"
#ifdef _WIN32
#ifndef __func__
@@ -109,29 +14,16 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
#endif
#endif
-// Let the compiler optimize this out
-#define GENERIC_LOG(t, v, ...) { \
- if (v <= LogTypes::MAX_LOGLEVEL) \
- GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \
- }
-
-#define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0)
-#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
-#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
-#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
-#define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0)
-#define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0)
-
-#if MAX_LOGLEVEL >= DEBUG_LEVEL
+#if _DEBUG
#define _dbg_assert_(_t_, _a_) \
if (!(_a_)) {\
- ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
+ LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
__LINE__, __FILE__, __TIME__); \
if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \
}
#define _dbg_assert_msg_(_t_, _a_, ...)\
if (!(_a_)) {\
- ERROR_LOG(_t_, __VA_ARGS__); \
+ LOG_CRITICAL(_t_, __VA_ARGS__); \
if (!PanicYesNo(__VA_ARGS__)) {Crash();} \
}
#define _dbg_update_() Host_UpdateLogDisplay();
@@ -143,11 +35,10 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
#define _dbg_assert_(_t_, _a_) {}
#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {}
#endif // dbg_assert
-#endif // MAX_LOGLEVEL DEBUG
+#endif
#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_)
-#ifndef GEKKO
#ifdef _WIN32
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
if (!(_a_)) {\
@@ -159,6 +50,3 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \
}
#endif // WIN32
-#else // GEKKO
-#define _assert_msg_(_t_, _a_, _fmt_, ...)
-#endif
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp
deleted file mode 100644
index 4d590d98..00000000
--- a/src/common/log_manager.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include <algorithm>
-
-#include "common/log_manager.h"
-#include "common/console_listener.h"
-#include "common/timer.h"
-#include "common/thread.h"
-
-void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
- const char* function, const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
-
- if (LogManager::GetInstance()) {
- LogManager::GetInstance()->Log(level, type,
- file, line, function, fmt, args);
- }
- va_end(args);
-}
-
-LogManager *LogManager::m_logManager = NULL;
-
-LogManager::LogManager()
-{
- // create log files
- m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log");
- m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot");
- m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common");
- m_Log[LogTypes::CONFIG] = new LogContainer("CONFIG", "Configuration");
- m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO");
- m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor");
- m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad");
- m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine");
- m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc");
- m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt");
- m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt");
- m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt");
- m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap");
- m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1");
- m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt");
- m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface");
- m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface");
- m_Log[LogTypes::GSP] = new LogContainer("GSP", "GSP");
- m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt");
- m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
- m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt");
- m_Log[LogTypes::ARM11] = new LogContainer("ARM11", "ARM11");
- m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");
- m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE");
- m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE");
- m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails");
- m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend");
- m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator");
- m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "JIT");
- m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
- m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
- m_Log[LogTypes::TIME] = new LogContainer("Time", "Core Timing");
- m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader");
- m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System");
- m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID");
- m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE");
- m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD");
- m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES");
- m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO");
- m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER");
- m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU");
- m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE");
- m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA");
- m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation");
- m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware");
- m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
- m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager");
- m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay");
-
- m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str());
- m_consoleLog = new ConsoleListener();
- m_debuggerLog = new DebuggerLogListener();
-
- for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
- {
- m_Log[i]->SetEnable(true);
- m_Log[i]->AddListener(m_fileLog);
- m_Log[i]->AddListener(m_consoleLog);
-#ifdef _MSC_VER
- if (IsDebuggerPresent())
- m_Log[i]->AddListener(m_debuggerLog);
-#endif
- }
-
- m_consoleLog->Open();
-}
-
-LogManager::~LogManager()
-{
- for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
- {
- m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
- m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog);
- m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog);
- }
-
- for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
- delete m_Log[i];
-
- delete m_fileLog;
- delete m_consoleLog;
- delete m_debuggerLog;
-}
-
-void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
- int line, const char* function, const char *fmt, va_list args)
-{
- char temp[MAX_MSGLEN];
- char msg[MAX_MSGLEN * 2];
- LogContainer *log = m_Log[type];
-
- if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
- return;
-
- Common::CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args);
-
- static const char level_to_char[7] = "ONEWID";
- sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line,
- level_to_char[(int)level], log->GetShortName(), function, temp);
-
-#ifdef ANDROID
- Host_SysMessage(msg);
-#endif
- log->Trigger(level, msg);
-}
-
-void LogManager::Init()
-{
- m_logManager = new LogManager();
-}
-
-void LogManager::Shutdown()
-{
- delete m_logManager;
- m_logManager = NULL;
-}
-
-LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable)
- : m_enable(enable)
-{
- strncpy(m_fullName, fullName, 128);
- strncpy(m_shortName, shortName, 32);
- m_level = LogTypes::MAX_LOGLEVEL;
-}
-
-// LogContainer
-void LogContainer::AddListener(LogListener *listener)
-{
- std::lock_guard<std::mutex> lk(m_listeners_lock);
- m_listeners.insert(listener);
-}
-
-void LogContainer::RemoveListener(LogListener *listener)
-{
- std::lock_guard<std::mutex> lk(m_listeners_lock);
- m_listeners.erase(listener);
-}
-
-void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg)
-{
- std::lock_guard<std::mutex> lk(m_listeners_lock);
-
- std::set<LogListener*>::const_iterator i;
- for (i = m_listeners.begin(); i != m_listeners.end(); ++i)
- {
- (*i)->Log(level, msg);
- }
-}
-
-FileLogListener::FileLogListener(const char *filename)
-{
- OpenFStream(m_logfile, filename, std::ios::app);
- SetEnable(true);
-}
-
-void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
-{
- if (!IsEnabled() || !IsValid())
- return;
-
- std::lock_guard<std::mutex> lk(m_log_lock);
- m_logfile << msg << std::flush;
-}
-
-void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
-{
-#if _MSC_VER
- ::OutputDebugStringA(msg);
-#endif
-}
diff --git a/src/common/log_manager.h b/src/common/log_manager.h
deleted file mode 100644
index de1d16ee..00000000
--- a/src/common/log_manager.h
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/log.h"
-#include "common/string_util.h"
-#include "common/file_util.h"
-
-#include <cstring>
-#include <set>
-#include <mutex>
-
-#define MAX_MESSAGES 8000
-#define MAX_MSGLEN 1024
-
-
-// pure virtual interface
-class LogListener
-{
-public:
- virtual ~LogListener() {}
-
- virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0;
-};
-
-class FileLogListener : public LogListener
-{
-public:
- FileLogListener(const char *filename);
-
- void Log(LogTypes::LOG_LEVELS, const char *msg) override;
-
- bool IsValid() { return !m_logfile.fail(); }
- bool IsEnabled() const { return m_enable; }
- void SetEnable(bool enable) { m_enable = enable; }
-
- const char* GetName() const { return "file"; }
-
-private:
- std::mutex m_log_lock;
- std::ofstream m_logfile;
- bool m_enable;
-};
-
-class DebuggerLogListener : public LogListener
-{
-public:
- void Log(LogTypes::LOG_LEVELS, const char *msg) override;
-};
-
-class LogContainer
-{
-public:
- LogContainer(const char* shortName, const char* fullName, bool enable = false);
-
- const char* GetShortName() const { return m_shortName; }
- const char* GetFullName() const { return m_fullName; }
-
- void AddListener(LogListener* listener);
- void RemoveListener(LogListener* listener);
-
- void Trigger(LogTypes::LOG_LEVELS, const char *msg);
-
- bool IsEnabled() const { return m_enable; }
- void SetEnable(bool enable) { m_enable = enable; }
-
- LogTypes::LOG_LEVELS GetLevel() const { return m_level; }
-
- void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; }
-
- bool HasListeners() const { return !m_listeners.empty(); }
-
-private:
- char m_fullName[128];
- char m_shortName[32];
- bool m_enable;
- LogTypes::LOG_LEVELS m_level;
- std::mutex m_listeners_lock;
- std::set<LogListener*> m_listeners;
-};
-
-class ConsoleListener;
-
-class LogManager : NonCopyable
-{
-private:
- LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS];
- FileLogListener *m_fileLog;
- ConsoleListener *m_consoleLog;
- DebuggerLogListener *m_debuggerLog;
- static LogManager *m_logManager; // Singleton. Ugh.
-
- LogManager();
- ~LogManager();
-public:
-
- static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; }
-
- void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
- const char* function, const char *fmt, va_list args);
-
- void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
- {
- m_Log[type]->SetLevel(level);
- }
-
- void SetEnable(LogTypes::LOG_TYPE type, bool enable)
- {
- m_Log[type]->SetEnable(enable);
- }
-
- bool IsEnabled(LogTypes::LOG_TYPE type) const
- {
- return m_Log[type]->IsEnabled();
- }
-
- const char* GetShortName(LogTypes::LOG_TYPE type) const
- {
- return m_Log[type]->GetShortName();
- }
-
- const char* GetFullName(LogTypes::LOG_TYPE type) const
- {
- return m_Log[type]->GetFullName();
- }
-
- void AddListener(LogTypes::LOG_TYPE type, LogListener *listener)
- {
- m_Log[type]->AddListener(listener);
- }
-
- void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener)
- {
- m_Log[type]->RemoveListener(listener);
- }
-
- FileLogListener *GetFileListener() const
- {
- return m_fileLog;
- }
-
- ConsoleListener *GetConsoleListener() const
- {
- return m_consoleLog;
- }
-
- DebuggerLogListener *GetDebuggerListener() const
- {
- return m_debuggerLog;
- }
-
- static LogManager* GetInstance()
- {
- return m_logManager;
- }
-
- static void SetInstance(LogManager *logManager)
- {
- m_logManager = logManager;
- }
-
- static void Init();
- static void Shutdown();
-};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
new file mode 100644
index 00000000..e79b8460
--- /dev/null
+++ b/src/common/logging/backend.cpp
@@ -0,0 +1,151 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "common/log.h" // For _dbg_assert_
+
+#include "common/logging/backend.h"
+#include "common/logging/log.h"
+#include "common/logging/text_formatter.h"
+
+namespace Log {
+
+static std::shared_ptr<Logger> global_logger;
+
+/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
+#define ALL_LOG_CLASSES() \
+ CLS(Log) \
+ CLS(Common) \
+ SUB(Common, Filesystem) \
+ SUB(Common, Memory) \
+ CLS(Core) \
+ SUB(Core, ARM11) \
+ CLS(Config) \
+ CLS(Debug) \
+ SUB(Debug, Emulated) \
+ SUB(Debug, GPU) \
+ SUB(Debug, Breakpoint) \
+ CLS(Kernel) \
+ SUB(Kernel, SVC) \
+ CLS(Service) \
+ SUB(Service, SRV) \
+ SUB(Service, FS) \
+ SUB(Service, APT) \
+ SUB(Service, GSP) \
+ SUB(Service, AC) \
+ SUB(Service, PTM) \
+ SUB(Service, CFG) \
+ SUB(Service, DSP) \
+ SUB(Service, HID) \
+ CLS(HW) \
+ SUB(HW, Memory) \
+ SUB(HW, GPU) \
+ CLS(Frontend) \
+ CLS(Render) \
+ SUB(Render, Software) \
+ SUB(Render, OpenGL) \
+ CLS(Loader)
+
+Logger::Logger() {
+ // Register logging classes so that they can be queried at runtime
+ size_t parent_class;
+ all_classes.reserve((size_t)Class::Count);
+
+#define CLS(x) \
+ all_classes.push_back(Class::x); \
+ parent_class = all_classes.size() - 1;
+#define SUB(x, y) \
+ all_classes.push_back(Class::x##_##y); \
+ all_classes[parent_class].num_children += 1;
+
+ ALL_LOG_CLASSES()
+#undef CLS
+#undef SUB
+
+ // Ensures that ALL_LOG_CLASSES isn't missing any entries.
+ _dbg_assert_(Log, all_classes.size() == (size_t)Class::Count);
+}
+
+// GetClassName is a macro defined by Windows.h, grrr...
+const char* Logger::GetLogClassName(Class log_class) {
+ switch (log_class) {
+#define CLS(x) case Class::x: return #x;
+#define SUB(x, y) case Class::x##_##y: return #x "." #y;
+ ALL_LOG_CLASSES()
+#undef CLS
+#undef SUB
+ }
+ return "Unknown";
+}
+
+const char* Logger::GetLevelName(Level log_level) {
+#define LVL(x) case Level::x: return #x
+ switch (log_level) {
+ LVL(Trace);
+ LVL(Debug);
+ LVL(Info);
+ LVL(Warning);
+ LVL(Error);
+ LVL(Critical);
+ }
+ return "Unknown";
+#undef LVL
+}
+
+void Logger::LogMessage(Entry entry) {
+ ring_buffer.Push(std::move(entry));
+}
+
+size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) {
+ return ring_buffer.BlockingPop(out_buffer, buffer_len);
+}
+
+std::shared_ptr<Logger> InitGlobalLogger() {
+ global_logger = std::make_shared<Logger>();
+ return global_logger;
+}
+
+Entry CreateEntry(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+ const char* format, va_list args) {
+ using std::chrono::steady_clock;
+ using std::chrono::duration_cast;
+
+ static steady_clock::time_point time_origin = steady_clock::now();
+
+ std::array<char, 4 * 1024> formatting_buffer;
+
+ Entry entry;
+ entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
+ entry.log_class = log_class;
+ entry.log_level = log_level;
+
+ snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr);
+ entry.location = std::string(formatting_buffer.data());
+
+ vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
+ entry.message = std::string(formatting_buffer.data());
+
+ return std::move(entry);
+}
+
+void LogMessage(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+ const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ Entry entry = CreateEntry(log_class, log_level,
+ filename, line_nr, function, format, args);
+ va_end(args);
+
+ if (global_logger != nullptr && !global_logger->IsClosed()) {
+ global_logger->LogMessage(std::move(entry));
+ } else {
+ // Fall back to directly printing to stderr
+ PrintMessage(entry);
+ }
+}
+
+}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
new file mode 100644
index 00000000..ae270efe
--- /dev/null
+++ b/src/common/logging/backend.h
@@ -0,0 +1,134 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstdarg>
+#include <memory>
+#include <vector>
+
+#include "common/concurrent_ring_buffer.h"
+
+#include "common/logging/log.h"
+
+namespace Log {
+
+/**
+ * A log entry. Log entries are store in a structured format to permit more varied output
+ * formatting on different frontends, as well as facilitating filtering and aggregation.
+ */
+struct Entry {
+ std::chrono::microseconds timestamp;
+ Class log_class;
+ Level log_level;
+ std::string location;
+ std::string message;
+
+ Entry() = default;
+
+ // TODO(yuriks) Use defaulted move constructors once MSVC supports them
+#define MOVE(member) member(std::move(o.member))
+ Entry(Entry&& o)
+ : MOVE(timestamp), MOVE(log_class), MOVE(log_level),
+ MOVE(location), MOVE(message)
+ {}
+#undef MOVE
+
+ Entry& operator=(const Entry&& o) {
+#define MOVE(member) member = std::move(o.member)
+ MOVE(timestamp);
+ MOVE(log_class);
+ MOVE(log_level);
+ MOVE(location);
+ MOVE(message);
+#undef MOVE
+ return *this;
+ }
+};
+
+struct ClassInfo {
+ Class log_class;
+
+ /**
+ * Total number of (direct or indirect) sub classes this class has. If any, they follow in
+ * sequence after this class in the class list.
+ */
+ unsigned int num_children = 0;
+
+ ClassInfo(Class log_class) : log_class(log_class) {}
+};
+
+/**
+ * Logging management class. This class has the dual purpose of acting as an exchange point between
+ * the logging clients and the log outputter, as well as containing reflection info about available
+ * log classes.
+ */
+class Logger {
+private:
+ using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>;
+
+public:
+ static const size_t QUEUE_CLOSED = Buffer::QUEUE_CLOSED;
+
+ Logger();
+
+ /**
+ * Returns a list of all vector classes and subclasses. The sequence returned is a pre-order of
+ * classes and subclasses, which together with the `num_children` field in ClassInfo, allows
+ * you to recover the hierarchy.
+ */
+ const std::vector<ClassInfo>& GetClasses() const { return all_classes; }
+
+ /**
+ * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
+ * instead of underscores as in the enumeration.
+ */
+ static const char* GetLogClassName(Class log_class);
+
+ /**
+ * Returns the name of the passed log level as a C-string.
+ */
+ static const char* GetLevelName(Level log_level);
+
+ /**
+ * Appends a messages to the log buffer.
+ * @note This function is thread safe.
+ */
+ void LogMessage(Entry entry);
+
+ /**
+ * Retrieves a batch of messages from the log buffer, blocking until they are available.
+ * @note This function is thread safe.
+ *
+ * @param out_buffer Destination buffer that will receive the log entries.
+ * @param buffer_len The maximum size of `out_buffer`.
+ * @return The number of entries stored. In case the logger is shutting down, `QUEUE_CLOSED` is
+ * returned, no entries are stored and the logger should shutdown.
+ */
+ size_t GetEntries(Entry* out_buffer, size_t buffer_len);
+
+ /**
+ * Initiates a shutdown of the logger. This will indicate to log output clients that they
+ * should shutdown.
+ */
+ void Close() { ring_buffer.Close(); }
+
+ /**
+ * Returns true if Close() has already been called on the Logger.
+ */
+ bool IsClosed() const { return ring_buffer.IsClosed(); }
+
+private:
+ Buffer ring_buffer;
+ std::vector<ClassInfo> all_classes;
+};
+
+/// Creates a log entry by formatting the given source location, and message.
+Entry CreateEntry(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+ const char* format, va_list args);
+/// Initializes the default Logger.
+std::shared_ptr<Logger> InitGlobalLogger();
+
+}
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
new file mode 100644
index 00000000..0cf9b05e
--- /dev/null
+++ b/src/common/logging/filter.cpp
@@ -0,0 +1,132 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "common/logging/filter.h"
+#include "common/logging/backend.h"
+#include "common/string_util.h"
+
+namespace Log {
+
+Filter::Filter(Level default_level) {
+ ResetAll(default_level);
+}
+
+void Filter::ResetAll(Level level) {
+ class_levels.fill(level);
+}
+
+void Filter::SetClassLevel(Class log_class, Level level) {
+ class_levels[static_cast<size_t>(log_class)] = level;
+}
+
+void Filter::SetSubclassesLevel(const ClassInfo& log_class, Level level) {
+ const size_t log_class_i = static_cast<size_t>(log_class.log_class);
+
+ const size_t begin = log_class_i + 1;
+ const size_t end = begin + log_class.num_children;
+ for (size_t i = begin; begin < end; ++i) {
+ class_levels[i] = level;
+ }
+}
+
+void Filter::ParseFilterString(const std::string& filter_str) {
+ auto clause_begin = filter_str.cbegin();
+ while (clause_begin != filter_str.cend()) {
+ auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
+
+ // If clause isn't empty
+ if (clause_end != clause_begin) {
+ ParseFilterRule(clause_begin, clause_end);
+ }
+
+ if (clause_end != filter_str.cend()) {
+ // Skip over the whitespace
+ ++clause_end;
+ }
+ clause_begin = clause_end;
+ }
+}
+
+template <typename It>
+static Level GetLevelByName(const It begin, const It end) {
+ for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
+ const char* level_name = Logger::GetLevelName(static_cast<Level>(i));
+ if (Common::ComparePartialString(begin, end, level_name)) {
+ return static_cast<Level>(i);
+ }
+ }
+ return Level::Count;
+}
+
+template <typename It>
+static Class GetClassByName(const It begin, const It end) {
+ for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
+ const char* level_name = Logger::GetLogClassName(static_cast<Class>(i));
+ if (Common::ComparePartialString(begin, end, level_name)) {
+ return static_cast<Class>(i);
+ }
+ }
+ return Class::Count;
+}
+
+template <typename InputIt, typename T>
+static InputIt find_last(InputIt begin, const InputIt end, const T& value) {
+ auto match = end;
+ while (begin != end) {
+ auto new_match = std::find(begin, end, value);
+ if (new_match != end) {
+ match = new_match;
+ ++new_match;
+ }
+ begin = new_match;
+ }
+ return match;
+}
+
+bool Filter::ParseFilterRule(const std::string::const_iterator begin,
+ const std::string::const_iterator end) {
+ auto level_separator = std::find(begin, end, ':');
+ if (level_separator == end) {
+ LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
+ std::string(begin, end).c_str());
+ return false;
+ }
+
+ const Level level = GetLevelByName(level_separator + 1, end);
+ if (level == Level::Count) {
+ LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
+ return false;
+ }
+
+ if (Common::ComparePartialString(begin, level_separator, "*")) {
+ ResetAll(level);
+ return true;
+ }
+
+ auto class_name_end = find_last(begin, level_separator, '.');
+ if (class_name_end != level_separator &&
+ !Common::ComparePartialString(class_name_end + 1, level_separator, "*")) {
+ class_name_end = level_separator;
+ }
+
+ const Class log_class = GetClassByName(begin, class_name_end);
+ if (log_class == Class::Count) {
+ LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
+ return false;
+ }
+
+ if (class_name_end == level_separator) {
+ SetClassLevel(log_class, level);
+ }
+ SetSubclassesLevel(log_class, level);
+ return true;
+}
+
+bool Filter::CheckMessage(Class log_class, Level level) const {
+ return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
+}
+
+}
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
new file mode 100644
index 00000000..32b14b15
--- /dev/null
+++ b/src/common/logging/filter.h
@@ -0,0 +1,63 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <array>
+#include <string>
+
+#include "common/logging/log.h"
+
+namespace Log {
+
+struct ClassInfo;
+
+/**
+ * Implements a log message filter which allows different log classes to have different minimum
+ * severity levels. The filter can be changed at runtime and can be parsed from a string to allow
+ * editing via the interface or loading from a configuration file.
+ */
+class Filter {
+public:
+ /// Initializes the filter with all classes having `default_level` as the minimum level.
+ Filter(Level default_level);
+
+ /// Resets the filter so that all classes have `level` as the minimum displayed level.
+ void ResetAll(Level level);
+ /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
+ void SetClassLevel(Class log_class, Level level);
+ /**
+ * Sets the minimum level of all of `log_class` subclasses to `level`. The level of `log_class`
+ * itself is not changed.
+ */
+ void SetSubclassesLevel(const ClassInfo& log_class, Level level);
+
+ /**
+ * Parses a filter string and applies it to this filter.
+ *
+ * A filter string consists of a space-separated list of filter rules, each of the format
+ * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods.
+ * A rule for a given class also affects all of its subclasses. `*` wildcards are allowed and
+ * can be used to apply a rule to all classes or to all subclasses of a class without affecting
+ * the parent class. `<level>` a severity level name which will be set as the minimum logging
+ * level of the matched classes. Rules are applied left to right, with each rule overriding
+ * previous ones in the sequence.
+ *
+ * A few examples of filter rules:
+ * - `*:Info` -- Resets the level of all classes to Info.
+ * - `Service:Info` -- Sets the level of Service and all subclasses (Service.FS, Service.APT,
+ * etc.) to Info.
+ * - `Service.*:Debug` -- Sets the level of all Service subclasses to Debug, while leaving the
+ * level of Service unchanged.
+ * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
+ */
+ void ParseFilterString(const std::string& filter_str);
+ bool ParseFilterRule(const std::string::const_iterator start, const std::string::const_iterator end);
+
+ /// Matches class/level combination against the filter, returning true if it passed.
+ bool CheckMessage(Class log_class, Level level) const;
+
+private:
+ std::array<Level, (size_t)Class::Count> class_levels;
+};
+
+}
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
new file mode 100644
index 00000000..1eec34fb
--- /dev/null
+++ b/src/common/logging/log.h
@@ -0,0 +1,115 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cassert>
+#include <chrono>
+#include <string>
+
+#include "common/common_types.h"
+
+namespace Log {
+
+/// Specifies the severity or level of detail of the log message.
+enum class Level : u8 {
+ Trace, ///< Extremely detailed and repetitive debugging information that is likely to
+ /// pollute logs.
+ Debug, ///< Less detailed debugging information.
+ Info, ///< Status information from important points during execution.
+ Warning, ///< Minor or potential problems found during execution of a task.
+ Error, ///< Major problems found during execution of a task that prevent it from being
+ /// completed.
+ Critical, ///< Major problems during execution that threathen the stability of the entire
+ /// application.
+
+ Count ///< Total number of logging levels
+};
+
+typedef u8 ClassType;
+
+/**
+ * Specifies the sub-system that generated the log message.
+ *
+ * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in log.cpp.
+ */
+enum class Class : ClassType {
+ Log, ///< Messages about the log system itself
+ Common, ///< Library routines
+ Common_Filesystem, ///< Filesystem interface library
+ Common_Memory, ///< Memory mapping and management functions
+ Core, ///< LLE emulation core
+ Core_ARM11, ///< ARM11 CPU core
+ Config, ///< Emulator configuration (including commandline)
+ Debug, ///< Debugging tools
+ Debug_Emulated, ///< Debug messages from the emulated programs
+ Debug_GPU, ///< GPU debugging tools
+ Debug_Breakpoint, ///< Logging breakpoints and watchpoints
+ Kernel, ///< The HLE implementation of the CTR kernel
+ Kernel_SVC, ///< Kernel system calls
+ Service, ///< HLE implementation of system services. Each major service
+ /// should have its own subclass.
+ Service_SRV, ///< The SRV (Service Directory) implementation
+ Service_FS, ///< The FS (Filesystem) service implementation
+ Service_APT, ///< The APT (Applets) service
+ Service_GSP, ///< The GSP (GPU control) service
+ Service_AC, ///< The AC (WiFi status) service
+ Service_PTM, ///< The PTM (Power status & misc.) service
+ Service_CFG, ///< The CFG (Configuration) service
+ Service_DSP, ///< The DSP (DSP control) service
+ Service_HID, ///< The HID (User input) service
+ HW, ///< Low-level hardware emulation
+ HW_Memory, ///< Memory-map and address translation
+ HW_GPU, ///< GPU control emulation
+ Frontend, ///< Emulator UI
+ Render, ///< Emulator video output and hardware acceleration
+ Render_Software, ///< Software renderer backend
+ Render_OpenGL, ///< OpenGL backend
+ Loader, ///< ROM loader
+
+ Count ///< Total number of logging classes
+};
+
+/**
+ * Level below which messages are simply discarded without buffering regardless of the display
+ * settings.
+ */
+const Level MINIMUM_LEVEL =
+#ifdef _DEBUG
+ Level::Trace;
+#else
+ Level::Debug;
+#endif
+
+/**
+ * Logs a message to the global logger. This proxy exists to avoid exposing the details of the
+ * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log
+ * messages, reducing unecessary recompilations.
+ */
+void LogMessage(Class log_class, Level log_level,
+ const char* filename, unsigned int line_nr, const char* function,
+#ifdef _MSC_VER
+ _Printf_format_string_
+#endif
+ const char* format, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 6, 7)))
+#endif
+ ;
+
+} // namespace Log
+
+#define LOG_GENERIC(log_class, log_level, ...) \
+ do { \
+ if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \
+ ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \
+ __FILE__, __LINE__, __func__, __VA_ARGS__); \
+ } while (0)
+
+#define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__)
+#define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__)
+#define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__)
+#define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__)
+#define LOG_ERROR( log_class, ...) LOG_GENERIC(log_class, Error, __VA_ARGS__)
+#define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__)
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
new file mode 100644
index 00000000..f6b02fd4
--- /dev/null
+++ b/src/common/logging/text_formatter.cpp
@@ -0,0 +1,136 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstdio>
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <Windows.h>
+#endif
+
+#include "common/logging/backend.h"
+#include "common/logging/filter.h"
+#include "common/logging/log.h"
+#include "common/logging/text_formatter.h"
+
+#include "common/string_util.h"
+
+namespace Log {
+
+// TODO(bunnei): This should be moved to a generic path manipulation library
+const char* TrimSourcePath(const char* path, const char* root) {
+ const char* p = path;
+
+ while (*p != '\0') {
+ const char* next_slash = p;
+ while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
+ ++next_slash;
+ }
+
+ bool is_src = Common::ComparePartialString(p, next_slash, root);
+ p = next_slash;
+
+ if (*p != '\0') {
+ ++p;
+ }
+ if (is_src) {
+ path = p;
+ }
+ }
+ return path;
+}
+
+void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
+ unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
+ unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
+
+ const char* class_name = Logger::GetLogClassName(entry.log_class);
+ const char* level_name = Logger::GetLevelName(entry.log_level);
+
+ snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s",
+ time_seconds, time_fractional, class_name, level_name,
+ TrimSourcePath(entry.location.c_str()), entry.message.c_str());
+}
+
+void PrintMessage(const Entry& entry) {
+ std::array<char, 4 * 1024> format_buffer;
+ FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
+ fputs(format_buffer.data(), stderr);
+ fputc('\n', stderr);
+}
+
+void PrintColoredMessage(const Entry& entry) {
+#ifdef _WIN32
+ static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
+
+ CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
+ GetConsoleScreenBufferInfo(console_handle, &original_info);
+
+ WORD color = 0;
+ switch (entry.log_level) {
+ case Level::Trace: // Grey
+ color = FOREGROUND_INTENSITY; break;
+ case Level::Debug: // Cyan
+ color = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ case Level::Info: // Bright gray
+ color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ case Level::Warning: // Bright yellow
+ color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
+ case Level::Error: // Bright red
+ color = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ case Level::Critical: // Bright magenta
+ color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
+ }
+
+ SetConsoleTextAttribute(console_handle, color);
+#else
+# define ESC "\x1b"
+ const char* color = "";
+ switch (entry.log_level) {
+ case Level::Trace: // Grey
+ color = ESC "[1;30m"; break;
+ case Level::Debug: // Cyan
+ color = ESC "[0;36m"; break;
+ case Level::Info: // Bright gray
+ color = ESC "[0;37m"; break;
+ case Level::Warning: // Bright yellow
+ color = ESC "[1;33m"; break;
+ case Level::Error: // Bright red
+ color = ESC "[1;31m"; break;
+ case Level::Critical: // Bright magenta
+ color = ESC "[1;35m"; break;
+ }
+
+ fputs(color, stderr);
+#endif
+
+ PrintMessage(entry);
+
+#ifdef _WIN32
+ SetConsoleTextAttribute(console_handle, original_info.wAttributes);
+#else
+ fputs(ESC "[0m", stderr);
+# undef ESC
+#endif
+}
+
+void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) {
+ std::array<Entry, 256> entry_buffer;
+
+ while (true) {
+ size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size());
+ if (num_entries == Logger::QUEUE_CLOSED) {
+ break;
+ }
+ 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);
+ }
+ }
+ }
+}
+
+}
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
new file mode 100644
index 00000000..1f73ca44
--- /dev/null
+++ b/src/common/logging/text_formatter.h
@@ -0,0 +1,41 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <memory>
+
+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
+ * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
+ * leaving only the path relative to the sources root.
+ *
+ * @param path The input file path as a null-terminated string
+ * @param root The name of the root source directory as a null-terminated string. Path up to and
+ * including the last occurence of this name will be stripped
+ * @return A pointer to the same string passed as `path`, but starting at the trimmed portion
+ */
+const char* TrimSourcePath(const char* path, const char* root = "src");
+
+/// Formats a log entry into the provided text buffer.
+void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
+/// Formats and prints a log entry to stderr.
+void PrintMessage(const Entry& entry);
+/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
+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);
+
+}
diff --git a/src/common/math_util.cpp b/src/common/math_util.cpp
index ab0e6b75..3613e82a 100644
--- a/src/common/math_util.cpp
+++ b/src/common/math_util.cpp
@@ -18,7 +18,7 @@ u32 ClassifyDouble(double dvalue)
value.d = dvalue;
u64 sign = value.i & DOUBLE_SIGN;
u64 exp = value.i & DOUBLE_EXP;
- if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP)
+ if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP)
{
// Nice normalized number.
return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
@@ -58,7 +58,7 @@ u32 ClassifyFloat(float fvalue)
value.f = fvalue;
u32 sign = value.i & FLOAT_SIGN;
u32 exp = value.i & FLOAT_EXP;
- if (exp > FLOAT_ZERO && exp < FLOAT_EXP)
+ if (exp > FLOAT_ZERO && exp < FLOAT_EXP)
{
// Nice normalized number.
return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
@@ -77,13 +77,13 @@ u32 ClassifyFloat(float fvalue)
// Denormalized number.
return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
}
- }
- else if (exp)
+ }
+ else if (exp)
{
// Infinite
return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
- }
- else
+ }
+ else
{
//Zero
return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
@@ -143,7 +143,7 @@ void Matrix33::RotateY(Matrix33 &mtx, float rad)
mtx.data[0] = c;
mtx.data[2] = s;
mtx.data[4] = 1;
- mtx.data[6] = -s;
+ mtx.data[6] = -s;
mtx.data[8] = c;
}
diff --git a/src/common/math_util.h b/src/common/math_util.h
index b32e7bb1..b10a25c1 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -7,6 +7,7 @@
#include "common/common.h"
#include <algorithm>
+#include <type_traits>
#include <vector>
namespace MathUtil
@@ -109,11 +110,11 @@ struct Rectangle
Rectangle(T theLeft, T theTop, T theRight, T theBottom)
: left(theLeft), top(theTop), right(theRight), bottom(theBottom)
{ }
-
+
bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; }
- T GetWidth() const { return abs(right - left); }
- T GetHeight() const { return abs(bottom - top); }
+ T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); }
+ T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); }
// If the rectangle is in a coordinate system with a lower-left origin, use
// this Clamp.
@@ -127,7 +128,7 @@ struct Rectangle
// If the rectangle is in a coordinate system with an upper-left origin,
// use this Clamp.
- void ClampUL(T x1, T y1, T x2, T y2)
+ void ClampUL(T x1, T y1, T x2, T y2)
{
if (left < x1) left = x1;
if (right > x2) right = x2;
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp
index 40d9c03a..9904d247 100644
--- a/src/common/mem_arena.cpp
+++ b/src/common/mem_arena.cpp
@@ -30,7 +30,7 @@
#endif
#ifdef IOS
-void* globalbase = NULL;
+void* globalbase = nullptr;
#endif
#ifdef ANDROID
@@ -38,7 +38,7 @@ void* globalbase = NULL;
// Hopefully this ABI will never change...
-#define ASHMEM_DEVICE "/dev/ashmem"
+#define ASHMEM_DEVICE "/dev/ashmem"
/*
* ashmem_create_region - creates a new ashmem region and returns the file
@@ -71,7 +71,7 @@ int ashmem_create_region(const char *name, size_t size)
return fd;
error:
- ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret);
+ LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret);
close(fd);
return ret;
}
@@ -121,7 +121,7 @@ void MemArena::GrabLowMemSpace(size_t size)
{
#ifdef _WIN32
#ifndef _XBOX
- hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL);
+ hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr);
GetSystemInfo(&sysInfo);
#endif
#elif defined(ANDROID)
@@ -130,7 +130,7 @@ void MemArena::GrabLowMemSpace(size_t size)
// Note that it appears that ashmem is pinned by default, so no need to pin.
if (fd < 0)
{
- ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
+ LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
return;
}
#else
@@ -148,12 +148,12 @@ void MemArena::GrabLowMemSpace(size_t size)
}
else if (errno != EEXIST)
{
- ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno));
+ LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno));
return;
}
}
if (ftruncate(fd, size) < 0)
- ERROR_LOG(MEMMAP, "Failed to allocate low memory space");
+ LOG_ERROR(Common_Memory, "Failed to allocate low memory space");
#endif
}
@@ -178,7 +178,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
#ifdef _XBOX
size = roundup(size);
// use 64kb pages
- void * ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
+ void * ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
return ptr;
#else
size = roundup(size);
@@ -197,7 +197,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
if (retval == MAP_FAILED)
{
- NOTICE_LOG(MEMMAP, "mmap failed");
+ LOG_ERROR(Common_Memory, "mmap failed");
return nullptr;
}
return retval;
@@ -243,8 +243,8 @@ u8* MemArena::Find4GBBase()
return base;
#else
#ifdef IOS
- void* base = NULL;
- if (globalbase == NULL){
+ void* base = nullptr;
+ if (globalbase == nullptr){
base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0);
if (base == MAP_FAILED) {
@@ -272,11 +272,11 @@ u8* MemArena::Find4GBBase()
// yeah, this could also be done in like two bitwise ops...
-#define SKIP(a_flags, b_flags)
-// if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY))
-// continue;
-// if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM))
-// continue;
+#define SKIP(a_flags, b_flags)
+//if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY))
+// continue;
+//if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM))
+// continue;
static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) {
// OK, we know where to find free space. Now grab it!
@@ -357,7 +357,7 @@ bail:
if (views[j].out_ptr_low && *views[j].out_ptr_low)
{
arena->ReleaseView(*views[j].out_ptr_low, views[j].size);
- *views[j].out_ptr_low = NULL;
+ *views[j].out_ptr_low = nullptr;
}
if (*views[j].out_ptr)
{
@@ -369,7 +369,7 @@ bail:
arena->ReleaseView(*views[j].out_ptr, views[j].size);
}
#endif
- *views[j].out_ptr = NULL;
+ *views[j].out_ptr = nullptr;
}
}
return false;
@@ -415,7 +415,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
#elif defined(_WIN32)
// Try a whole range of possible bases. Return once we got a valid one.
u32 max_base_addr = 0x7FFF0000 - 0x10000000;
- u8 *base = NULL;
+ u8 *base = nullptr;
for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000)
{
@@ -423,7 +423,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
base = (u8 *)base_addr;
if (Memory_TryBase(base, views, num_views, flags, arena))
{
- INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts);
+ LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts);
base_attempts = 0;
break;
}
@@ -442,7 +442,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
u8 *base = MemArena::Find4GBBase();
if (!Memory_TryBase(base, views, num_views, flags, arena))
{
- ERROR_LOG(MEMMAP, "MemoryMap_Setup: Failed finding a memory base.");
+ LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
return 0;
}
@@ -463,8 +463,8 @@ void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemAr
arena->ReleaseView(*views[i].out_ptr_low, views[i].size);
if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low))
arena->ReleaseView(*views[i].out_ptr, views[i].size);
- *views[i].out_ptr = NULL;
+ *views[i].out_ptr = nullptr;
if (views[i].out_ptr_low)
- *views[i].out_ptr_low = NULL;
+ *views[i].out_ptr_low = nullptr;
}
}
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
index bab7d9f7..ca8a2a9e 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -47,7 +47,7 @@ void* AllocateExecutableMemory(size_t size, bool low)
// printf("Mapped executable memory at %p (size %ld)\n", ptr,
// (unsigned long)size);
-
+
#ifdef _WIN32
if (ptr == nullptr)
{
@@ -55,7 +55,7 @@ void* AllocateExecutableMemory(size_t size, bool low)
if (ptr == MAP_FAILED)
{
ptr = nullptr;
-#endif
+#endif
PanicAlert("Failed to allocate executable memory");
}
#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT)
@@ -93,7 +93,7 @@ void* AllocateMemoryPages(size_t size)
// printf("Mapped memory at %p (size %ld)\n", ptr,
// (unsigned long)size);
- if (ptr == NULL)
+ if (ptr == nullptr)
PanicAlert("Failed to allocate raw memory");
return ptr;
@@ -104,19 +104,19 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
#ifdef _WIN32
void* ptr = _aligned_malloc(size,alignment);
#else
- void* ptr = NULL;
+ void* ptr = nullptr;
#ifdef ANDROID
ptr = memalign(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size) != 0)
- ERROR_LOG(MEMMAP, "Failed to allocate aligned memory");
+ LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
#endif
#endif
// printf("Mapped memory at %p (size %ld)\n", ptr,
// (unsigned long)size);
- if (ptr == NULL)
+ if (ptr == nullptr)
PanicAlert("Failed to allocate aligned memory");
return ptr;
@@ -127,11 +127,11 @@ void FreeMemoryPages(void* ptr, size_t size)
if (ptr)
{
#ifdef _WIN32
-
+
if (!VirtualFree(ptr, 0, MEM_RELEASE))
PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg());
- ptr = NULL; // Is this our responsibility?
-
+ ptr = nullptr; // Is this our responsibility?
+
#else
munmap(ptr, size);
#endif
@@ -184,7 +184,7 @@ std::string MemUsage()
// Print information about the memory usage of the process.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
- if (NULL == hProcess) return "MemUsage Error";
+ if (nullptr == hProcess) return "MemUsage Error";
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str());
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index cf6df44e..bc9d2618 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -23,9 +23,9 @@ const char* GetLastErrorMsg()
#ifdef _WIN32
static __declspec(thread) char err_str[buff_size] = {};
- FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- err_str, buff_size, NULL);
+ err_str, buff_size, nullptr);
#else
static __thread char err_str[buff_size] = {};
diff --git a/src/common/msg_handler.cpp b/src/common/msg_handler.cpp
index b3556aaa..7ffedc45 100644
--- a/src/common/msg_handler.cpp
+++ b/src/common/msg_handler.cpp
@@ -75,7 +75,7 @@ bool MsgAlert(bool yes_no, int Style, const char* format, ...)
Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args);
va_end(args);
- ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer);
+ 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))
diff --git a/src/common/msg_handler.h b/src/common/msg_handler.h
index 7c5319ec..9bfdf950 100644
--- a/src/common/msg_handler.h
+++ b/src/common/msg_handler.h
@@ -15,7 +15,7 @@ enum MSG_TYPE
CRITICAL
};
-typedef bool (*MsgAlertHandler)(const char* caption, const char* text,
+typedef bool (*MsgAlertHandler)(const char* caption, const char* text,
bool yes_no, int Style);
typedef std::string (*StringTranslator)(const char* text);
@@ -31,29 +31,29 @@ void SetEnableAlert(bool enable);
#ifndef GEKKO
#ifdef _WIN32
- #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__)
+ #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__)
+ #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__)
+ #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__)
+ #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
#else
// GEKKO
diff --git a/src/common/platform.h b/src/common/platform.h
index d9f09543..53d98fe7 100644
--- a/src/common/platform.h
+++ b/src/common/platform.h
@@ -77,7 +77,7 @@
inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
if (localtime_s(result, clock) == 0)
return result;
- return NULL;
+ return nullptr;
}
#else
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
new file mode 100644
index 00000000..1d3e5931
--- /dev/null
+++ b/src/common/scope_exit.h
@@ -0,0 +1,37 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace detail {
+ template <typename Func>
+ struct ScopeExitHelper {
+ explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
+ ~ScopeExitHelper() { func(); }
+
+ Func func;
+ };
+
+ template <typename Func>
+ ScopeExitHelper<Func> ScopeExit(Func&& func) { return ScopeExitHelper<Func>(std::move(func)); }
+}
+
+/**
+ * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
+ * for doing ad-hoc clean-up tasks in a function with multiple returns.
+ *
+ * Example usage:
+ * \code
+ * const int saved_val = g_foo;
+ * g_foo = 55;
+ * SCOPE_EXIT({ g_foo = saved_val; });
+ *
+ * if (Bar()) {
+ * return 0;
+ * } else {
+ * return 20;
+ * }
+ * \endcode
+ */
+#define SCOPE_EXIT(body) auto scope_exit_helper_##__LINE__ = detail::ScopeExit([&]() body)
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 61f0939c..6d9612fb 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -2,13 +2,14 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
-#include <algorithm>
+#include <boost/range/algorithm.hpp>
#include "common/common.h"
#include "common/string_util.h"
#ifdef _WIN32
#include <Windows.h>
+ #include <codecvt>
#else
#include <iconv.h>
#endif
@@ -17,20 +18,20 @@ namespace Common {
/// Make a string lowercase
std::string ToLower(std::string str) {
- std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+ boost::transform(str, str.begin(), ::tolower);
return str;
}
/// Make a string uppercase
std::string ToUpper(std::string str) {
- std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+ boost::transform(str, str.begin(), ::toupper);
return str;
}
// faster than sscanf
bool AsciiToHex(const char* _szValue, u32& result)
{
- char *endptr = NULL;
+ char *endptr = nullptr;
const u32 value = strtoul(_szValue, &endptr, 16);
if (!endptr || *endptr)
@@ -68,7 +69,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar
// will be present in the middle of a multibyte sequence.
//
// This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
- static locale_t c_locale = NULL;
+ static locale_t c_locale = nullptr;
if (!c_locale)
c_locale = _create_locale(LC_ALL, ".1252");
writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
@@ -91,7 +92,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar
std::string StringFromFormat(const char* format, ...)
{
va_list args;
- char *buf = NULL;
+ char *buf = nullptr;
#ifdef _WIN32
int required = 0;
@@ -106,7 +107,7 @@ std::string StringFromFormat(const char* format, ...)
#else
va_start(args, format);
if (vasprintf(&buf, format, args) < 0)
- ERROR_LOG(COMMON, "Unable to allocate memory for string");
+ LOG_ERROR(Common, "Unable to allocate memory for string");
va_end(args);
std::string temp = buf;
@@ -120,11 +121,11 @@ std::string ArrayToString(const u8 *data, u32 size, int line_len, bool spaces)
{
std::ostringstream oss;
oss << std::setfill('0') << std::hex;
-
+
for (int line = 0; size; ++data, --size)
{
oss << std::setw(2) << (int)*data;
-
+
if (line_len == ++line)
{
oss << '\n';
@@ -161,13 +162,13 @@ std::string StripQuotes(const std::string& s)
bool TryParse(const std::string &str, u32 *const output)
{
- char *endptr = NULL;
+ char *endptr = nullptr;
// Reset errno to a value other than ERANGE
errno = 0;
unsigned long value = strtoul(str.c_str(), &endptr, 0);
-
+
if (!endptr || *endptr)
return false;
@@ -293,7 +294,7 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
//#include <string>
//#include <assert.h>
-const char HEX2DEC[256] =
+const char HEX2DEC[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
@@ -326,7 +327,7 @@ std::string UriDecode(const std::string & sSrc)
const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
const size_t SRC_LEN = sSrc.length();
const unsigned char * const SRC_END = pSrc + SRC_LEN;
- const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%'
+ const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%'
char * const pStart = new char[SRC_LEN];
char * pEnd = pStart;
@@ -393,7 +394,7 @@ std::string UriEncode(const std::string & sSrc)
for (; pSrc < SRC_END; ++pSrc)
{
- if (SAFE[*pSrc])
+ if (SAFE[*pSrc])
*pEnd++ = *pSrc;
else
{
@@ -411,7 +412,19 @@ std::string UriEncode(const std::string & sSrc)
#ifdef _WIN32
-std::string UTF16ToUTF8(const std::wstring& input)
+std::string UTF16ToUTF8(const std::u16string& input)
+{
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
+ return convert.to_bytes(input);
+}
+
+std::u16string UTF8ToUTF16(const std::string& input)
+{
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
+ return convert.from_bytes(input);
+}
+
+static std::string UTF16ToUTF8(const std::wstring& input)
{
auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), nullptr, 0, nullptr, nullptr);
@@ -424,7 +437,7 @@ std::string UTF16ToUTF8(const std::wstring& input)
return output;
}
-std::wstring CPToUTF16(u32 code_page, const std::string& input)
+static std::wstring CPToUTF16(u32 code_page, const std::string& input)
{
auto const size = MultiByteToWideChar(code_page, 0, input.data(), input.size(), nullptr, 0);
@@ -437,7 +450,7 @@ std::wstring CPToUTF16(u32 code_page, const std::string& input)
return output;
}
-std::wstring UTF8ToUTF16(const std::string& input)
+std::wstring UTF8ToUTF16W(const std::string &input)
{
return CPToUTF16(CP_UTF8, input);
}
@@ -455,61 +468,123 @@ std::string CP1252ToUTF8(const std::string& input)
#else
template <typename T>
-std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input)
+static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input)
{
std::string result;
iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
- if ((iconv_t)-1 == conv_desc)
+ if ((iconv_t)(-1) == conv_desc)
{
- ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
+ LOG_ERROR(Common, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
+ iconv_close(conv_desc);
+ return {};
}
- else
- {
- size_t const in_bytes = sizeof(T) * input.size();
- size_t const out_buffer_size = 4 * in_bytes;
- std::string out_buffer;
- out_buffer.resize(out_buffer_size);
+ const size_t in_bytes = sizeof(T) * input.size();
+ // Multiply by 4, which is the max number of bytes to encode a codepoint
+ const size_t out_buffer_size = 4 * in_bytes;
- auto src_buffer = &input[0];
- size_t src_bytes = in_bytes;
- auto dst_buffer = &out_buffer[0];
- size_t dst_bytes = out_buffer.size();
+ std::string out_buffer;
+ out_buffer.resize(out_buffer_size);
- while (src_bytes != 0)
- {
- size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes,
- &dst_buffer, &dst_bytes);
+ auto src_buffer = &input[0];
+ size_t src_bytes = in_bytes;
+ auto dst_buffer = &out_buffer[0];
+ size_t dst_bytes = out_buffer.size();
- if ((size_t)-1 == iconv_result)
+ while (0 != src_bytes)
+ {
+ size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes,
+ &dst_buffer, &dst_bytes);
+
+ if (static_cast<size_t>(-1) == iconv_result)
+ {
+ if (EILSEQ == errno || EINVAL == errno)
{
- if (EILSEQ == errno || EINVAL == errno)
- {
- // Try to skip the bad character
- if (src_bytes != 0)
- {
- --src_bytes;
- ++src_buffer;
- }
- }
- else
+ // Try to skip the bad character
+ if (0 != src_bytes)
{
- ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno));
- break;
+ --src_bytes;
+ ++src_buffer;
}
}
+ else
+ {
+ LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno));
+ break;
+ }
}
+ }
+
+ out_buffer.resize(out_buffer_size - dst_bytes);
+ out_buffer.swap(result);
+
+ iconv_close(conv_desc);
+
+ return result;
+}
+
+std::u16string UTF8ToUTF16(const std::string& input)
+{
+ std::u16string result;
- out_buffer.resize(out_buffer_size - dst_bytes);
- out_buffer.swap(result);
-
+ iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
+ if ((iconv_t)(-1) == conv_desc)
+ {
+ LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: %s", strerror(errno));
iconv_close(conv_desc);
+ return {};
}
-
+
+ const size_t in_bytes = sizeof(char) * input.size();
+ // Multiply by 4, which is the max number of bytes to encode a codepoint
+ const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
+
+ std::u16string out_buffer;
+ out_buffer.resize(out_buffer_size);
+
+ char* src_buffer = const_cast<char*>(&input[0]);
+ size_t src_bytes = in_bytes;
+ char* dst_buffer = (char*)(&out_buffer[0]);
+ size_t dst_bytes = out_buffer.size();
+
+ while (0 != src_bytes)
+ {
+ size_t const iconv_result = iconv(conv_desc, &src_buffer, &src_bytes,
+ &dst_buffer, &dst_bytes);
+
+ if (static_cast<size_t>(-1) == iconv_result)
+ {
+ if (EILSEQ == errno || EINVAL == errno)
+ {
+ // Try to skip the bad character
+ if (0 != src_bytes)
+ {
+ --src_bytes;
+ ++src_buffer;
+ }
+ }
+ else
+ {
+ LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno));
+ break;
+ }
+ }
+ }
+
+ out_buffer.resize(out_buffer_size - dst_bytes);
+ out_buffer.swap(result);
+
+ iconv_close(conv_desc);
+
return result;
}
+std::string UTF16ToUTF8(const std::u16string& input)
+{
+ return CodeToUTF8("UTF-16LE", input);
+}
+
std::string CP1252ToUTF8(const std::string& input)
{
//return CodeToUTF8("CP1252//TRANSLIT", input);
@@ -523,19 +598,6 @@ std::string SHIFTJISToUTF8(const std::string& input)
return CodeToUTF8("SJIS", input);
}
-std::string UTF16ToUTF8(const std::wstring& input)
-{
- std::string result =
- // CodeToUTF8("UCS-2", input);
- // CodeToUTF8("UCS-2LE", input);
- // CodeToUTF8("UTF-16", input);
- CodeToUTF8("UTF-16LE", input);
-
- // TODO: why is this needed?
- result.erase(std::remove(result.begin(), result.end(), 0x00), result.end());
- return result;
-}
-
#endif
}
diff --git a/src/common/string_util.h b/src/common/string_util.h
index a41ccc69..7d75691b 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -63,7 +63,7 @@ template <typename N>
static bool TryParse(const std::string &str, N *const output)
{
std::istringstream iss(str);
-
+
N tmp = 0;
if (iss >> tmp)
{
@@ -89,20 +89,22 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
std::string UriDecode(const std::string & sSrc);
std::string UriEncode(const std::string & sSrc);
+std::string UTF16ToUTF8(const std::u16string& input);
+std::u16string UTF8ToUTF16(const std::string& input);
+
std::string CP1252ToUTF8(const std::string& str);
std::string SHIFTJISToUTF8(const std::string& str);
-std::string UTF16ToUTF8(const std::wstring& str);
#ifdef _WIN32
-std::wstring UTF8ToUTF16(const std::string& str);
+std::wstring UTF8ToUTF16W(const std::string& str);
#ifdef _UNICODE
inline std::string TStrToUTF8(const std::wstring& str)
{ return UTF16ToUTF8(str); }
inline std::wstring UTF8ToTStr(const std::string& str)
-{ return UTF8ToUTF16(str); }
+{ return UTF8ToUTF16W(str); }
#else
inline std::string TStrToUTF8(const std::string& str)
{ return str; }
@@ -113,4 +115,19 @@ inline std::string UTF8ToTStr(const std::string& str)
#endif
+/**
+ * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
+ * `other` for equality.
+ */
+template <typename InIt>
+bool ComparePartialString(InIt begin, InIt end, const char* other) {
+ for (; begin != end && *other != '\0'; ++begin, ++other) {
+ if (*begin != *other) {
+ return false;
+ }
+ }
+ // Only return true if both strings finished at the same point
+ return (begin == end) == (*other == '\0');
+}
+
}
diff --git a/src/common/symbols.cpp b/src/common/symbols.cpp
index d61f4c0c..63ad6218 100644
--- a/src/common/symbols.cpp
+++ b/src/common/symbols.cpp
@@ -31,7 +31,7 @@ namespace Symbols
{
TSymbolsMap::iterator foundSymbolItr;
TSymbol symbol;
-
+
foundSymbolItr = g_symbols.find(_address);
if (foundSymbolItr != g_symbols.end())
{
@@ -44,7 +44,7 @@ namespace Symbols
{
return GetSymbol(_address).name;
}
-
+
void Remove(u32 _address)
{
g_symbols.erase(_address);
diff --git a/src/common/symbols.h b/src/common/symbols.h
index b13a8001..4560f524 100644
--- a/src/common/symbols.h
+++ b/src/common/symbols.h
@@ -33,5 +33,5 @@ namespace Symbols
const std::string GetName(u32 _address);
void Remove(u32 _address);
void Clear();
-};
+}
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 60d8ed07..dc153ba7 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -25,7 +25,7 @@ int CurrentThreadId()
return 0;
#endif
}
-
+
#ifdef _WIN32
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
@@ -52,7 +52,7 @@ void SwitchCurrentThread()
// Sets the debugger-visible name of the current thread.
// Uses undocumented (actually, it is now documented) trick.
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
-
+
// This is implemented much nicer in upcoming msvc++, see:
// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
void SetCurrentThreadName(const char* szThreadName)
@@ -81,7 +81,7 @@ void SetCurrentThreadName(const char* szThreadName)
__except(EXCEPTION_CONTINUE_EXECUTION)
{}
}
-
+
#else // !WIN32, so must be POSIX threads
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
diff --git a/src/common/thread.h b/src/common/thread.h
index f7ace21b..8c36d3f0 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -21,6 +21,7 @@
//for gettimeofday and struct time(spec|val)
#include <time.h>
#include <sys/time.h>
+#include <unistd.h>
#endif
namespace Common
@@ -30,13 +31,13 @@ int CurrentThreadId();
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
void SetCurrentThreadAffinity(u32 mask);
-
+
class Event
{
public:
Event()
: is_set(false)
- {};
+ {}
void Set()
{
@@ -135,7 +136,7 @@ private:
const size_t m_count;
volatile size_t m_waiting;
};
-
+
void SleepCurrentThread(int ms);
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
@@ -146,7 +147,7 @@ inline void YieldCPU()
{
std::this_thread::yield();
}
-
+
void SetCurrentThreadName(const char *name);
-
+
} // namespace Common
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 4a89572f..7e3b620c 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -12,7 +12,7 @@ template<class IdType>
struct ThreadQueueList {
// Number of queues (number of priority levels starting at 0.)
static const int NUM_QUEUES = 128;
-
+
// Initial number of threads a single queue can handle.
static const int INITIAL_CAPACITY = 32;
@@ -37,7 +37,7 @@ struct ThreadQueueList {
~ThreadQueueList() {
for (int i = 0; i < NUM_QUEUES; ++i)
{
- if (queues[i].data != NULL)
+ if (queues[i].data != nullptr)
free(queues[i].data);
}
}
@@ -46,7 +46,7 @@ struct ThreadQueueList {
int contains(const IdType uid) {
for (int i = 0; i < NUM_QUEUES; ++i)
{
- if (queues[i].data == NULL)
+ if (queues[i].data == nullptr)
continue;
Queue *cur = &queues[i];
@@ -133,7 +133,7 @@ struct ThreadQueueList {
inline void clear() {
for (int i = 0; i < NUM_QUEUES; ++i)
{
- if (queues[i].data != NULL)
+ if (queues[i].data != nullptr)
free(queues[i].data);
}
memset(queues, 0, sizeof(queues));
@@ -147,7 +147,7 @@ struct ThreadQueueList {
inline void prepare(u32 priority) {
Queue *cur = &queues[priority];
- if (cur->next == NULL)
+ if (cur->next == nullptr)
link(priority, INITIAL_CAPACITY);
}
@@ -176,7 +176,7 @@ private:
for (int i = (int) priority - 1; i >= 0; --i)
{
- if (queues[i].next != NULL)
+ if (queues[i].next != nullptr)
{
cur->next = queues[i].next;
queues[i].next = cur;
@@ -193,7 +193,7 @@ private:
int size = cur->end - cur->first;
if (size >= cur->capacity - 2) {
IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
- if (new_data != NULL) {
+ if (new_data != nullptr) {
cur->capacity *= 2;
cur->data = new_data;
}
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index ded4a344..4a797f75 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -25,7 +25,7 @@ u32 Timer::GetTimeMs()
return timeGetTime();
#else
struct timeval t;
- (void)gettimeofday(&t, NULL);
+ (void)gettimeofday(&t, nullptr);
return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000));
#endif
}
@@ -183,7 +183,7 @@ std::string Timer::GetTimeFormatted()
return StringFromFormat("%s:%03i", tmp, tp.millitm);
#else
struct timeval t;
- (void)gettimeofday(&t, NULL);
+ (void)gettimeofday(&t, nullptr);
return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000));
#endif
}
@@ -197,7 +197,7 @@ double Timer::GetDoubleTime()
(void)::ftime(&tp);
#else
struct timeval t;
- (void)gettimeofday(&t, NULL);
+ (void)gettimeofday(&t, nullptr);
#endif
// Get continuous timestamp
u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970();
diff --git a/src/common/utf8.cpp b/src/common/utf8.cpp
index be4ebc85..66a2f633 100644
--- a/src/common/utf8.cpp
+++ b/src/common/utf8.cpp
@@ -281,28 +281,28 @@ int u8_read_escape_sequence(const char *str, u32 *dest)
do {
digs[dno++] = str[i++];
} while (octal_digit(str[i]) && dno < 3);
- ch = strtol(digs, NULL, 8);
+ 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, NULL, 16);
+ 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, NULL, 16);
+ 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, NULL, 16);
+ ch = strtol(digs, nullptr, 16);
}
*dest = ch;
@@ -353,7 +353,7 @@ const char *u8_strchr(const char *s, u32 ch, int *charn)
lasti = i;
(*charn)++;
}
- return NULL;
+ return nullptr;
}
const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn)
@@ -378,7 +378,7 @@ const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn)
lasti = i;
(*charn)++;
}
- return NULL;
+ return nullptr;
}
int u8_is_locale_utf8(const char *locale)
@@ -419,35 +419,35 @@ bool UTF8StringHasNonASCII(const char *utf8string) {
std::string ConvertWStringToUTF8(const wchar_t *wstr) {
int len = (int)wcslen(wstr);
- int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, NULL, NULL);
+ 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, NULL, NULL);
+ 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, NULL, NULL);
+ 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, NULL, NULL);
+ 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, NULL, 0);
+ 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, NULL, 0);
+ int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0);
std::wstring str;
str.resize(size);
if (size > 0) {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f6748135..f71232c1 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,29 +18,42 @@ set(SRCS
arm/skyeye_common/vfp/vfpinstr.cpp
arm/skyeye_common/vfp/vfpsingle.cpp
file_sys/archive_romfs.cpp
+ file_sys/archive_savedata.cpp
file_sys/archive_sdmc.cpp
+ file_sys/disk_archive.cpp
file_sys/file_romfs.cpp
- file_sys/file_sdmc.cpp
file_sys/directory_romfs.cpp
- file_sys/directory_sdmc.cpp
hle/kernel/address_arbiter.cpp
- hle/kernel/archive.cpp
hle/kernel/event.cpp
hle/kernel/kernel.cpp
hle/kernel/mutex.cpp
+ hle/kernel/semaphore.cpp
hle/kernel/shared_memory.cpp
hle/kernel/thread.cpp
hle/service/ac_u.cpp
+ hle/service/am_app.cpp
+ hle/service/am_net.cpp
hle/service/apt_u.cpp
+ hle/service/boss_u.cpp
+ hle/service/cecd_u.cpp
+ hle/service/cfg_i.cpp
hle/service/cfg_u.cpp
+ hle/service/csnd_snd.cpp
hle/service/dsp_dsp.cpp
hle/service/err_f.cpp
- hle/service/fs_user.cpp
+ hle/service/fs/archive.cpp
+ hle/service/fs/fs_user.cpp
+ hle/service/frd_u.cpp
hle/service/gsp_gpu.cpp
hle/service/hid_user.cpp
+ hle/service/ir_rst.cpp
+ hle/service/ir_u.cpp
+ hle/service/ldr_ro.cpp
hle/service/mic_u.cpp
+ hle/service/nim_aoc.cpp
hle/service/ndm_u.cpp
hle/service/nwm_uds.cpp
+ hle/service/pm_app.cpp
hle/service/ptm_u.cpp
hle/service/service.cpp
hle/service/soc_u.cpp
@@ -51,10 +64,10 @@ set(SRCS
hle/svc.cpp
hw/gpu.cpp
hw/hw.cpp
- hw/ndma.cpp
loader/elf.cpp
loader/loader.cpp
loader/ncch.cpp
+ loader/3dsx.cpp
core.cpp
core_timing.cpp
mem_map.cpp
@@ -84,48 +97,63 @@ set(HEADERS
arm/skyeye_common/vfp/vfp.h
arm/skyeye_common/vfp/vfp_helper.h
arm/arm_interface.h
- file_sys/archive.h
+ file_sys/archive_backend.h
file_sys/archive_romfs.h
+ file_sys/archive_savedata.h
file_sys/archive_sdmc.h
- file_sys/file.h
+ file_sys/disk_archive.h
+ file_sys/file_backend.h
file_sys/file_romfs.h
- file_sys/file_sdmc.h
- file_sys/directory.h
+ file_sys/directory_backend.h
file_sys/directory_romfs.h
- file_sys/directory_sdmc.h
hle/kernel/address_arbiter.h
- hle/kernel/archive.h
hle/kernel/event.h
hle/kernel/kernel.h
hle/kernel/mutex.h
+ hle/kernel/semaphore.h
+ hle/kernel/session.h
hle/kernel/shared_memory.h
hle/kernel/thread.h
hle/service/ac_u.h
+ hle/service/am_app.h
+ hle/service/am_net.h
hle/service/apt_u.h
+ hle/service/boss_u.h
+ hle/service/cecd_u.h
+ hle/service/cfg_i.h
hle/service/cfg_u.h
+ hle/service/csnd_snd.h
hle/service/dsp_dsp.h
hle/service/err_f.h
- hle/service/fs_user.h
+ hle/service/fs/archive.h
+ hle/service/fs/fs_user.h
+ hle/service/frd_u.h
hle/service/gsp_gpu.h
hle/service/hid_user.h
+ hle/service/ir_rst.h
+ hle/service/ir_u.h
+ hle/service/ldr_ro.h
hle/service/mic_u.h
+ hle/service/nim_aoc.h
hle/service/ndm_u.h
hle/service/nwm_uds.h
+ hle/service/pm_app.h
hle/service/ptm_u.h
hle/service/service.h
hle/service/soc_u.h
hle/service/srv.h
hle/service/ssl_c.h
hle/config_mem.h
+ hle/result.h
hle/function_wrappers.h
hle/hle.h
hle/svc.h
hw/gpu.h
hw/hw.h
- hw/ndma.h
loader/elf.h
loader/loader.h
loader/ncch.h
+ loader/3dsx.h
core.h
core_timing.h
mem_map.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index be677ae2..3ae52856 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -16,7 +16,7 @@ public:
num_instructions = 0;
}
- ~ARM_Interface() {
+ virtual ~ARM_Interface() {
}
/**
@@ -63,7 +63,7 @@ public:
* Get the current CPSR register
* @return Returns the value of the CPSR register
*/
- virtual u32 GetCPSR() const = 0;
+ virtual u32 GetCPSR() const = 0;
/**
* Set the current CPSR register
@@ -98,7 +98,7 @@ public:
}
protected:
-
+
/**
* Executes the given number of instructions
* @param num_instructions Number of instructions to executes
diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp
index 0f384ad3..55278474 100644
--- a/src/core/arm/disassembler/load_symbol_map.cpp
+++ b/src/core/arm/disassembler/load_symbol_map.cpp
@@ -22,8 +22,8 @@ void LoadSymbolMap(std::string filename) {
while (std::getline(infile, line)) {
std::istringstream iss(line);
- if (!(iss >> address_str >> size >> function_name)) {
- break; // Error parsing
+ if (!(iss >> address_str >> size >> function_name)) {
+ break; // Error parsing
}
u32 address = std::stoul(address_str, nullptr, 16);
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index 669b612f..6c8ea211 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "core/arm/skyeye_common/armcpu.h"
#include "core/arm/skyeye_common/armemu.h"
@@ -60,7 +60,7 @@ void ARM_DynCom::SetPC(u32 pc) {
* @return Returns current PC
*/
u32 ARM_DynCom::GetPC() const {
- return state->pc;
+ return state->Reg[15];
}
/**
@@ -110,9 +110,12 @@ u64 ARM_DynCom::GetTicks() const {
* @param num_instructions Number of instructions to executes
*/
void ARM_DynCom::ExecuteInstructions(int num_instructions) {
- ticks += num_instructions;
state->NumInstrsToExecute = num_instructions;
- InterpreterMainLoop(state.get());
+
+ // Dyncom only breaks on instruction dispatch. This only happens on every instruction when
+ // executing one instruction at a time. Otherwise, if a block is being executed, more
+ // instructions may actually be executed than specified.
+ ticks += InterpreterMainLoop(state.get());
}
/**
@@ -126,7 +129,7 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) {
ctx.sp = state->Reg[13];
ctx.lr = state->Reg[14];
- ctx.pc = state->pc;
+ ctx.pc = state->Reg[15];
ctx.cpsr = state->Cpsr;
ctx.fpscr = state->VFP[1];
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 1f8cd3a3..51eea41e 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -19,7 +19,7 @@ public:
/**
* Set the Program Counter to an address
- * @param addr Address to set PC to
+ * @param pc Address to set PC to
*/
void SetPC(u32 pc) override;
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index fe1501b5..68012bff 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -26,7 +26,7 @@
#define CITRA_IGNORE_EXIT(x)
#include <algorithm>
-#include <map>
+#include <unordered_map>
#include <stdio.h>
#include <assert.h>
#include <cstdio>
@@ -94,9 +94,8 @@ typedef unsigned int (*shtop_fp_t)(arm_processor *cpu, unsigned int sht_oper);
/* exclusive memory access */
static int exclusive_detect(ARMul_State* state, ARMword addr){
- int i;
#if 0
- for(i = 0; i < 128; i++){
+ for(int i = 0; i < 128; i++){
if(state->exclusive_tag_array[i] == addr)
return 0;
}
@@ -108,9 +107,8 @@ static int exclusive_detect(ARMul_State* state, ARMword addr){
}
static void add_exclusive_addr(ARMul_State* state, ARMword addr){
- int i;
#if 0
- for(i = 0; i < 128; i++){
+ for(int i = 0; i < 128; i++){
if(state->exclusive_tag_array[i] == 0xffffffff){
state->exclusive_tag_array[i] = addr;
//DEBUG_LOG(ARM11, "In %s, add addr 0x%x\n", __func__, addr);
@@ -435,9 +433,7 @@ typedef struct _ldst_inst {
unsigned int inst;
get_addr_fp_t get_addr;
} ldst_inst;
-#define DEBUG_MSG DEBUG_LOG(ARM11, "in %s %d\n", __FUNCTION__, __LINE__); \
- DEBUG_LOG(ARM11, "inst is %x\n", inst); \
- CITRA_IGNORE_EXIT(0)
+#define DEBUG_MSG LOG_DEBUG(Core_ARM11, "inst is %x", inst); CITRA_IGNORE_EXIT(0)
int CondPassed(arm_processor *cpu, unsigned int cond);
#define LnSWoUB(s) glue(LnSWoUB, s)
@@ -1425,7 +1421,7 @@ inline void *AllocBuffer(unsigned int size)
int start = top;
top += size;
if (top > CACHE_BUFFER_SIZE) {
- DEBUG_LOG(ARM11, "inst_buf is full\n");
+ LOG_ERROR(Core_ARM11, "inst_buf is full");
CITRA_IGNORE_EXIT(-1);
}
return (void *)&inst_buf[start];
@@ -1611,6 +1607,10 @@ get_addr_fp_t get_calc_addr_op(unsigned int inst)
#define CHECK_RM (inst_cream->Rm == 15)
#define CHECK_RS (inst_cream->Rs == 15)
+#define UNIMPLEMENTED_INSTRUCTION(mnemonic) \
+ LOG_ERROR(Core_ARM11, "unimplemented instruction: %s", mnemonic); \
+ CITRA_IGNORE_EXIT(-1); \
+ return nullptr;
ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index)
{
@@ -1725,7 +1725,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(bic)(unsigned int inst, int index)
inst_base->br = INDIRECT_BRANCH;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BKPT"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_inst));
@@ -1760,7 +1760,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(bx)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BXJ"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst));
cdp_inst *inst_cream = (cdp_inst *)inst_base->component;
@@ -1777,7 +1777,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){
inst_cream->opcode_1 = BITS(inst, 20, 23);
inst_cream->inst = inst;
- DEBUG_LOG(ARM11, "in func %s inst %x index %x\n", __FUNCTION__, inst, index);
+ LOG_TRACE(Core_ARM11, "inst %x index %x", inst, index);
return inst_base;
}
ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(unsigned int inst, int index)
@@ -2207,7 +2207,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(mcr)(unsigned int inst, int index)
inst_cream->inst = inst;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MCRR"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst));
@@ -2266,7 +2266,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(mrc)(unsigned int inst, int index)
inst_cream->inst = inst;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MRRC"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst));
@@ -2360,8 +2360,8 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(orr)(unsigned int inst, int index)
}
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHBT"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHTB"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pld_inst));
@@ -2373,16 +2373,16 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDADD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDSUB"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUBADDX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst));
@@ -2412,8 +2412,8 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rev16)(unsigned int inst, int index){
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("REVSH"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("RFE"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsb_inst));
@@ -2462,9 +2462,9 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(rsc)(unsigned int inst, int index)
}
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADDSUBX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst));
@@ -2489,14 +2489,14 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index)
}
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SEL"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SETEND"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUBADDX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst));
@@ -2555,15 +2555,15 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smlal)(unsigned int inst, int index)
inst_base->load_r15 = 1;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLAW"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSLD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLA"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLS"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMUL"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUAD"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smul_inst));
@@ -2626,13 +2626,13 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(smulw)(unsigned int inst, int index)
inst_base->load_r15 = 1;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUSD"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SRS"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUBADDX"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst));
@@ -2939,9 +2939,9 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTAB16"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){
- DEBUG_LOG(ARM11, "in func %s, SXTAH untested\n", __func__);
+ LOG_WARNING(Core_ARM11, "SXTAH untested");
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst));
sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component;
@@ -2957,7 +2957,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTB16"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(teq_inst));
@@ -3001,16 +3001,16 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index)
inst_base->load_r15 = 1;
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UMAAL"); }
ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index)
{
arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst));
@@ -3113,21 +3113,21 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(blx_1_thumb)(unsigned int tinst, int index)
return inst_base;
}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
-ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;}
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADDSUBX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUBADDX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAD8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USADA8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB8"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUBADDX"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTAB16"); }
+ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTB16"); }
@@ -3309,9 +3309,8 @@ const transop_fp_t arm_instruction_trans[] = {
INTERPRETER_TRANSLATE(blx_1_thumb)
};
-typedef map<unsigned int, int> bb_map;
-bb_map CreamCache[65536];
-bb_map ProfileCache[65536];
+typedef std::unordered_map<u32, int> bb_map;
+bb_map CreamCache;
//#define USE_DUMMY_CACHE
@@ -3319,14 +3318,12 @@ bb_map ProfileCache[65536];
unsigned int DummyCache[0x100000];
#endif
-#define HASH(x) ((x + (x << 3) + (x >> 6)) % 65536)
void insert_bb(unsigned int addr, int start)
{
#ifdef USE_DUMMY_CACHE
DummyCache[addr] = start;
#else
-// CreamCache[addr] = start;
- CreamCache[HASH(addr)][addr] = start;
+ CreamCache[addr] = start;
#endif
}
@@ -3341,8 +3338,8 @@ int find_bb(unsigned int addr, int &start)
} else
ret = -1;
#else
- bb_map::const_iterator it = CreamCache[HASH(addr)].find(addr);
- if (it != CreamCache[HASH(addr)].end()) {
+ bb_map::const_iterator it = CreamCache.find(addr);
+ if (it != CreamCache.end()) {
start = static_cast<int>(it->second);
ret = 0;
#if HYBRID_MODE
@@ -3396,7 +3393,7 @@ static tdstate decode_thumb_instr(arm_processor *cpu, uint32_t inst, addr_t addr
}
else{
/* something wrong */
- DEBUG_LOG(ARM11, "In %s, thumb decoder error\n", __FUNCTION__);
+ LOG_ERROR(Core_ARM11, "thumb decoder error");
}
break;
case 28:
@@ -3473,30 +3470,15 @@ void flush_bb(uint32_t addr)
uint32_t start;
addr &= 0xfffff000;
- for (int i = 0; i < 65536; i ++) {
- for (it = CreamCache[i].begin(); it != CreamCache[i].end(); ) {
- start = static_cast<uint32_t>(it->first);
- //start = (start >> 12) << 12;
- start &= 0xfffff000;
- if (start == addr) {
- //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
- CreamCache[i].erase(it ++);
- } else
- ++it;
- }
- }
-
- for (int i = 0; i < 65536; i ++) {
- for (it = ProfileCache[i].begin(); it != ProfileCache[i].end(); ) {
- start = static_cast<uint32_t>(it->first);
- //start = (start >> 12) << 12;
- start &= 0xfffff000;
- if (start == addr) {
- //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
- ProfileCache[i].erase(it ++);
- } else
- ++it;
- }
+ for (it = CreamCache.begin(); it != CreamCache.end(); ) {
+ start = static_cast<uint32_t>(it->first);
+ //start = (start >> 12) << 12;
+ start &= 0xfffff000;
+ if (start == addr) {
+ //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
+ CreamCache.erase(it++);
+ } else
+ ++it;
}
//DEBUG_LOG(ARM11, "flush bb @ %x\n", addr);
@@ -3619,7 +3601,7 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr)
bank->bank_read(32, phys_addr, &inst);
}
else {
- DEBUG_LOG(ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr);
+ LOG_ERROR(Core_ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr);
return FETCH_FAILURE;
}
#else
@@ -3649,8 +3631,8 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr)
ret = decode_arm_instr(inst, &idx);
if (ret == DECODE_FAILURE) {
- DEBUG_LOG(ARM11, "[info] : Decode failure.\tPC : [0x%x]\tInstruction : [%x]\n", phys_addr, inst);
- DEBUG_LOG(ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x\n", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
+ LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : [%x]", phys_addr, inst);
+ LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
CITRA_IGNORE_EXIT(-1);
}
// DEBUG_LOG(ARM11, "PC : [0x%x] INST : %s\n", cpu->translate_pc, arm_instruction[idx].name);
@@ -3694,7 +3676,7 @@ void InterpreterInitInstLength(unsigned long long int *ptr, size_t size)
}
}
for (int i = 0; i < array_size - 4; i ++)
- DEBUG_LOG(ARM11, "[%d]:%d\n", i, InstLength[i]);
+ LOG_DEBUG(Core_ARM11, "[%d]:%d", i, InstLength[i]);
}
int clz(unsigned int x)
@@ -3718,7 +3700,7 @@ static bool InAPrivilegedMode(arm_core_t *core)
}
/* r15 = r15 + 8 */
-void InterpreterMainLoop(ARMul_State* state)
+unsigned InterpreterMainLoop(ARMul_State* state)
{
#define CRn inst_cream->crn
#define OPCODE_2 inst_cream->opcode_2
@@ -3741,22 +3723,28 @@ void InterpreterMainLoop(ARMul_State* state)
//if (debug_function(core)) \
if (core->check_int_flag) \
goto END
- //DEBUG_LOG(ARM11, "icounter is %llx line is %d pc is %x\n", cpu->icounter, __LINE__, cpu->Reg[15])
+ //LOG_TRACE(Core_ARM11, "icounter is %llx pc is %x\n", cpu->icounter, cpu->Reg[15])
#else
#define INC_ICOUNTER ;
#endif
#define FETCH_INST if (inst_base->br != NON_BRANCH) \
- goto PROFILING; \
+ goto DISPATCH; \
inst_base = (arm_inst *)&inst_buf[ptr]
#define INC_PC(l) ptr += sizeof(arm_inst) + l
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
// clunky switch statement.
#if defined __GNUC__ || defined __clang__
-#define GOTO_NEXT_INST goto *InstLabel[inst_base->idx]
+#define GOTO_NEXT_INST \
+ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
+ num_instrs++; \
+ goto *InstLabel[inst_base->idx]
#else
-#define GOTO_NEXT_INST switch(inst_base->idx) { \
+#define GOTO_NEXT_INST \
+ if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
+ num_instrs++; \
+ switch(inst_base->idx) { \
case 0: goto VMLA_INST; \
case 1: goto VMLS_INST; \
case 2: goto VNMLA_INST; \
@@ -4028,20 +4016,15 @@ void InterpreterMainLoop(ARMul_State* state)
unsigned int addr;
unsigned int phys_addr;
unsigned int last_pc = 0;
+ unsigned int num_instrs = 0;
fault_t fault;
static unsigned int last_physical_base = 0, last_logical_base = 0;
int ptr;
+ bool single_step = (cpu->NumInstrsToExecute == 1);
LOAD_NZCVT;
DISPATCH:
{
- if (cpu->NumInstrsToExecute == 0)
- return;
-
- cpu->NumInstrsToExecute--;
-
- //NOTICE_LOG(ARM11, "instr!");
-
if (!cpu->NirqSig) {
if (!(cpu->Cpsr & 0x80)) {
goto END;
@@ -4179,10 +4162,6 @@ void InterpreterMainLoop(ARMul_State* state)
inst_base = (arm_inst *)&inst_buf[ptr];
GOTO_NEXT_INST;
}
- PROFILING:
- {
- goto DISPATCH;
- }
ADC_INST:
{
INC_ICOUNTER;
@@ -4207,7 +4186,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(adc_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4241,7 +4220,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(add_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4272,7 +4251,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(and_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4290,11 +4269,11 @@ void InterpreterMainLoop(ARMul_State* state)
}
SET_PC;
INC_PC(sizeof(bbl_inst));
- goto PROFILING;
+ goto DISPATCH;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(bbl_inst));
- goto PROFILING;
+ goto DISPATCH;
}
BIC_INST:
{
@@ -4322,7 +4301,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(bic_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4358,12 +4337,12 @@ void InterpreterMainLoop(ARMul_State* state)
//DEBUG_MSG;
}
INC_PC(sizeof(blx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
// INC_PC(sizeof(bx_inst));
INC_PC(sizeof(blx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
BX_INST:
{
@@ -4371,17 +4350,17 @@ void InterpreterMainLoop(ARMul_State* state)
bx_inst *inst_cream = (bx_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
if (inst_cream->Rm == 15)
- DEBUG_LOG(ARM11, "In %s, BX at pc %x: use of Rm = R15 is discouraged\n", __FUNCTION__, cpu->Reg[15]);
+ LOG_WARNING(Core_ARM11, "BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]);
cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1;
cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe;
// cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1;
INC_PC(sizeof(bx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
// INC_PC(sizeof(bx_inst));
INC_PC(sizeof(bx_inst));
- goto PROFILING;
+ goto DISPATCH;
}
BXJ_INST:
CDP_INST:
@@ -4393,12 +4372,13 @@ void InterpreterMainLoop(ARMul_State* state)
#define CP_ACCESS_ALLOW 0
if(CP_ACCESS_ALLOW){
/* undefined instruction here */
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
- ERROR_LOG(ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]);
+ LOG_ERROR(Core_ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]);
unsigned cpab = (cpu->CDP[inst_cream->cp_num]) (cpu, ARMul_FIRST, inst_cream->inst);
if(cpab != ARMul_DONE){
- ERROR_LOG(ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num);
+ LOG_ERROR(Core_ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num);
//CITRA_IGNORE_EXIT(-1);
}
}
@@ -4522,7 +4502,7 @@ void InterpreterMainLoop(ARMul_State* state)
// RD = RM;
if ((inst_cream->Rd == 15)) {
INC_PC(sizeof(mov_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
// DEBUG_LOG(ARM11, "cpy inst %x\n", cpu->Reg[15]);
@@ -4558,7 +4538,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(eor_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4717,7 +4697,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (BIT(inst, 15)) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4764,7 +4744,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->TFlag = value & 0x1;
cpu->Reg[15] &= 0xFFFFFFFE;
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
//}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4794,7 +4774,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->TFlag = value & 0x1;
cpu->Reg[15] &= 0xFFFFFFFE;
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4825,7 +4805,7 @@ void InterpreterMainLoop(ARMul_State* state)
& 0xffff;
RD = RN + operand2;
if (inst_cream->Rn == 15 || inst_cream->Rm == 15) {
- DEBUG_LOG(ARM11, "in line %d\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operands for UXTAH");
CITRA_IGNORE_EXIT(-1);
}
}
@@ -4848,7 +4828,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4869,7 +4849,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4888,7 +4868,7 @@ void InterpreterMainLoop(ARMul_State* state)
uint32_t rear_phys_addr;
fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 1);
if(fault){
- ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n");
+ LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n");
CITRA_IGNORE_EXIT(-1);
goto MMU_EXCEPTION;
}
@@ -4926,7 +4906,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4953,7 +4933,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4980,7 +4960,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5006,7 +4986,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5031,7 +5011,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5058,7 +5038,7 @@ void InterpreterMainLoop(ARMul_State* state)
if (BITS(inst_cream->inst, 12, 15) == 15) {
INC_PC(sizeof(ldst_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5111,7 +5091,7 @@ void InterpreterMainLoop(ARMul_State* state)
switch(OPCODE_2){
case 0: /* invalidate all */
//invalidate_all_tlb(state);
- DEBUG_LOG(ARM11, "{TLB} [INSN] invalidate all\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [INSN] invalidate all");
//remove_tlb(INSN_TLB);
//erase_all(core, INSN_TLB);
break;
@@ -5137,7 +5117,7 @@ void InterpreterMainLoop(ARMul_State* state)
//invalidate_all_tlb(state);
//remove_tlb(DATA_TLB);
//erase_all(core, DATA_TLB);
- DEBUG_LOG(ARM11, "{TLB} [DATA] invalidate all\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [DATA] invalidate all");
break;
case 1: /* invalidate by MVA */
//invalidate_by_mva(state, value);
@@ -5169,13 +5149,13 @@ void InterpreterMainLoop(ARMul_State* state)
//invalidate_by_mva(state, value);
//erase_by_mva(core, RD, DATA_TLB);
//erase_by_mva(core, RD, INSN_TLB);
- DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by mva\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by mva");
break;
case 2: /* invalidate by asid */
//invalidate_by_asid(state, value);
//erase_by_asid(core, RD, DATA_TLB);
//erase_by_asid(core, RD, INSN_TLB);
- DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by asid\n");
+ LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by asid");
break;
default:
break;
@@ -5197,7 +5177,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
} else {
- DEBUG_LOG(ARM11, "mcr is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2);
+ LOG_ERROR(Core_ARM11, "mcr CRn=%d, CRm=%d OP2=%d is not implemented", CRn, CRm, OPCODE_2);
}
}
}
@@ -5217,7 +5197,7 @@ void InterpreterMainLoop(ARMul_State* state)
uint64_t rs = RS;
uint64_t rn = RN;
if (inst_cream->Rm == 15 || inst_cream->Rs == 15 || inst_cream->Rn == 15) {
- DEBUG_LOG(ARM11, "in __line__\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operands for MLA");
CITRA_IGNORE_EXIT(-1);
}
// RD = dst = RM * RS + RN;
@@ -5228,7 +5208,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mla_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5260,7 +5240,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mov_inst));
- goto PROFILING;
+ goto DISPATCH;
}
// return;
}
@@ -5331,7 +5311,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
}
else {
- DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2);
+ LOG_ERROR(Core_ARM11, "mrc CRn=%d, CRm=%d, OP2=%d is not implemented", CRn, CRm, OPCODE_2);
}
}
//DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2);
@@ -5422,7 +5402,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mul_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5451,7 +5431,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mvn_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5483,7 +5463,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(orr_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5522,7 +5502,7 @@ void InterpreterMainLoop(ARMul_State* state)
(((RM >> 16) & 0xff) << 8) |
((RM >> 24) & 0xff);
if (inst_cream->Rm == 15) {
- DEBUG_LOG(ARM11, "in line %d\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operand for REV");
CITRA_IGNORE_EXIT(-1);
}
}
@@ -5575,7 +5555,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsb_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5612,7 +5592,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsc_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5653,7 +5633,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sbc_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5975,7 +5955,7 @@ void InterpreterMainLoop(ARMul_State* state)
sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
if (inst_cream->Rm == 15) {
- DEBUG_LOG(ARM11, "line is %d\n", __LINE__);
+ LOG_ERROR(Core_ARM11, "invalid operand for SXTB");
CITRA_IGNORE_EXIT(-1);
}
unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
@@ -6066,7 +6046,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
//if (BITS(inst_cream->inst, 12, 15) == 15)
- // goto PROFILING;
+ // goto DISPATCH;
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
@@ -6081,7 +6061,7 @@ void InterpreterMainLoop(ARMul_State* state)
uint32_t rear_phys_addr;
fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 0);
if (fault){
- ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n");
+ LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n");
CITRA_IGNORE_EXIT(-1);
goto MMU_EXCEPTION;
}
@@ -6175,7 +6155,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
//if (BITS(inst_cream->inst, 12, 15) == 15)
- // goto PROFILING;
+ // goto DISPATCH;
INC_PC(sizeof(ldst_inst));
FETCH_INST;
GOTO_NEXT_INST;
@@ -6225,7 +6205,7 @@ void InterpreterMainLoop(ARMul_State* state)
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sub_inst));
- goto PROFILING;
+ goto DISPATCH;
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6449,7 +6429,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm;
//DEBUG_LOG(ARM11, " BL_1_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]);
INC_PC(sizeof(b_2_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
B_COND_THUMB:
{
@@ -6461,7 +6441,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[15] += 2;
//DEBUG_LOG(ARM11, " B_COND_THUMB: imm=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[15]);
INC_PC(sizeof(b_cond_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
BL_1_THUMB:
{
@@ -6487,7 +6467,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->Reg[14] = tmp;
//DEBUG_LOG(ARM11, " BL_2_THUMB: imm=0x%x, r14=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]);
INC_PC(sizeof(bl_2_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
BLX_1_THUMB:
{
@@ -6503,7 +6483,7 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->TFlag = 0;
//DEBUG_LOG(ARM11, "In BLX_1_THUMB, BLX(1),imm=0x%x,r14=0x%x, r15=0x%x, \n", inst_cream->imm, cpu->Reg[14], cpu->Reg[15]);
INC_PC(sizeof(blx_1_thumb));
- goto PROFILING;
+ goto DISPATCH;
}
UQADD16_INST:
@@ -6532,12 +6512,14 @@ void InterpreterMainLoop(ARMul_State* state)
cpu->AbortAddr = addr;
cpu->CP15[CP15(CP15_FAULT_STATUS)] = fault & 0xff;
cpu->CP15[CP15(CP15_FAULT_ADDRESS)] = addr;
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
END:
{
SAVE_NZCVT;
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
INIT_INST_LENGTH:
{
@@ -6557,7 +6539,8 @@ void InterpreterMainLoop(ARMul_State* state)
DEBUG_LOG(ARM11, "%llx\n", InstLabel[1]);
DEBUG_LOG(ARM11, "%lld\n", (char *)InstEndLabel[1] - (char *)InstLabel[1]);
#endif
- return;
+ cpu->NumInstrsToExecute = 0;
+ return num_instrs;
}
}
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.h b/src/core/arm/dyncom/arm_dyncom_interpreter.h
index d73f8f65..3a2462f5 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.h
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.h
@@ -1,7 +1,7 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
-void InterpreterMainLoop(ARMul_State* state);
+unsigned InterpreterMainLoop(ARMul_State* state);
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index ed441508..e2aa5ce9 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "core/arm/interpreter/arm_interpreter.h"
@@ -24,7 +24,7 @@ ARM_Interpreter::ARM_Interpreter() {
state->lateabtSig = LOW;
// Reset the core to initial state
- ARMul_CoProInit(state);
+ ARMul_CoProInit(state);
ARMul_Reset(state);
state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext
state->Emulate = 3;
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index ceb1be43..ed53d997 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -18,7 +18,7 @@ public:
/**
* Set the Program Counter to an address
- * @param addr Address to set PC to
+ * @param pc Address to set PC to
*/
void SetPC(u32 pc) override;
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp
index 73223874..1a589e39 100644
--- a/src/core/arm/interpreter/armemu.cpp
+++ b/src/core/arm/interpreter/armemu.cpp
@@ -949,7 +949,7 @@ ARMul_Emulate26 (ARMul_State * state)
//printf("t decode %04lx -> %08lx\n", instr & 0xffff, armOp);
if (armOp == 0xDEADC0DE) {
- DEBUG("Failed to decode thumb opcode %04X at %08X\n", instr, pc);
+ LOG_ERROR(Core_ARM11, "Failed to decode thumb opcode %04X at %08X", instr, pc);
}
instr = armOp;
@@ -1166,7 +1166,7 @@ mainswitch:
else if ((((int)BITS(21, 27)) == 0x3e) && ((int)BITS(4, 6) == 0x1)) {
//(ARMword)(instr<<(31-(n))) >> ((31-(n))+(m))
unsigned msb ,tmp_rn, tmp_rd, dst;
- msb = tmp_rd = tmp_rn = dst = 0;
+ tmp_rd = tmp_rn = dst = 0;
Rd = BITS(12, 15);
Rn = BITS(0, 3);
lsb = BITS(7, 11);
@@ -1737,7 +1737,7 @@ mainswitch:
//chy 2006-02-15 if in user mode, can not set cpsr 0:23
//from p165 of ARMARM book
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS & rhs;
@@ -1877,7 +1877,7 @@ mainswitch:
/* TEQP reg */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS ^ rhs;
@@ -1993,7 +1993,7 @@ mainswitch:
/* CMPP reg. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS - rhs;
@@ -2112,7 +2112,7 @@ mainswitch:
if (DESTReg == 15) {
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
rhs = DPRegRHS;
temp = LHS + rhs;
@@ -2200,17 +2200,57 @@ mainswitch:
Handle_Store_Double (state, instr);
break;
}
+ if (BITS(4, 11) == 0xF9) { //strexd
+ u32 l = LHSReg;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr)&&
+ state->currentexvald == (u32)ARMul_ReadWord(state, state->currentexaddr + 4))
+ enter = true;
+
+
+ //todo bug this and STREXD and LDREXD http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360e/CHDGJGGC.html
+
+
+ if (enter) {
+ ARMul_StoreWordN(state, LHS, state->Reg[RHSReg]);
+ ARMul_StoreWordN(state,LHS + 4 , state->Reg[RHSReg + 1]);
+ state->Reg[DESTReg] = 0;
+ } else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ break;
+ }
#endif
dest = DPRegRHS;
WRITEDEST (dest);
break;
- case 0x1b: /* MOVS reg */
+ case 0x1B: /* MOVS reg */
#ifdef MODET
+ /* ldrexd ichfly */
+ if (BITS(0, 11) == 0xF9F) { //strexd
+ lhs = LHS;
+
+ state->currentexaddr = lhs;
+ state->currentexval = (u32)ARMul_ReadWord(state, lhs);
+ state->currentexvald = (u32)ARMul_ReadWord(state, lhs + 4);
+
+ state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs);
+ state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs + 4);
+ break;
+ }
+
if ((BITS (4, 11) & 0xF9) == 0x9)
/* LDR register offset, write-back, up, pre indexed. */
LHPREUPWB ();
/* Continue with remaining instruction decoding. */
+
+
+
+
#endif
dest = DPSRegRHS;
WRITESDEST (dest);
@@ -2297,12 +2337,12 @@ mainswitch:
if (state->currentexval == (u32)ARMul_LoadHalfWord(state, state->currentexaddr))enter = true;
- ARMul_StoreHalfWord(state, lhs, RHS);
//StoreWord(state, lhs, RHS)
if (state->Aborted) {
TAKEABORT;
}
if (enter) {
+ ARMul_StoreHalfWord(state, lhs, RHS);
state->Reg[DESTReg] = 0;
} else {
state->Reg[DESTReg] = 1;
@@ -2520,7 +2560,7 @@ mainswitch:
/* TSTP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS & DPImmRHS;
SETR15PSR (temp);
@@ -2547,7 +2587,7 @@ mainswitch:
/* TEQP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS ^ DPImmRHS;
SETR15PSR (temp);
@@ -2568,7 +2608,7 @@ mainswitch:
/* CMPP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS - DPImmRHS;
SETR15PSR (temp);
@@ -2604,7 +2644,7 @@ mainswitch:
/* CMNP immed. */
#ifdef MODE32
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
#else
temp = LHS + DPImmRHS;
SETR15PSR (temp);
@@ -3055,17 +3095,14 @@ mainswitch:
case 0x68: /* Store Word, No WriteBack, Post Inc, Reg. */
//ichfly PKHBT PKHTB todo check this
- if ((instr & 0x70) == 0x10) //pkhbt
- {
+ if ((instr & 0x70) == 0x10) { //pkhbt
u8 idest = BITS(12, 15);
u8 rfis = BITS(16, 19);
u8 rlast = BITS(0, 3);
u8 ishi = BITS(7,11);
state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000);
break;
- }
- else if ((instr & 0x70) == 0x50)//pkhtb
- {
+ } else if ((instr & 0x70) == 0x50) { //pkhtb
u8 idest = BITS(12, 15);
u8 rfis = BITS(16, 19);
u8 rlast = BITS(0, 3);
@@ -3073,8 +3110,7 @@ mainswitch:
if (ishi == 0)ishi = 0x20;
state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000);
break;
- }
- else if (BIT (4)) {
+ } else if (BIT (4)) {
#ifdef MODE32
if (state->is_v6
&& handle_v6_insn (state, instr))
@@ -3437,7 +3473,7 @@ mainswitch:
case 0x7f: /* Load Byte, WriteBack, Pre Inc, Reg. */
if (BIT (4)) {
- DEBUG("got unhandled special breakpoint\n");
+ LOG_DEBUG(Core_ARM11, "got unhandled special breakpoint");
return 1;
}
UNDEF_LSRBaseEQOffWb;
@@ -3686,13 +3722,11 @@ mainswitch:
/* Co-Processor Data Transfers. */
case 0xc4:
- if ((instr & 0x0FF00FF0) == 0xC400B10) //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0
- {
+ if ((instr & 0x0FF00FF0) == 0xC400B10) { //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0
state->ExtReg[BITS(0, 3) << 1] = state->Reg[BITS(12, 15)];
state->ExtReg[(BITS(0, 3) << 1) + 1] = state->Reg[BITS(16, 20)];
break;
- }
- else if (state->is_v5) {
+ } else if (state->is_v5) {
/* Reading from R15 is UNPREDICTABLE. */
if (BITS (12, 15) == 15 || BITS (16, 19) == 15)
ARMul_UndefInstr (state, instr);
@@ -3712,22 +3746,18 @@ mainswitch:
break;
case 0xc5:
- if ((instr & 0x00000FF0) == 0xB10) //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0
- {
+ if ((instr & 0x00000FF0) == 0xB10) { //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0
state->Reg[BITS(12, 15)] = state->ExtReg[BITS(0, 3) << 1];
state->Reg[BITS(16, 19)] = state->ExtReg[(BITS(0, 3) << 1) + 1];
break;
- }
- else if (state->is_v5) {
+ } else if (state->is_v5) {
/* Writes to R15 are UNPREDICATABLE. */
if (DESTReg == 15 || LHSReg == 15)
ARMul_UndefInstr (state, instr);
/* Is access to the coprocessor allowed ? */
- else if (!CP_ACCESS_ALLOWED(state, CPNum))
- {
+ else if (!CP_ACCESS_ALLOWED(state, CPNum)) {
ARMul_UndefInstr(state, instr);
- }
- else {
+ } else {
/* MRRC, ARMv5TE and up */
ARMul_MRRC (state, instr, &DEST, &(state->Reg[LHSReg]));
break;
@@ -4565,7 +4595,7 @@ out:
#ifdef MODE32
if (state->Bank > 0) {
state->Cpsr = state->Spsr[state->Bank];
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
}
#ifdef MODET
if (TFLAG)
@@ -5256,7 +5286,7 @@ L_ldm_s_makeabort:
//chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
if (state->Mode != USER26MODE && state->Mode != USER32MODE ) {
state->Cpsr = GETSPSR (state->Bank);
- //ARMul_CPSRAltered (state);
+ ARMul_CPSRAltered (state);
}
WriteR15 (state, PC);
@@ -5641,30 +5671,9 @@ L_stm_s_takeabort:
static int
handle_v6_insn (ARMul_State * state, ARMword instr) {
- switch (BITS (20, 27)) {
- //ichfly
- case 0x66: //UQSUB8
- if ((instr & 0x0FF00FF0) == 0x06600FF0) {
- u32 rd = (instr >> 12) & 0xF;
- u32 rm = (instr >> 16) & 0xF;
- u32 rn = (instr >> 0) & 0xF;
- u32 subfrom = state->Reg[rm];
- u32 tosub = state->Reg[rn];
+ ARMword lhs, temp;
- u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub));
- if (b1 > (u8)(subfrom)) b1 = 0;
- u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8));
- if (b2 > (u8)(subfrom >> 8)) b2 = 0;
- u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16));
- if (b3 > (u8)(subfrom >> 16)) b3 = 0;
- u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24));
- if (b4 > (u8)(subfrom >> 24)) b4 = 0;
- state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24);
- return 1;
- } else {
- printf("UQSUB8 decoding fail %08X",instr);
- }
-#if 0
+ switch (BITS (20, 27)) {
case 0x03:
printf ("Unhandled v6 insn: ldr\n");
break;
@@ -5678,9 +5687,43 @@ L_stm_s_takeabort:
printf ("Unhandled v6 insn: smi\n");
break;
case 0x18:
+ if (BITS(4, 7) == 0x9) {
+ /* strex */
+ u32 l = LHSReg;
+ u32 r = RHSReg;
+ lhs = LHS;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true;
+ //StoreWord(state, lhs, RHS)
+ if (state->Aborted) {
+ TAKEABORT;
+ }
+
+ if (enter) {
+ ARMul_StoreWordS(state, lhs, RHS);
+ state->Reg[DESTReg] = 0;
+ }
+ else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ return 1;
+ }
printf ("Unhandled v6 insn: strex\n");
break;
case 0x19:
+ /* ldrex */
+ if (BITS(4, 7) == 0x9) {
+ lhs = LHS;
+
+ state->currentexaddr = lhs;
+ state->currentexval = ARMul_ReadWord(state, lhs);
+
+ LoadWord(state, instr, lhs);
+ return 1;
+ }
printf ("Unhandled v6 insn: ldrex\n");
break;
case 0x1a:
@@ -5690,9 +5733,52 @@ L_stm_s_takeabort:
printf ("Unhandled v6 insn: ldrexd\n");
break;
case 0x1c:
+ if (BITS(4, 7) == 0x9) {
+ /* strexb */
+ lhs = LHS;
+
+ bool enter = false;
+
+ if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true;
+
+ BUSUSEDINCPCN;
+ if (state->Aborted) {
+ TAKEABORT;
+ }
+
+
+ if (enter) {
+ ARMul_StoreByte(state, lhs, RHS);
+ state->Reg[DESTReg] = 0;
+ }
+ else {
+ state->Reg[DESTReg] = 1;
+ }
+
+ //printf("In %s, strexb not implemented\n", __FUNCTION__);
+ UNDEF_LSRBPC;
+ /* WRITESDEST (dest); */
+ return 1;
+ }
printf ("Unhandled v6 insn: strexb\n");
break;
case 0x1d:
+ if ((BITS(4, 7)) == 0x9) {
+ /* ldrexb */
+ temp = LHS;
+ LoadByte(state, instr, temp, LUNSIGNED);
+
+ state->currentexaddr = temp;
+ state->currentexval = (u32)ARMul_ReadByte(state, temp);
+
+ //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]);
+ //printf("ldrexb\n");
+ //printf("instr is %x rm is %d\n", instr, BITS(16, 19));
+ //exit(-1);
+
+ //printf("In %s, ldrexb not implemented\n", __FUNCTION__);
+ return 1;
+ }
printf ("Unhandled v6 insn: ldrexb\n");
break;
case 0x1e:
@@ -5713,10 +5799,8 @@ L_stm_s_takeabort:
case 0x3f:
printf ("Unhandled v6 insn: rbit\n");
break;
-#endif
case 0x61:
- if ((instr & 0xFF0) == 0xf70)//ssub16
- {
+ if ((instr & 0xFF0) == 0xf70) { //ssub16
u8 tar = BITS(12, 15);
u8 src1 = BITS(16, 19);
u8 src2 = BITS(0, 3);
@@ -5724,23 +5808,20 @@ L_stm_s_takeabort:
s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
s16 b1 = (state->Reg[src2] & 0xFFFF);
s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a1 - a2)&0xFFFF | (((b1 - b2)&0xFFFF)<< 0x10);
+ state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2) & 0xFFFF) << 0x10);
return 1;
- }
- else if ((instr & 0xFF0) == 0xf10)//sadd16
- {
- u8 tar = BITS(12, 15);
- u8 src1 = BITS(16, 19);
- u8 src2 = BITS(0, 3);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = (state->Reg[src2] & 0xFFFF);
- s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a1 + a2)&0xFFFF | (((b1 + b2)&0xFFFF)<< 0x10);
+ } else if ((instr & 0xFF0) == 0xf10) { //sadd16
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
+ const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF);
+ const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
+ const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF);
+
+ state->Reg[rd_idx] = ((rn_lo + rm_lo) & 0xFFFF) | (((rn_hi + rm_hi) & 0xFFFF) << 16);
return 1;
- }
- else if ((instr & 0xFF0) == 0xf50)//ssax
- {
+ } else if ((instr & 0xFF0) == 0xf50) { //ssax
u8 tar = BITS(12, 15);
u8 src1 = BITS(16, 19);
u8 src2 = BITS(0, 3);
@@ -5748,11 +5829,9 @@ L_stm_s_takeabort:
s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
s16 b1 = (state->Reg[src2] & 0xFFFF);
s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a1 - b2) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10);
+ state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10);
return 1;
- }
- else if ((instr & 0xFF0) == 0xf30)//sasx
- {
+ } else if ((instr & 0xFF0) == 0xf30) { //sasx
u8 tar = BITS(12, 15);
u8 src1 = BITS(16, 19);
u8 src2 = BITS(0, 3);
@@ -5760,127 +5839,445 @@ L_stm_s_takeabort:
s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
s16 b1 = (state->Reg[src2] & 0xFFFF);
s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = (a2 - b1) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10);
+ state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10);
return 1;
- }
- else printf ("Unhandled v6 insn: sadd/ssub\n");
+ } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n");
break;
- case 0x62:
- if ((instr & 0xFF0) == 0xf70)//QSUB16
- {
- u8 tar = BITS(12, 15);
- u8 src1 = BITS(16, 19);
- u8 src2 = BITS(0, 3);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = (state->Reg[src2] & 0xFFFF);
- s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- s32 res1 = (a1 - b1);
- s32 res2 = (a2 - b2);
- if (res1 > 0x7FFF) res1 = 0x7FFF;
- if (res2 > 0x7FFF) res2 = 0x7FFF;
- if (res1 < 0x7FFF) res1 = -0x8000;
- if (res2 < 0x7FFF) res2 = -0x8000;
- state->Reg[tar] = (res1 & 0xFFFF) | ((res2 & 0xFFFF) << 0x10);
- return 1;
- }
- else if ((instr & 0xFF0) == 0xf10)//QADD16
- {
- u8 tar = BITS(12, 15);
- u8 src1 = BITS(16, 19);
- u8 src2 = BITS(0, 3);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = (state->Reg[src2] & 0xFFFF);
- s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
- s32 res1 = (a1 + b1);
- s32 res2 = (a2 + b2);
- if (res1 > 0x7FFF) res1 = 0x7FFF;
- if (res2 > 0x7FFF) res2 = 0x7FFF;
- if (res1 < 0x7FFF) res1 = -0x8000;
- if (res2 < 0x7FFF) res2 = -0x8000;
- state->Reg[tar] = ((res1) & 0xFFFF) | (((res2) & 0xFFFF) << 0x10);
+ case 0x62: // QSUB16 and QADD16
+ if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) {
+ const u8 rd_idx = BITS(12, 15);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 rm_idx = BITS(0, 3);
+ const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
+ const s16 rm_hi = ((state->Reg[rm_idx] >> 0x10) & 0xFFFF);
+ const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
+ const s16 rn_hi = ((state->Reg[rn_idx] >> 0x10) & 0xFFFF);
+
+ s32 lo_result;
+ s32 hi_result;
+
+ // QSUB16
+ if ((instr & 0xFF0) == 0xf70) {
+ lo_result = (rn_lo - rm_lo);
+ hi_result = (rn_hi - rm_hi);
+ }
+ else { // QADD16
+ lo_result = (rn_lo + rm_lo);
+ hi_result = (rn_hi + rm_hi);
+ }
+
+ if (lo_result > 0x7FFF)
+ lo_result = 0x7FFF;
+ else if (lo_result < -0x8000)
+ lo_result = -0x8000;
+
+ if (hi_result > 0x7FFF)
+ hi_result = 0x7FFF;
+ else if (hi_result < -0x8000)
+ hi_result = -0x8000;
+
+ state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
return 1;
+ } else {
+ printf("Unhandled v6 insn: %08x", BITS(20, 27));
}
- else printf ("Unhandled v6 insn: qadd/qsub\n");
break;
-#if 0
case 0x63:
printf ("Unhandled v6 insn: shadd/shsub\n");
break;
case 0x65:
- printf ("Unhandled v6 insn: uadd/usub\n");
+ {
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rn = (instr >> 16) & 0xF;
+ u32 rm = (instr >> 0) & 0xF;
+ u32 from = state->Reg[rn];
+ u32 to = state->Reg[rm];
+
+ if ((instr & 0xFF0) == 0xF10 || (instr & 0xFF0) == 0xF70) { // UADD16/USUB16
+ u32 h1, h2;
+ state->Cpsr &= 0xfff0ffff;
+ if ((instr & 0x0F0) == 0x070) { // USUB16
+ h1 = ((u16)from - (u16)to);
+ h2 = ((u16)(from >> 16) - (u16)(to >> 16));
+ if (!(h1 & 0xffff0000)) state->Cpsr |= (3 << 16);
+ if (!(h2 & 0xffff0000)) state->Cpsr |= (3 << 18);
+ }
+ else { // UADD16
+ h1 = ((u16)from + (u16)to);
+ h2 = ((u16)(from >> 16) + (u16)(to >> 16));
+ if (h1 & 0xffff0000) state->Cpsr |= (3 << 16);
+ if (h2 & 0xffff0000) state->Cpsr |= (3 << 18);
+ }
+ state->Reg[rd] = (u32)((h1 & 0xffff) | ((h2 & 0xffff) << 16));
+ return 1;
+ }
+ else
+ if ((instr & 0xFF0) == 0xF90 || (instr & 0xFF0) == 0xFF0) { // UADD8/USUB8
+ u32 b1, b2, b3, b4;
+ state->Cpsr &= 0xfff0ffff;
+ if ((instr & 0x0F0) == 0x0F0) { // USUB8
+ b1 = ((u8)from - (u8)to);
+ b2 = ((u8)(from >> 8) - (u8)(to >> 8));
+ b3 = ((u8)(from >> 16) - (u8)(to >> 16));
+ b4 = ((u8)(from >> 24) - (u8)(to >> 24));
+ if (!(b1 & 0xffffff00)) state->Cpsr |= (1 << 16);
+ if (!(b2 & 0xffffff00)) state->Cpsr |= (1 << 17);
+ if (!(b3 & 0xffffff00)) state->Cpsr |= (1 << 18);
+ if (!(b4 & 0xffffff00)) state->Cpsr |= (1 << 19);
+ }
+ else { // UADD8
+ b1 = ((u8)from + (u8)to);
+ b2 = ((u8)(from >> 8) + (u8)(to >> 8));
+ b3 = ((u8)(from >> 16) + (u8)(to >> 16));
+ b4 = ((u8)(from >> 24) + (u8)(to >> 24));
+ if (b1 & 0xffffff00) state->Cpsr |= (1 << 16);
+ if (b2 & 0xffffff00) state->Cpsr |= (1 << 17);
+ if (b3 & 0xffffff00) state->Cpsr |= (1 << 18);
+ if (b4 & 0xffffff00) state->Cpsr |= (1 << 19);
+ }
+ state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24);
+ return 1;
+ }
+ }
+ printf("Unhandled v6 insn: uasx/usax\n");
break;
case 0x66:
- printf ("Unhandled v6 insn: uqadd/uqsub\n");
+ if ((instr & 0x0FF00FF0) == 0x06600FF0) { //uqsub8
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rm = (instr >> 16) & 0xF;
+ u32 rn = (instr >> 0) & 0xF;
+ u32 subfrom = state->Reg[rm];
+ u32 tosub = state->Reg[rn];
+
+ u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub));
+ if (b1 > (u8)(subfrom)) b1 = 0;
+ u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8));
+ if (b2 > (u8)(subfrom >> 8)) b2 = 0;
+ u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16));
+ if (b3 > (u8)(subfrom >> 16)) b3 = 0;
+ u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24));
+ if (b4 > (u8)(subfrom >> 24)) b4 = 0;
+ state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24);
+ return 1;
+ } else {
+ printf ("Unhandled v6 insn: uqsub16\n");
+ }
break;
case 0x67:
printf ("Unhandled v6 insn: uhadd/uhsub\n");
break;
case 0x68:
- printf ("Unhandled v6 insn: pkh/sxtab/selsxtb\n");
- break;
-#endif
- case 0x6c:
- if ((instr & 0xf03f0) == 0xf0070) //uxtb16
- {
- u8 src1 = BITS(0, 3);
- u8 tar = BITS(12, 15);
- u32 base = state->Reg[src1];
- u32 shamt = BITS(9,10)* 8;
- u32 in = ((base << (32 - shamt)) | (base >> shamt));
- state->Reg[tar] = in & 0x00FF00FF;
+ {
+ u32 rd = (instr >> 12) & 0xF;
+ u32 rn = (instr >> 16) & 0xF;
+ u32 rm = (instr >> 0) & 0xF;
+ u32 from = state->Reg[rn];
+ u32 to = state->Reg[rm];
+ u32 cpsr = state->Cpsr;
+ if ((instr & 0xFF0) == 0xFB0) { // SEL
+ u32 result;
+ if (cpsr & (1 << 16))
+ result = from & 0xff;
+ else
+ result = to & 0xff;
+ if (cpsr & (1 << 17))
+ result |= from & 0x0000ff00;
+ else
+ result |= to & 0x0000ff00;
+ if (cpsr & (1 << 18))
+ result |= from & 0x00ff0000;
+ else
+ result |= to & 0x00ff0000;
+ if (cpsr & (1 << 19))
+ result |= from & 0xff000000;
+ else
+ result |= to & 0xff000000;
+ state->Reg[rd] = result;
return 1;
}
- else
- printf ("Unhandled v6 insn: uxtb16/uxtab16\n");
+ }
+ printf("Unhandled v6 insn: pkh/sxtab/selsxtb\n");
break;
+ case 0x6a: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0x01:
+ case 0xf3:
+ //ichfly
+ //SSAT16
+ {
+ u8 tar = BITS(12, 15);
+ u8 src = BITS(0, 3);
+ u8 val = BITS(16, 19) + 1;
+ s16 a1 = (state->Reg[src]);
+ s16 a2 = (state->Reg[src] >> 0x10);
+ s16 min = (s16)(0x8000 >> (16 - val));
+ s16 max = 0x7FFF >> (16 - val);
+ if (min > a1) a1 = min;
+ if (max < a1) a1 = max;
+ if (min > a2) a2 = min;
+ if (max < a2) a2 = max;
+ u32 temp2 = ((u32)(a2)) << 0x10;
+ state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
+ }
+
+ return 1;
+ default:
+ break;
+ }
+
+ if (ror == -1) {
+ if (BITS(4, 6) == 0x7) {
+ printf("Unhandled v6 insn: ssat\n");
+ return 0;
+ }
+ break;
+ }
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF);
+ if (Rm & 0x80)
+ Rm |= 0xffffff00;
+
+ if (BITS(16, 19) == 0xf)
+ /* SXTB */
+ state->Reg[BITS(12, 15)] = Rm;
+ else
+ /* SXTAB */
+ state->Reg[BITS(12, 15)] += Rm;
+
+ return 1;
+ }
+ case 0x6b: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0xf3:
+ DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24);
+ return 1;
+ case 0xfb:
+ DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8);
+ return 1;
+ default:
+ break;
+ }
+
+ if (ror == -1)
+ break;
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF);
+ if (Rm & 0x8000)
+ Rm |= 0xffff0000;
+
+ if (BITS(16, 19) == 0xf)
+ /* SXTH */
+ state->Reg[BITS(12, 15)] = Rm;
+ else
+ /* SXTAH */
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm;
+
+ return 1;
+ }
+ case 0x6c: // UXTB16 and UXTAB16
+ {
+ const u8 rm_idx = BITS(0, 3);
+ const u8 rn_idx = BITS(16, 19);
+ const u8 rd_idx = BITS(12, 15);
+ const u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+ const u32 rotation = BITS(10, 11) * 8;
+ const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
+
+ // UXTB16
+ if ((instr & 0xf03f0) == 0xf0070) {
+ state->Reg[rd_idx] = rotated_rm & 0x00FF00FF;
+ }
+ else { // UXTAB16
+ const u8 lo_rotated = (rotated_rm & 0xFF);
+ const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated;
+
+ const u8 hi_rotated = (rotated_rm >> 16) & 0xFF;
+ const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated;
+
+ state->Reg[rd_idx] = ((hi_result << 16) | (lo_result & 0xFFFF));
+ }
+
+ return 1;
+ }
+ break;
+ case 0x6e: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0x01:
+ case 0xf3:
+ //ichfly
+ //USAT16
+ {
+ u8 tar = BITS(12, 15);
+ u8 src = BITS(0, 3);
+ u8 val = BITS(16, 19);
+ s16 a1 = (state->Reg[src]);
+ s16 a2 = (state->Reg[src] >> 0x10);
+ s16 max = 0xFFFF >> (16 - val);
+ if (max < a1) a1 = max;
+ if (max < a2) a2 = max;
+ u32 temp2 = ((u32)(a2)) << 0x10;
+ state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
+ }
+ return 1;
+ default:
+ break;
+ }
+
+ if (ror == -1) {
+ if (BITS(4, 6) == 0x7) {
+ printf("Unhandled v6 insn: usat\n");
+ return 0;
+ }
+ break;
+ }
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF);
+
+ if (BITS(16, 19) == 0xf)
+ /* UXTB */
+ state->Reg[BITS(12, 15)] = Rm;
+ else
+ /* UXTAB */
+ state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm;
+
+ return 1;
+ }
+
+ case 0x6f: {
+ ARMword Rm;
+ int ror = -1;
+
+ switch (BITS(4, 11)) {
+ case 0x07:
+ ror = 0;
+ break;
+ case 0x47:
+ ror = 8;
+ break;
+ case 0x87:
+ ror = 16;
+ break;
+ case 0xc7:
+ ror = 24;
+ break;
+
+ case 0xfb:
+ printf("Unhandled v6 insn: revsh\n");
+ return 0;
+ default:
+ break;
+ }
+
+ if (ror == -1)
+ break;
+
+ Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF);
+
+ /* UXT */
+ /* state->Reg[BITS (12, 15)] = Rm; */
+ /* dyf add */
+ if (BITS(16, 19) == 0xf) {
+ state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF;
+ }
+ else {
+ /* UXTAH */
+ /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */
+ // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)]
+ // , Rm, BITS(10, 11));
+ // printf("icounter is %lld\n", state->NumInstrs);
+ state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm;
+ // printf("rd is %x\n", state->Reg[BITS (12, 15)]);
+ // exit(-1);
+ }
+
+ return 1;
+ }
case 0x70:
- if ((instr & 0xf0d0) == 0xf010)//smuad //ichfly
- {
- u8 tar = BITS(16, 19);
- u8 src1 = BITS(0, 3);
- u8 src2 = BITS(8, 11);
- u8 swap = BIT(5);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF);
- s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = a1*a2 + b1*b2;
- return 1;
-
- }
- else if ((instr & 0xf0d0) == 0xf050)//smusd
- {
- u8 tar = BITS(16, 19);
- u8 src1 = BITS(0, 3);
- u8 src2 = BITS(8, 11);
- u8 swap = BIT(5);
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF);
- s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = a1*a2 - b1*b2;
- return 1;
- }
- else if ((instr & 0xd0) == 0x10)//smlad
- {
- u8 tar = BITS(16, 19);
- u8 src1 = BITS(0, 3);
- u8 src2 = BITS(8, 11);
- u8 src3 = BITS(12, 15);
- u8 swap = BIT(5);
-
- u32 a3 = state->Reg[src3];
-
- s16 a1 = (state->Reg[src1] & 0xFFFF);
- s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
- s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF);
- s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF);
- state->Reg[tar] = a1*a2 + b1*b2 + a3;
+ // ichfly
+ // SMUAD, SMUSD, SMLAD
+ if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 || (instr & 0xd0) == 0x10) {
+ const u8 rd_idx = BITS(16, 19);
+ const u8 rn_idx = BITS(0, 3);
+ const u8 rm_idx = BITS(8, 11);
+ const bool do_swap = (BIT(5) == 1);
+
+ u32 rm_val = state->Reg[rm_idx];
+ const u32 rn_val = state->Reg[rn_idx];
+
+ if (do_swap)
+ rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
+
+ const s16 rm_lo = (rm_val & 0xFFFF);
+ const s16 rm_hi = ((rm_val >> 16) & 0xFFFF);
+ const s16 rn_lo = (rn_val & 0xFFFF);
+ const s16 rn_hi = ((rn_val >> 16) & 0xFFFF);
+
+ // SMUAD
+ if ((instr & 0xf0d0) == 0xf010) {
+ state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi);
+ }
+ // SMUSD
+ else if ((instr & 0xf0d0) == 0xf050) {
+ state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi);
+ }
+ // SMLAD
+ else {
+ const u8 ra_idx = BITS(12, 15);
+ state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx];
+ }
return 1;
+ } else {
+ printf ("Unhandled v6 insn: smlsd\n");
}
- else printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n");
break;
case 0x74:
printf ("Unhandled v6 insn: smlald/smlsld\n");
@@ -5891,332 +6288,18 @@ L_stm_s_takeabort:
case 0x78:
printf ("Unhandled v6 insn: usad/usada8\n");
break;
-#if 0
case 0x7a:
printf ("Unhandled v6 insn: usbfx\n");
break;
case 0x7c:
printf ("Unhandled v6 insn: bfc/bfi\n");
break;
-#endif
-
-
- /* add new instr for arm v6. */
- ARMword lhs, temp;
- case 0x18: { /* ORR reg */
- /* dyf add armv6 instr strex 2010.9.17 */
- if (BITS (4, 7) == 0x9) {
- u32 l = LHSReg;
- u32 r = RHSReg;
- lhs = LHS;
-
- bool enter = false;
-
- if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true;
- ARMul_StoreWordS(state, lhs, RHS);
- //StoreWord(state, lhs, RHS)
- if (state->Aborted) {
- TAKEABORT;
- }
-
- if (enter) {
- state->Reg[DESTReg] = 0;
- } else {
- state->Reg[DESTReg] = 1;
- }
-
- return 1;
- }
- break;
- }
-
- case 0x19: { /* orrs reg */
- /* dyf add armv6 instr ldrex */
- if (BITS (4, 7) == 0x9) {
- lhs = LHS;
-
- state->currentexaddr = lhs;
- state->currentexval = ARMul_ReadWord(state, lhs);
-
- LoadWord (state, instr, lhs);
- return 1;
- }
- break;
- }
-
- case 0x1c: { /* BIC reg */
- /* dyf add for STREXB */
- if (BITS (4, 7) == 0x9) {
- lhs = LHS;
-
- bool enter = false;
-
- if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true;
-
- ARMul_StoreByte (state, lhs, RHS);
- BUSUSEDINCPCN;
- if (state->Aborted) {
- TAKEABORT;
- }
-
-
- if (enter) {
- state->Reg[DESTReg] = 0;
- } else {
- state->Reg[DESTReg] = 1;
- }
-
- //printf("In %s, strexb not implemented\n", __FUNCTION__);
- UNDEF_LSRBPC;
- /* WRITESDEST (dest); */
- return 1;
- }
- break;
- }
-
- case 0x1d: { /* BICS reg */
- if ((BITS (4, 7)) == 0x9) {
- /* ldrexb */
- temp = LHS;
- LoadByte (state, instr, temp, LUNSIGNED);
-
- state->currentexaddr = temp;
- state->currentexval = (u32)ARMul_ReadByte(state, temp);
-
- //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]);
- //printf("ldrexb\n");
- //printf("instr is %x rm is %d\n", instr, BITS(16, 19));
- //exit(-1);
-
- //printf("In %s, ldrexb not implemented\n", __FUNCTION__);
- return 1;
- }
- break;
- }
- /* add end */
-
- case 0x6a: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0x01:
- case 0xf3:
- //ichfly
- //SSAT16
- {
- u8 tar = BITS(12,15);
- u8 src = BITS(0, 3);
- u8 val = BITS(16, 19) + 1;
- s16 a1 = (state->Reg[src]);
- s16 a2 = (state->Reg[src] >> 0x10);
- s16 min = (s16)(0x8000) >> (16 - val);
- s16 max = 0x7FFF >> (16 - val);
- if (min > a1) a1 = min;
- if (max < a1) a1 = max;
- if (min > a2) a2 = min;
- if (max < a2) a2 = max;
- u32 temp2 = ((u32)(a2)) << 0x10;
- state->Reg[tar] = (a1&0xFFFF) | (temp2);
- }
-
- return 1;
- default:
- break;
- }
-
- if (ror == -1) {
- if (BITS (4, 6) == 0x7) {
- printf ("Unhandled v6 insn: ssat\n");
- return 0;
- }
- break;
- }
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
- if (Rm & 0x80)
- Rm |= 0xffffff00;
-
- if (BITS (16, 19) == 0xf)
- /* SXTB */
- state->Reg[BITS (12, 15)] = Rm;
- else
- /* SXTAB */
- state->Reg[BITS (12, 15)] += Rm;
- }
- return 1;
-
- case 0x6b: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0xf3:
- DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24);
- return 1;
- case 0xfb:
- DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8);
- return 1;
- default:
- break;
- }
-
- if (ror == -1)
- break;
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
- if (Rm & 0x8000)
- Rm |= 0xffff0000;
-
- if (BITS (16, 19) == 0xf)
- /* SXTH */
- state->Reg[BITS (12, 15)] = Rm;
- else
- /* SXTAH */
- state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
- }
- return 1;
-
- case 0x6e: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0x01:
- case 0xf3:
- //ichfly
- //USAT16
- {
- u8 tar = BITS(12, 15);
- u8 src = BITS(0, 3);
- u8 val = BITS(16, 19);
- s16 a1 = (state->Reg[src]);
- s16 a2 = (state->Reg[src] >> 0x10);
- s16 max = 0xFFFF >> (16 - val);
- if (max < a1) a1 = max;
- if (max < a2) a2 = max;
- u32 temp2 = ((u32)(a2)) << 0x10;
- state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
- }
- return 1;
- default:
- break;
- }
-
- if (ror == -1) {
- if (BITS (4, 6) == 0x7) {
- printf ("Unhandled v6 insn: usat\n");
- return 0;
- }
- break;
- }
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
-
- if (BITS (16, 19) == 0xf)
- /* UXTB */
- state->Reg[BITS (12, 15)] = Rm;
- else
- /* UXTAB */
- state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
- }
- return 1;
-
- case 0x6f: {
- ARMword Rm;
- int ror = -1;
-
- switch (BITS (4, 11)) {
- case 0x07:
- ror = 0;
- break;
- case 0x47:
- ror = 8;
- break;
- case 0x87:
- ror = 16;
- break;
- case 0xc7:
- ror = 24;
- break;
-
- case 0xfb:
- printf ("Unhandled v6 insn: revsh\n");
- return 0;
- default:
- break;
- }
-
- if (ror == -1)
- break;
-
- Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
-
- /* UXT */
- /* state->Reg[BITS (12, 15)] = Rm; */
- /* dyf add */
- if (BITS (16, 19) == 0xf) {
- state->Reg[BITS (12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF;
- } else {
- /* UXTAH */
- /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */
-// printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)]
-// , Rm, BITS(10, 11));
-// printf("icounter is %lld\n", state->NumInstrs);
- state->Reg[BITS (12, 15)] = (state->Reg[BITS (16, 19)] >> (8 * (BITS(10, 11)))) + Rm;
-// printf("rd is %x\n", state->Reg[BITS (12, 15)]);
-// exit(-1);
- }
- }
- return 1;
-
-#if 0
case 0x84:
printf ("Unhandled v6 insn: srs\n");
break;
-#endif
default:
break;
}
printf("Unhandled v6 insn: UNKNOWN: %08x %08X\n", instr, BITS(20, 27));
return 0;
- }
+ } \ No newline at end of file
diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp
index 2568b93e..30519f21 100644
--- a/src/core/arm/interpreter/armsupp.cpp
+++ b/src/core/arm/interpreter/armsupp.cpp
@@ -665,7 +665,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
//if (!CP_ACCESS_ALLOWED (state, CPNum)) {
if (!state->MCR[CPNum]) {
//chy 2004-07-19 should fix in the future ????!!!!
- DEBUG("SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x\n",CPNum, source);
+ LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x",CPNum, source);
ARMul_UndefInstr (state, instr);
return;
}
@@ -690,7 +690,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
}
if (cpab == ARMul_CANT) {
- DEBUG("SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x\n", instr, CPNum, source); //ichfly todo
+ LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x", instr, CPNum, source); //ichfly todo
//ARMul_Abort (state, ARMul_UndefinedInstrV);
} else {
BUSUSEDINCPCN;
@@ -762,7 +762,7 @@ ARMword ARMul_MRC (ARMul_State * state, ARMword instr)
//if (!CP_ACCESS_ALLOWED (state, CPNum)) {
if (!state->MRC[CPNum]) {
//chy 2004-07-19 should fix in the future????!!!!
- DEBUG("SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x\n", CPNum, instr);
+ LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x", CPNum, instr);
ARMul_UndefInstr (state, instr);
return -1;
}
@@ -865,7 +865,7 @@ void
ARMul_UndefInstr (ARMul_State * state, ARMword instr)
{
std::string disasm = ARM_Disasm::Disassemble(state->pc, instr);
- ERROR_LOG(ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
+ LOG_ERROR(Core_ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
ARMul_Abort (state, ARMul_UndefinedInstrV);
}
diff --git a/src/core/arm/interpreter/thumbemu.cpp b/src/core/arm/interpreter/thumbemu.cpp
index f7f11f71..9cf80672 100644
--- a/src/core/arm/interpreter/thumbemu.cpp
+++ b/src/core/arm/interpreter/thumbemu.cpp
@@ -467,7 +467,7 @@ ARMul_ThumbDecode (
(state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC;
state->Reg[14] = (tmp | 1);
CLEART;
- DEBUG_LOG(ARM11, "In %s, After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x\n", __FUNCTION__, state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
+ LOG_DEBUG(Core_ARM11, "After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x", state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
valid = t_branch;
FLUSHPIPE;
}
diff --git a/src/core/arm/skyeye_common/armcpu.h b/src/core/arm/skyeye_common/armcpu.h
index 3a029f0e..2b756c5b 100644
--- a/src/core/arm/skyeye_common/armcpu.h
+++ b/src/core/arm/skyeye_common/armcpu.h
@@ -24,8 +24,6 @@
#include <stddef.h>
#include <stdio.h>
-#include "common/thread.h"
-
#include "core/arm/skyeye_common/armdefs.h"
typedef struct ARM_CPU_State_s {
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h
index 8e71948c..28a4a0db 100644
--- a/src/core/arm/skyeye_common/armdefs.h
+++ b/src/core/arm/skyeye_common/armdefs.h
@@ -18,38 +18,26 @@
#ifndef _ARMDEFS_H_
#define _ARMDEFS_H_
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include "common/platform.h"
-
-//teawater add for arm2x86 2005.02.14-------------------------------------------
-// koodailar remove it for mingw 2005.12.18----------------
-//anthonylee modify it for portable 2007.01.30
-//#include "portable/mman.h"
+#include <cerrno>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include "arm_regformat.h"
+#include "common/common_types.h"
#include "common/platform.h"
+#include "core/arm/skyeye_common/armmmu.h"
#include "core/arm/skyeye_common/skyeye_defs.h"
-//AJ2D--------------------------------------------------------------------------
-
-//teawater add for arm2x86 2005.07.03-------------------------------------------
-
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#if EMU_PLATFORM == PLATFORM_LINUX
+#include <sys/time.h>
#include <unistd.h>
#endif
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-//#include <memory_space.h>
-//AJ2D--------------------------------------------------------------------------
#if 0
#if 0
#define DIFF_STATE 1
@@ -70,25 +58,8 @@
#define LOWHIGH 1
#define HIGHLOW 2
-//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
-#include <signal.h>
-
-#include "common/platform.h"
-
-#if EMU_PLATFORM == PLATFORM_LINUX
-#include <sys/time.h>
-#endif
-
//#define DBCT_TEST_SPEED
#define DBCT_TEST_SPEED_SEC 10
-//AJ2D--------------------------------------------------------------------------
-
-//teawater add compile switch for DBCT GDB RSP function 2005.10.21--------------
-//#define DBCT_GDBRSP
-//AJ2D--------------------------------------------------------------------------
-
-//#include <skyeye_defs.h>
-//#include <skyeye_types.h>
#define ARM_BYTE_TYPE 0
#define ARM_HALFWORD_TYPE 1
@@ -103,71 +74,34 @@
typedef char *VoidStar;
#endif
-typedef unsigned long long ARMdword; /* must be 64 bits wide */
-typedef unsigned int ARMword; /* must be 32 bits wide */
-typedef unsigned char ARMbyte; /* must be 8 bits wide */
-typedef unsigned short ARMhword; /* must be 16 bits wide */
+typedef u64 ARMdword; // must be 64 bits wide
+typedef u32 ARMword; // must be 32 bits wide
+typedef u16 ARMhword; // must be 16 bits wide
+typedef u8 ARMbyte; // must be 8 bits wide
typedef struct ARMul_State ARMul_State;
typedef struct ARMul_io ARMul_io;
typedef struct ARMul_Energy ARMul_Energy;
-//teawater add for arm2x86 2005.06.24-------------------------------------------
-#include <stdint.h>
-//AJ2D--------------------------------------------------------------------------
-/*
-//chy 2005-05-11
-#ifndef __CYGWIN__
-//teawater add for arm2x86 2005.02.14-------------------------------------------
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int u32;
-#if defined (__x86_64__)
-typedef unsigned long uint64_t;
-#else
-typedef unsigned long long uint64_t;
-#endif
-////AJ2D--------------------------------------------------------------------------
-#endif
-*/
-#include "core/arm/skyeye_common/armmmu.h"
-//#include "lcd/skyeye_lcd.h"
-
-
-//#include "skyeye.h"
-//#include "skyeye_device.h"
-//#include "net/skyeye_net.h"
-//#include "skyeye_config.h"
-
-
-typedef unsigned ARMul_CPInits (ARMul_State * state);
-typedef unsigned ARMul_CPExits (ARMul_State * state);
-typedef unsigned ARMul_LDCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword value);
-typedef unsigned ARMul_STCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword * value);
-typedef unsigned ARMul_MRCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword * value);
-typedef unsigned ARMul_MCRs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword value);
-typedef unsigned ARMul_MRRCs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword * value1, ARMword * value2);
-typedef unsigned ARMul_MCRRs (ARMul_State * state, unsigned type,
- ARMword instr, ARMword value1, ARMword value2);
-typedef unsigned ARMul_CDPs (ARMul_State * state, unsigned type,
- ARMword instr);
-typedef unsigned ARMul_CPReads (ARMul_State * state, unsigned reg,
- ARMword * value);
-typedef unsigned ARMul_CPWrites (ARMul_State * state, unsigned reg,
- ARMword value);
+typedef unsigned ARMul_CPInits(ARMul_State* state);
+typedef unsigned ARMul_CPExits(ARMul_State* state);
+typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
+typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
+typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
+typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
+typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr);
+typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value);
+typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value);
//added by ksh,2004-3-5
struct ARMul_io
{
- ARMword *instr; //to display the current interrupt state
- ARMword *net_flag; //to judge if network is enabled
- ARMword *net_int; //netcard interrupt
+ ARMword *instr; // to display the current interrupt state
+ ARMword *net_flag; // to judge if network is enabled
+ ARMword *net_int; // netcard interrupt
//ywc,2004-04-01
ARMword *ts_int;
@@ -180,17 +114,17 @@ struct ARMul_io
/* added by ksh,2004-11-26,some energy profiling */
struct ARMul_Energy
{
- int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
- int enable_func_energy; /* <tktan> BUG200105181702 */
+ int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
+ int enable_func_energy; /* <tktan> BUG200105181702 */
char *func_energy;
- int func_display; /* <tktan> BUG200103311509 : for function call display */
+ int func_display; /* <tktan> BUG200103311509 : for function call display */
int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */
- char *start_func; /* <tktan> BUG200104191428 */
+ char *start_func; /* <tktan> BUG200104191428 */
- FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
+ FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
long long tcycle, pcycle;
float t_energy;
- void *cur_task; /* <tktan> BUG200103291737 */
+ void *cur_task; /* <tktan> BUG200103291737 */
long long t_mem_cycle, t_idle_cycle, t_uart_cycle;
long long p_mem_cycle, p_idle_cycle, p_uart_cycle;
long long p_io_update_tcycle;
@@ -203,13 +137,12 @@ struct ARMul_Energy
typedef struct mem_bank
{
- ARMword (*read_byte) (ARMul_State * state, ARMword addr);
- void (*write_byte) (ARMul_State * state, ARMword addr, ARMword data);
- ARMword (*read_halfword) (ARMul_State * state, ARMword addr);
- void (*write_halfword) (ARMul_State * state, ARMword addr,
- ARMword data);
- ARMword (*read_word) (ARMul_State * state, ARMword addr);
- void (*write_word) (ARMul_State * state, ARMword addr, ARMword data);
+ ARMword (*read_byte) (ARMul_State* state, ARMword addr);
+ void (*write_byte) (ARMul_State* state, ARMword addr, ARMword data);
+ ARMword (*read_halfword) (ARMul_State* state, ARMword addr);
+ void (*write_halfword) (ARMul_State* state, ARMword addr, ARMword data);
+ ARMword (*read_word) (ARMul_State* state, ARMword addr);
+ void (*write_word) (ARMul_State* state, ARMword addr, ARMword data);
unsigned int addr, len;
char filename[MAX_STR];
unsigned type; //chy 2003-09-21: maybe io,ram,rom
@@ -224,24 +157,24 @@ typedef struct
#define VFP_REG_NUM 64
struct ARMul_State
{
- ARMword Emulate; /* to start and stop emulation */
- unsigned EndCondition; /* reason for stopping */
+ ARMword Emulate; /* to start and stop emulation */
+ unsigned EndCondition; /* reason for stopping */
unsigned ErrorCode; /* type of illegal instruction */
/* Order of the following register should not be modified */
- ARMword Reg[16]; /* the current register file */
- ARMword Cpsr; /* the current psr */
+ ARMword Reg[16]; /* the current register file */
+ ARMword Cpsr; /* the current psr */
ARMword Spsr_copy;
ARMword phys_pc;
ARMword Reg_usr[2];
- ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
+ ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */
ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */
ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */
ARMword Reg_firq[7]; /* R8---R14 FIRQ */
- ARMword Spsr[7]; /* the exception psr's */
- ARMword Mode; /* the current mode */
- ARMword Bank; /* the current register bank */
+ ARMword Spsr[7]; /* the exception psr's */
+ ARMword Mode; /* the current mode */
+ ARMword Bank; /* the current register bank */
ARMword exclusive_tag;
ARMword exclusive_state;
ARMword exclusive_result;
@@ -281,38 +214,39 @@ struct ARMul_State
ARMword currentexaddr;
ARMword currentexval;
+ ARMword currentexvald;
ARMword servaddr;
unsigned NextInstr;
- unsigned VectorCatch; /* caught exception mask */
- unsigned CallDebug; /* set to call the debugger */
- unsigned CanWatch; /* set by memory interface if its willing to suffer the
- overhead of checking for watchpoints on each memory
- access */
+ unsigned VectorCatch; /* caught exception mask */
+ unsigned CallDebug; /* set to call the debugger */
+ unsigned CanWatch; /* set by memory interface if its willing to suffer the
+ overhead of checking for watchpoints on each memory
+ access */
unsigned int StopHandle;
- char *CommandLine; /* Command Line from ARMsd */
-
- ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
- ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
- ARMul_LDCs *LDC[16]; /* LDC instruction */
- ARMul_STCs *STC[16]; /* STC instruction */
- ARMul_MRCs *MRC[16]; /* MRC instruction */
- ARMul_MCRs *MCR[16]; /* MCR instruction */
- ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
- ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
- ARMul_CDPs *CDP[16]; /* CDP instruction */
- ARMul_CPReads *CPRead[16]; /* Read CP register */
- ARMul_CPWrites *CPWrite[16]; /* Write CP register */
- unsigned char *CPData[16]; /* Coprocessor data */
+ char *CommandLine; /* Command Line from ARMsd */
+
+ ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
+ ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
+ ARMul_LDCs *LDC[16]; /* LDC instruction */
+ ARMul_STCs *STC[16]; /* STC instruction */
+ ARMul_MRCs *MRC[16]; /* MRC instruction */
+ ARMul_MCRs *MCR[16]; /* MCR instruction */
+ ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
+ ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
+ ARMul_CDPs *CDP[16]; /* CDP instruction */
+ ARMul_CPReads *CPRead[16]; /* Read CP register */
+ ARMul_CPWrites *CPWrite[16]; /* Write CP register */
+ unsigned char *CPData[16]; /* Coprocessor data */
unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */
- unsigned EventSet; /* the number of events in the queue */
- unsigned int Now; /* time to the nearest cycle */
- struct EventNode **EventPtr; /* the event list */
+ unsigned EventSet; /* the number of events in the queue */
+ unsigned int Now; /* time to the nearest cycle */
+ struct EventNode **EventPtr; /* the event list */
- unsigned Debug; /* show instructions as they are executed */
- unsigned NresetSig; /* reset the processor */
+ unsigned Debug; /* show instructions as they are executed */
+ unsigned NresetSig; /* reset the processor */
unsigned NfiqSig;
unsigned NirqSig;
@@ -356,12 +290,12 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
*/
unsigned lateabtSig;
- ARMword Vector; /* synthesize aborts in cycle modes */
- ARMword Aborted; /* sticky flag for aborts */
- ARMword Reseted; /* sticky flag for Reset */
+ ARMword Vector; /* synthesize aborts in cycle modes */
+ ARMword Aborted; /* sticky flag for aborts */
+ ARMword Reseted; /* sticky flag for Reset */
ARMword Inted, LastInted; /* sticky flags for interrupts */
- ARMword Base; /* extra hand for base writeback */
- ARMword AbortAddr; /* to keep track of Prefetch aborts */
+ ARMword Base; /* extra hand for base writeback */
+ ARMword AbortAddr; /* to keep track of Prefetch aborts */
const struct Dbg_HostosInterface *hostif;
@@ -378,7 +312,7 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
//chy: 2003-08-11, for different arm core type
unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */
unsigned is_v5; /* Are we emulating a v5 architecture ? */
- unsigned is_v5e; /* Are we emulating a v5e architecture ? */
+ unsigned is_v5e; /* Are we emulating a v5e architecture ? */
unsigned is_v6; /* Are we emulating a v6 architecture ? */
unsigned is_v7; /* Are we emulating a v7 architecture ? */
unsigned is_XScale; /* Are we emulating an XScale architecture ? */
@@ -387,51 +321,43 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
//chy 2005-09-19
unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */
//chy: seems only used in xscale's CP14
- unsigned int LastTime; /* Value of last call to ARMul_Time() */
+ unsigned int LastTime; /* Value of last call to ARMul_Time() */
ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */
-//added by ksh:for handle different machs io 2004-3-5
+ //added by ksh:for handle different machs io 2004-3-5
ARMul_io mach_io;
-/*added by ksh,2004-11-26,some energy profiling*/
+ /*added by ksh,2004-11-26,some energy profiling*/
ARMul_Energy energy;
-//teawater add for next_dis 2004.10.27-----------------------
+ //teawater add for next_dis 2004.10.27-----------------------
int disassemble;
-//AJ2D------------------------------------------
-//teawater add for arm2x86 2005.02.15-------------------------------------------
+
+ //teawater add for arm2x86 2005.02.15-------------------------------------------
u32 trap;
u32 tea_break_addr;
u32 tea_break_ok;
int tea_pc;
-//AJ2D--------------------------------------------------------------------------
-//teawater add for arm2x86 2005.07.03-------------------------------------------
-
- /*
- * 2007-01-24 removed the term-io functions by Anthony Lee,
- * moved to "device/uart/skyeye_uart_stdio.c".
- */
-//AJ2D--------------------------------------------------------------------------
-//teawater add for arm2x86 2005.07.05-------------------------------------------
+ //teawater add for arm2x86 2005.07.05-------------------------------------------
//arm_arm A2-18
int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model
-//AJ2D--------------------------------------------------------------------------
-//teawater change for return if running tb dirty 2005.07.09---------------------
+
+ //teawater change for return if running tb dirty 2005.07.09---------------------
void *tb_now;
-//AJ2D--------------------------------------------------------------------------
-//teawater add for record reg value to ./reg.txt 2005.07.10---------------------
+
+ //teawater add for record reg value to ./reg.txt 2005.07.10---------------------
FILE *tea_reg_fd;
-//AJ2D--------------------------------------------------------------------------
-/*added by ksh in 2005-10-1*/
+
+ /*added by ksh in 2005-10-1*/
cpu_config_t *cpu;
//mem_config_t *mem_bank;
-/* added LPC remap function */
+ /* added LPC remap function */
int vector_remap_flag;
u32 vector_remap_addr;
u32 vector_remap_size;
@@ -486,17 +412,14 @@ typedef ARMul_State arm_core_t;
#define ARM_Debug_Prop 0x10
#define ARM_Isync_Prop ARM_Debug_Prop
#define ARM_Lock_Prop 0x20
-//chy 2003-08-11
#define ARM_v4_Prop 0x40
#define ARM_v5_Prop 0x80
-/*jeff.du 2010-08-05 */
#define ARM_v6_Prop 0xc0
#define ARM_v5e_Prop 0x100
#define ARM_XScale_Prop 0x200
#define ARM_ep9312_Prop 0x400
#define ARM_iWMMXt_Prop 0x800
-//chy 2005-09-19
#define ARM_PXA27X_Prop 0x1000
#define ARM_v7_Prop 0x2000
@@ -591,47 +514,44 @@ typedef ARMul_State arm_core_t;
#ifdef __cplusplus
extern "C" {
#endif
-extern void ARMul_EmulateInit (void);
-extern void ARMul_Reset (ARMul_State * state);
+extern void ARMul_EmulateInit();
+extern void ARMul_Reset(ARMul_State* state);
#ifdef __cplusplus
}
#endif
-extern ARMul_State *ARMul_NewState (ARMul_State * state);
-extern ARMword ARMul_DoProg (ARMul_State * state);
-extern ARMword ARMul_DoInstr (ARMul_State * state);
+extern ARMul_State *ARMul_NewState(ARMul_State* state);
+extern ARMword ARMul_DoProg(ARMul_State* state);
+extern ARMword ARMul_DoInstr(ARMul_State* state);
/***************************************************************************\
* Definitons of things for event handling *
\***************************************************************************/
-extern void ARMul_ScheduleEvent (ARMul_State * state, unsigned int delay,
- unsigned (*func) ());
-extern void ARMul_EnvokeEvent (ARMul_State * state);
-extern unsigned int ARMul_Time (ARMul_State * state);
+extern void ARMul_ScheduleEvent(ARMul_State* state, unsigned int delay, unsigned(*func) ());
+extern void ARMul_EnvokeEvent(ARMul_State* state);
+extern unsigned int ARMul_Time(ARMul_State* state);
/***************************************************************************\
* Useful support routines *
\***************************************************************************/
-extern ARMword ARMul_GetReg (ARMul_State * state, unsigned mode,
- unsigned reg);
-extern void ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg,
- ARMword value);
-extern ARMword ARMul_GetPC (ARMul_State * state);
-extern ARMword ARMul_GetNextPC (ARMul_State * state);
-extern void ARMul_SetPC (ARMul_State * state, ARMword value);
-extern ARMword ARMul_GetR15 (ARMul_State * state);
-extern void ARMul_SetR15 (ARMul_State * state, ARMword value);
-
-extern ARMword ARMul_GetCPSR (ARMul_State * state);
-extern void ARMul_SetCPSR (ARMul_State * state, ARMword value);
-extern ARMword ARMul_GetSPSR (ARMul_State * state, ARMword mode);
-extern void ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value);
+extern ARMword ARMul_GetReg (ARMul_State* state, unsigned mode, unsigned reg);
+extern void ARMul_SetReg (ARMul_State* state, unsigned mode, unsigned reg, ARMword value);
+extern ARMword ARMul_GetPC(ARMul_State* state);
+extern ARMword ARMul_GetNextPC(ARMul_State* state);
+extern void ARMul_SetPC(ARMul_State* state, ARMword value);
+extern ARMword ARMul_GetR15(ARMul_State* state);
+extern void ARMul_SetR15(ARMul_State* state, ARMword value);
+
+extern ARMword ARMul_GetCPSR(ARMul_State* state);
+extern void ARMul_SetCPSR(ARMul_State* state, ARMword value);
+extern ARMword ARMul_GetSPSR(ARMul_State* state, ARMword mode);
+extern void ARMul_SetSPSR(ARMul_State* state, ARMword mode, ARMword value);
/***************************************************************************\
* Definitons of things to handle aborts *
\***************************************************************************/
-extern void ARMul_Abort (ARMul_State * state, ARMword address);
+extern void ARMul_Abort(ARMul_State* state, ARMword address);
#ifdef MODET
#define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */
#define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \
@@ -649,54 +569,40 @@ extern void ARMul_Abort (ARMul_State * state, ARMword address);
* Definitons of things in the memory interface *
\***************************************************************************/
-extern unsigned ARMul_MemoryInit (ARMul_State * state,
- unsigned int initmemsize);
-extern void ARMul_MemoryExit (ARMul_State * state);
+extern unsigned ARMul_MemoryInit(ARMul_State* state, unsigned int initmemsize);
+extern void ARMul_MemoryExit(ARMul_State* state);
-extern ARMword ARMul_LoadInstrS (ARMul_State * state, ARMword address,
- ARMword isize);
-extern ARMword ARMul_LoadInstrN (ARMul_State * state, ARMword address,
- ARMword isize);
+extern ARMword ARMul_LoadInstrS(ARMul_State* state, ARMword address, ARMword isize);
+extern ARMword ARMul_LoadInstrN(ARMul_State* state, ARMword address, ARMword isize);
#ifdef __cplusplus
extern "C" {
#endif
-extern ARMword ARMul_ReLoadInstr (ARMul_State * state, ARMword address,
- ARMword isize);
+extern ARMword ARMul_ReLoadInstr(ARMul_State* state, ARMword address, ARMword isize);
#ifdef __cplusplus
}
#endif
-extern ARMword ARMul_LoadWordS (ARMul_State * state, ARMword address);
-extern ARMword ARMul_LoadWordN (ARMul_State * state, ARMword address);
-extern ARMword ARMul_LoadHalfWord (ARMul_State * state, ARMword address);
-extern ARMword ARMul_LoadByte (ARMul_State * state, ARMword address);
-
-extern void ARMul_StoreWordS (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_StoreWordN (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_StoreHalfWord (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_StoreByte (ARMul_State * state, ARMword address,
- ARMword data);
-
-extern ARMword ARMul_SwapWord (ARMul_State * state, ARMword address,
- ARMword data);
-extern ARMword ARMul_SwapByte (ARMul_State * state, ARMword address,
- ARMword data);
-
-extern void ARMul_Icycles (ARMul_State * state, unsigned number,
- ARMword address);
-extern void ARMul_Ccycles (ARMul_State * state, unsigned number,
- ARMword address);
-
-extern ARMword ARMul_ReadWord (ARMul_State * state, ARMword address);
-extern ARMword ARMul_ReadByte (ARMul_State * state, ARMword address);
-extern void ARMul_WriteWord (ARMul_State * state, ARMword address,
- ARMword data);
-extern void ARMul_WriteByte (ARMul_State * state, ARMword address,
- ARMword data);
-
-extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
+extern ARMword ARMul_LoadWordS(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadWordN(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadHalfWord(ARMul_State* state, ARMword address);
+extern ARMword ARMul_LoadByte(ARMul_State* state, ARMword address);
+
+extern void ARMul_StoreWordS(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreWordN(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreHalfWord(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_StoreByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern ARMword ARMul_SwapWord(ARMul_State* state, ARMword address, ARMword data);
+extern ARMword ARMul_SwapByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern void ARMul_Icycles(ARMul_State* state, unsigned number, ARMword address);
+extern void ARMul_Ccycles(ARMul_State* state, unsigned number, ARMword address);
+
+extern ARMword ARMul_ReadWord(ARMul_State* state, ARMword address);
+extern ARMword ARMul_ReadByte(ARMul_State* state, ARMword address);
+extern void ARMul_WriteWord(ARMul_State* state, ARMword address, ARMword data);
+extern void ARMul_WriteByte(ARMul_State* state, ARMword address, ARMword data);
+
+extern ARMword ARMul_MemAccess(ARMul_State* state, ARMword, ARMword,
ARMword, ARMword, ARMword, ARMword, ARMword,
ARMword, ARMword, ARMword);
@@ -739,82 +645,58 @@ extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
#define ARMul_CP15_DBCON_E1 0x000c
#define ARMul_CP15_DBCON_E0 0x0003
-extern unsigned ARMul_CoProInit (ARMul_State * state);
-extern void ARMul_CoProExit (ARMul_State * state);
-extern void ARMul_CoProAttach (ARMul_State * state, unsigned number,
- ARMul_CPInits * init, ARMul_CPExits * exit,
- ARMul_LDCs * ldc, ARMul_STCs * stc,
- ARMul_MRCs * mrc, ARMul_MCRs * mcr,
- ARMul_MRRCs * mrrc, ARMul_MCRRs * mcrr,
- ARMul_CDPs * cdp,
- ARMul_CPReads * read, ARMul_CPWrites * write);
-extern void ARMul_CoProDetach (ARMul_State * state, unsigned number);
+extern unsigned ARMul_CoProInit(ARMul_State* state);
+extern void ARMul_CoProExit(ARMul_State* state);
+extern void ARMul_CoProAttach (ARMul_State* state, unsigned number,
+ ARMul_CPInits* init, ARMul_CPExits* exit,
+ ARMul_LDCs* ldc, ARMul_STCs* stc,
+ ARMul_MRCs* mrc, ARMul_MCRs* mcr,
+ ARMul_MRRCs* mrrc, ARMul_MCRRs* mcrr,
+ ARMul_CDPs* cdp,
+ ARMul_CPReads* read, ARMul_CPWrites* write);
+extern void ARMul_CoProDetach(ARMul_State* state, unsigned number);
/***************************************************************************\
* Definitons of things in the host environment *
\***************************************************************************/
-extern unsigned ARMul_OSInit (ARMul_State * state);
-extern void ARMul_OSExit (ARMul_State * state);
+extern unsigned ARMul_OSInit(ARMul_State* state);
+extern void ARMul_OSExit(ARMul_State* state);
#ifdef __cplusplus
extern "C" {
#endif
-extern unsigned ARMul_OSHandleSWI (ARMul_State * state, ARMword number);
+extern unsigned ARMul_OSHandleSWI(ARMul_State* state, ARMword number);
#ifdef __cplusplus
}
#endif
-extern ARMword ARMul_OSLastErrorP (ARMul_State * state);
+extern ARMword ARMul_OSLastErrorP(ARMul_State* state);
-extern ARMword ARMul_Debug (ARMul_State * state, ARMword pc, ARMword instr);
-extern unsigned ARMul_OSException (ARMul_State * state, ARMword vector,
- ARMword pc);
+extern ARMword ARMul_Debug(ARMul_State* state, ARMword pc, ARMword instr);
+extern unsigned ARMul_OSException(ARMul_State* state, ARMword vector, ARMword pc);
extern int rdi_log;
-/***************************************************************************\
-* Host-dependent stuff *
-\***************************************************************************/
-
-#ifdef macintosh
-pascal void SpinCursor (short increment); /* copied from CursorCtl.h */
-# define HOURGLASS SpinCursor( 1 )
-# define HOURGLASS_RATE 1023 /* 2^n - 1 */
-#endif
-
-//teawater add for arm2x86 2005.02.14-------------------------------------------
-/*ywc 2005-03-31*/
-/*
-#include "arm2x86.h"
-#include "arm2x86_dp.h"
-#include "arm2x86_movl.h"
-#include "arm2x86_psr.h"
-#include "arm2x86_shift.h"
-#include "arm2x86_mem.h"
-#include "arm2x86_mul.h"
-#include "arm2x86_test.h"
-#include "arm2x86_other.h"
-#include "list.h"
-#include "tb.h"
-*/
-#define EQ 0
-#define NE 1
-#define CS 2
-#define CC 3
-#define MI 4
-#define PL 5
-#define VS 6
-#define VC 7
-#define HI 8
-#define LS 9
-#define GE 10
-#define LT 11
-#define GT 12
-#define LE 13
-#define AL 14
-#define NV 15
+enum ConditionCode {
+ EQ = 0,
+ NE = 1,
+ CS = 2,
+ CC = 3,
+ MI = 4,
+ PL = 5,
+ VS = 6,
+ VC = 7,
+ HI = 8,
+ LS = 9,
+ GE = 10,
+ LT = 11,
+ GT = 12,
+ LE = 13,
+ AL = 14,
+ NV = 15,
+};
#ifndef NFLAG
#define NFLAG state->NFlag
@@ -849,32 +731,16 @@ pascal void SpinCursor (short increment); /* copied from CursorCtl.h */
#define ZBIT_SHIFT 30
#define CBIT_SHIFT 29
#define VBIT_SHIFT 28
-#ifdef DBCT
-//teawater change for local tb branch directly jump 2005.10.18------------------
-#include "dbct/list.h"
-#include "dbct/arm2x86.h"
-#include "dbct/arm2x86_dp.h"
-#include "dbct/arm2x86_movl.h"
-#include "dbct/arm2x86_psr.h"
-#include "dbct/arm2x86_shift.h"
-#include "dbct/arm2x86_mem.h"
-#include "dbct/arm2x86_mul.h"
-#include "dbct/arm2x86_test.h"
-#include "dbct/arm2x86_other.h"
-#include "dbct/arm2x86_coproc.h"
-#include "dbct/tb.h"
-#endif
-//AJ2D--------------------------------------------------------------------------
-//AJ2D--------------------------------------------------------------------------
+
#define SKYEYE_OUTREGS(fd) { fprintf ((fd), "R %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,C %x,S %x,%x,%x,%x,%x,%x,%x,M %x,B %x,E %x,I %x,P %x,T %x,L %x,D %x,",\
state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \
state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \
state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \
state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \
- state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
+ state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\
- state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
- state->temp,state->loaded,state->decoded);}
+ state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
+ state->temp,state->loaded,state->decoded);}
#define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\
RUs %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
@@ -912,17 +778,17 @@ RUn %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n",\
#define SA1110 0x6901b110
#define SA1100 0x4401a100
-#define PXA250 0x69052100
-#define PXA270 0x69054110
-//#define PXA250 0x69052903
+#define PXA250 0x69052100
+#define PXA270 0x69054110
+//#define PXA250 0x69052903
// 0x69052903; //PXA250 B1 from intel 278522-001.pdf
-extern void ARMul_UndefInstr (ARMul_State *, ARMword);
-extern void ARMul_FixCPSR (ARMul_State *, ARMword, ARMword);
-extern void ARMul_FixSPSR (ARMul_State *, ARMword, ARMword);
-extern void ARMul_ConsolePrint (ARMul_State *, const char *, ...);
-extern void ARMul_SelectProcessor (ARMul_State *, unsigned);
+extern void ARMul_UndefInstr(ARMul_State*, ARMword);
+extern void ARMul_FixCPSR(ARMul_State*, ARMword, ARMword);
+extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword);
+extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...);
+extern void ARMul_SelectProcessor(ARMul_State*, unsigned);
#define DIFF_LOG 0
#define SAVE_LOG 0
diff --git a/src/core/arm/skyeye_common/armemu.h b/src/core/arm/skyeye_common/armemu.h
index c0f0270f..7f7c0e68 100644
--- a/src/core/arm/skyeye_common/armemu.h
+++ b/src/core/arm/skyeye_common/armemu.h
@@ -23,26 +23,6 @@
//extern ARMword isize;
-#define DEBUG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
-
-/* Condition code values. */
-#define EQ 0
-#define NE 1
-#define CS 2
-#define CC 3
-#define MI 4
-#define PL 5
-#define VS 6
-#define VC 7
-#define HI 8
-#define LS 9
-#define GE 10
-#define LT 11
-#define GT 12
-#define LE 13
-#define AL 14
-#define NV 15
-
/* Shift Opcodes. */
#define LSL 0
#define LSR 1
@@ -503,7 +483,7 @@ tdstate;
* out-of-updated with the newer ISA.
* -- Michael.Kang
********************************************************************************/
-#define UNDEF_WARNING WARN_LOG(ARM11, "undefined or unpredicted behavior for arm instruction.\n");
+#define UNDEF_WARNING LOG_WARNING(Core_ARM11, "undefined or unpredicted behavior for arm instruction.");
/* Macros to scrutinize instructions. */
#define UNDEF_Test UNDEF_WARNING
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
index 07d0c1f4..6c33d8b7 100644
--- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
@@ -522,8 +522,7 @@ static s64 vfp_single_to_doubleintern(ARMul_State* state, s32 m, u32 fpscr) //ic
if (tm == VFP_QNAN)
vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
goto pack_nan;
- }
- else if (tm & VFP_ZERO)
+ } else if (tm & VFP_ZERO)
vdd.exponent = 0;
else
vdd.exponent = vsm.exponent + (1023 - 127);
@@ -615,12 +614,12 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
exceptions |= FPSCR_IDC;
if (tm & VFP_NAN)
- vsm.sign = 0;
+ vsm.sign = 1;
if (vsm.exponent >= 127 + 32) {
d = vsm.sign ? 0 : 0xffffffff;
exceptions = FPSCR_IOC;
- } else if (vsm.exponent >= 127 - 1) {
+ } else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0;
@@ -705,7 +704,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
if (vsm.sign)
d = ~d;
exceptions |= FPSCR_IOC;
- } else if (vsm.exponent >= 127 - 1) {
+ } else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0;
@@ -1149,7 +1148,10 @@ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
/*
* Subtraction is addition with one sign inverted.
*/
- return vfp_single_fadd(state, sd, sn, vfp_single_packed_negate(m), fpscr);
+ if (m != 0x7FC00000) // Only negate if m isn't NaN.
+ m = vfp_single_packed_negate(m);
+
+ return vfp_single_fadd(state, sd, sn, m, fpscr);
}
/*
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 25c78d33..64de0cbb 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -16,10 +16,10 @@
namespace Core {
-u64 g_last_ticks = 0; ///< Last CPU ticks
-ARM_Disasm* g_disasm = nullptr; ///< ARM disassembler
-ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
-ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
+static u64 last_ticks = 0; ///< Last CPU ticks
+static ARM_Disasm* disasm = nullptr; ///< ARM disassembler
+ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
+ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop(int tight_loop) {
@@ -47,9 +47,9 @@ void Stop() {
/// Initialize the core
int Init() {
- NOTICE_LOG(MASTER_LOG, "initialized OK");
+ LOG_DEBUG(Core, "initialized OK");
- g_disasm = new ARM_Disasm();
+ disasm = new ARM_Disasm();
g_sys_core = new ARM_Interpreter();
switch (Settings::values.cpu_core) {
@@ -62,17 +62,17 @@ int Init() {
break;
}
- g_last_ticks = Core::g_app_core->GetTicks();
+ last_ticks = Core::g_app_core->GetTicks();
return 0;
}
void Shutdown() {
- delete g_disasm;
+ delete disasm;
delete g_app_core;
delete g_sys_core;
- NOTICE_LOG(MASTER_LOG, "shutdown OK");
+ LOG_DEBUG(Core, "shutdown OK");
}
} // namespace
diff --git a/src/core/core.h b/src/core/core.h
index 872dc0cd..850bb0ab 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -26,13 +26,13 @@ void Start();
/**
* Run the core CPU loop
- * This function loops for 100 instructions in the CPU before trying to update hardware. This is a
- * little bit faster than SingleStep, and should be pretty much equivalent. The number of
- * instructions chosen is fairly arbitrary, however a large number will more drastically affect the
- * frequency of GSP interrupts and likely break things. The point of this is to just loop in the CPU
- * for more than 1 instruction to reduce overhead and make it a little bit faster...
+ * This function runs the core for the specified number of CPU instructions before trying to update
+ * hardware. This is much faster than SingleStep (and should be equivalent), as the CPU is not
+ * required to do a full dispatch with each instruction. NOTE: the number of instructions requested
+ * is not guaranteed to run, as this will be interrupted preemptively if a hardware update is
+ * requested (e.g. on a thread switch).
*/
-void RunLoop(int tight_loop=100);
+void RunLoop(int tight_loop=1000);
/// Step the CPU one instruction
void SingleStep();
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 0116cb37..1a0b2724 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -41,7 +41,7 @@ struct BaseEvent
s64 time;
u64 userdata;
int type;
- // Event *next;
+ // Event *next;
};
typedef LinkedListItem<BaseEvent> Event;
@@ -67,7 +67,7 @@ s64 idledCycles;
static std::recursive_mutex externalEventSection;
// Warning: not included in save state.
-void(*advanceCallback)(int cyclesExecuted) = NULL;
+void(*advanceCallback)(int cyclesExecuted) = nullptr;
void SetClockFrequencyMHz(int cpuMhz)
{
@@ -124,7 +124,7 @@ int RegisterEvent(const char *name, TimedCallback callback)
void AntiCrashCallback(u64 userdata, int cyclesLate)
{
- ERROR_LOG(TIME, "Savestate broken: an unregistered event was called.");
+ LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called.");
Core::Halt("invalid timing events");
}
@@ -176,7 +176,7 @@ void Shutdown()
u64 GetTicks()
{
- ERROR_LOG(TIME, "Unimplemented function!");
+ LOG_ERROR(Core, "Unimplemented function!");
return 0;
//return (u64)globalTimer + slicelength - currentMIPS->downcount;
}
@@ -231,7 +231,7 @@ void ClearPendingEvents()
void AddEventToQueue(Event* ne)
{
- Event* prev = NULL;
+ Event* prev = nullptr;
Event** pNext = &first;
for (;;)
{
@@ -249,7 +249,7 @@ void AddEventToQueue(Event* ne)
// This must be run ONLY from within the cpu thread
// cyclesIntoFuture may be VERY inaccurate if called from anything else
-// than Advance
+// than Advance
void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
{
Event *ne = GetNewEvent();
@@ -327,7 +327,7 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
}
if (!tsFirst)
{
- tsLast = NULL;
+ tsLast = nullptr;
return result;
}
@@ -433,7 +433,7 @@ void RemoveThreadsafeEvent(int event_type)
}
if (!tsFirst)
{
- tsLast = NULL;
+ tsLast = nullptr;
return;
}
Event *prev = tsFirst;
@@ -469,8 +469,8 @@ void ProcessFifoWaitEvents()
{
if (first->time <= globalTimer)
{
- // LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
- // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
+ //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
+ // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
Event* evt = first;
first = first->next;
event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
@@ -495,7 +495,7 @@ void MoveEvents()
AddEventToQueue(tsFirst);
tsFirst = next;
}
- tsLast = NULL;
+ tsLast = nullptr;
// Move free events to threadsafe pool
while (allocatedTsEvents > 0 && eventPool)
@@ -510,29 +510,29 @@ void MoveEvents()
void Advance()
{
- ERROR_LOG(TIME, "Unimplemented function!");
+ LOG_ERROR(Core, "Unimplemented function!");
//int cyclesExecuted = slicelength - currentMIPS->downcount;
//globalTimer += cyclesExecuted;
//currentMIPS->downcount = slicelength;
//if (Common::AtomicLoadAcquire(hasTsEvents))
- // MoveEvents();
+ // MoveEvents();
//ProcessFifoWaitEvents();
//if (!first)
//{
- // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
- // currentMIPS->downcount += 10000;
+ // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
+ // currentMIPS->downcount += 10000;
//}
//else
//{
- // slicelength = (int)(first->time - globalTimer);
- // if (slicelength > MAX_SLICE_LENGTH)
- // slicelength = MAX_SLICE_LENGTH;
- // currentMIPS->downcount = slicelength;
+ // slicelength = (int)(first->time - globalTimer);
+ // if (slicelength > MAX_SLICE_LENGTH)
+ // slicelength = MAX_SLICE_LENGTH;
+ // currentMIPS->downcount = slicelength;
//}
//if (advanceCallback)
- // advanceCallback(cyclesExecuted);
+ // advanceCallback(cyclesExecuted);
}
void LogPendingEvents()
@@ -547,23 +547,23 @@ void LogPendingEvents()
void Idle(int maxIdle)
{
- ERROR_LOG(TIME, "Unimplemented function!");
+ LOG_ERROR(Core, "Unimplemented function!");
//int cyclesDown = currentMIPS->downcount;
//if (maxIdle != 0 && cyclesDown > maxIdle)
- // cyclesDown = maxIdle;
+ // cyclesDown = maxIdle;
//if (first && cyclesDown > 0)
//{
- // int cyclesExecuted = slicelength - currentMIPS->downcount;
- // int cyclesNextEvent = (int) (first->time - globalTimer);
-
- // if (cyclesNextEvent < cyclesExecuted + cyclesDown)
- // {
- // cyclesDown = cyclesNextEvent - cyclesExecuted;
- // // Now, now... no time machines, please.
- // if (cyclesDown < 0)
- // cyclesDown = 0;
- // }
+ // int cyclesExecuted = slicelength - currentMIPS->downcount;
+ // int cyclesNextEvent = (int) (first->time - globalTimer);
+
+ // if (cyclesNextEvent < cyclesExecuted + cyclesDown)
+ // {
+ // cyclesDown = cyclesNextEvent - cyclesExecuted;
+ // // Now, now... no time machines, please.
+ // if (cyclesDown < 0)
+ // cyclesDown = 0;
+ // }
//}
//INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f));
@@ -571,7 +571,7 @@ void Idle(int maxIdle)
//idledCycles += cyclesDown;
//currentMIPS->downcount -= cyclesDown;
//if (currentMIPS->downcount == 0)
- // currentMIPS->downcount = -1;
+ // currentMIPS->downcount = -1;
}
std::string GetScheduledEventsSummary()
@@ -614,7 +614,7 @@ void DoState(PointerWrap &p)
// These (should) be filled in later by the modules.
event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
- p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)NULL);
+ p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr);
p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
p.Do(g_clock_rate_arm11);
@@ -623,4 +623,4 @@ void DoState(PointerWrap &p)
p.Do(idledCycles);
}
-} // namespace
+} // namespace
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 09fdf7a9..b197cf40 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -106,4 +106,4 @@ void SetClockFrequencyMHz(int cpuMhz);
int GetClockFrequencyMHz();
extern int slicelength;
-}; // namespace
+} // namespace
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive.h
deleted file mode 100644
index aeabf09a..00000000
--- a/src/core/file_sys/archive.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-
-#include "common/common_types.h"
-#include "common/bit_field.h"
-
-#include "core/file_sys/file.h"
-#include "core/file_sys/directory.h"
-
-#include "core/hle/kernel/kernel.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-union Mode {
- u32 hex;
- BitField<0, 1, u32> read_flag;
- BitField<1, 1, u32> write_flag;
- BitField<2, 1, u32> create_flag;
-};
-
-class Archive : NonCopyable {
-public:
- /// Supported archive types
- enum class IdCode : u32 {
- RomFS = 0x00000003,
- SaveData = 0x00000004,
- ExtSaveData = 0x00000006,
- SharedExtSaveData = 0x00000007,
- SystemSaveData = 0x00000008,
- SDMC = 0x00000009,
- SDMCWriteOnly = 0x0000000A,
- };
-
- Archive() { }
- virtual ~Archive() { }
-
- /**
- * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
- * @return IdCode of the archive
- */
- virtual IdCode GetIdCode() const = 0;
-
- /**
- * Open a file specified by its path, using the specified mode
- * @param path Path relative to the archive
- * @param mode Mode to open the file with
- * @return Opened file, or nullptr
- */
- virtual std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const = 0;
-
- /**
- * Create a directory specified by its path
- * @param path Path relative to the archive
- * @return Whether the directory could be created
- */
- virtual bool CreateDirectory(const std::string& path) const = 0;
-
- /**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
- virtual std::unique_ptr<Directory> OpenDirectory(const std::string& path) const = 0;
-
- /**
- * Read data from the archive
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
- virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0;
-
- /**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
- */
- virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0;
-
- /**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
- virtual size_t GetSize() const = 0;
-
- /**
- * Set the size of the archive in bytes
- */
- virtual void SetSize(const u64 size) = 0;
-};
-
-} // namespace FileSys
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
new file mode 100644
index 00000000..18c31488
--- /dev/null
+++ b/src/core/file_sys/archive_backend.h
@@ -0,0 +1,225 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+#include "common/string_util.h"
+#include "common/bit_field.h"
+
+#include "core/file_sys/file_backend.h"
+#include "core/file_sys/directory_backend.h"
+
+#include "core/mem_map.h"
+#include "core/hle/kernel/kernel.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+// Path string type
+enum LowPathType : u32 {
+ Invalid = 0,
+ Empty = 1,
+ Binary = 2,
+ Char = 3,
+ Wchar = 4
+};
+
+union Mode {
+ u32 hex;
+ BitField<0, 1, u32> read_flag;
+ BitField<1, 1, u32> write_flag;
+ BitField<2, 1, u32> create_flag;
+};
+
+class Path {
+public:
+
+ Path():
+ type(Invalid)
+ {
+ }
+
+ Path(LowPathType type, u32 size, u32 pointer):
+ type(type)
+ {
+ switch (type) {
+ case Binary:
+ {
+ u8* data = Memory::GetPointer(pointer);
+ binary = std::vector<u8>(data, data + size);
+ break;
+ }
+ case Char:
+ {
+ const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
+ string = std::string(data, size - 1); // Data is always null-terminated.
+ break;
+ }
+ case Wchar:
+ {
+ const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
+ u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ LowPathType GetType() const {
+ return type;
+ }
+
+ /**
+ * Gets the string representation of the path for debugging
+ * @return String representation of the path for debugging
+ */
+ const std::string DebugStr() const {
+ switch (GetType()) {
+ case Invalid:
+ return "[Invalid]";
+ case Empty:
+ return "[Empty]";
+ case Binary:
+ {
+ std::stringstream res;
+ res << "[Binary: ";
+ for (unsigned byte : binary)
+ res << std::hex << std::setw(2) << std::setfill('0') << byte;
+ res << ']';
+ return res.str();
+ }
+ case Char:
+ return "[Char: " + AsString() + ']';
+ case Wchar:
+ return "[Wchar: " + AsString() + ']';
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
+ return {};
+ }
+ }
+
+ const std::string AsString() const {
+ switch (GetType()) {
+ case Char:
+ return string;
+ case Wchar:
+ return Common::UTF16ToUTF8(u16str);
+ case Empty:
+ return {};
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
+ return {};
+ }
+ }
+
+ const std::u16string AsU16Str() const {
+ switch (GetType()) {
+ case Char:
+ return Common::UTF8ToUTF16(string);
+ case Wchar:
+ return u16str;
+ case Empty:
+ return {};
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
+ return {};
+ }
+ }
+
+ const std::vector<u8> AsBinary() const {
+ switch (GetType()) {
+ case Binary:
+ return binary;
+ case Char:
+ return std::vector<u8>(string.begin(), string.end());
+ case Wchar:
+ return std::vector<u8>(u16str.begin(), u16str.end());
+ case Empty:
+ return {};
+ default:
+ // TODO(yuriks): Add assert
+ LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!");
+ return {};
+ }
+ }
+
+private:
+ LowPathType type;
+ std::vector<u8> binary;
+ std::string string;
+ std::u16string u16str;
+};
+
+class ArchiveBackend : NonCopyable {
+public:
+ virtual ~ArchiveBackend() { }
+
+ /**
+ * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
+ */
+ virtual std::string GetName() const = 0;
+
+ /**
+ * Open a file specified by its path, using the specified mode
+ * @param path Path relative to the archive
+ * @param mode Mode to open the file with
+ * @return Opened file, or nullptr
+ */
+ virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0;
+
+ /**
+ * Delete a file specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the file could be deleted
+ */
+ virtual bool DeleteFile(const FileSys::Path& path) const = 0;
+
+ /**
+ * Rename a File specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
+ */
+ virtual bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0;
+
+ /**
+ * Delete a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be deleted
+ */
+ virtual bool DeleteDirectory(const FileSys::Path& path) const = 0;
+
+ /**
+ * Create a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be created
+ */
+ virtual bool CreateDirectory(const Path& path) const = 0;
+
+ /**
+ * Rename a Directory specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
+ */
+ virtual bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0;
+
+ /**
+ * Open a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Opened directory, or nullptr
+ */
+ virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
index cc759faa..0709b62a 100644
--- a/src/core/file_sys/archive_romfs.cpp
+++ b/src/core/file_sys/archive_romfs.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
+#include <memory>
+
#include "common/common_types.h"
#include "core/file_sys/archive_romfs.h"
@@ -16,81 +18,67 @@ namespace FileSys {
Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) {
- WARN_LOG(FILESYS, "Unable to read RomFS!");
+ LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
-Archive_RomFS::~Archive_RomFS() {
-}
-
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
-std::unique_ptr<File> Archive_RomFS::OpenFile(const std::string& path, const Mode mode) const {
- return std::unique_ptr<File>(new File_RomFS);
+std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const {
+ return std::make_unique<File_RomFS>(this);
}
/**
- * Create a directory specified by its path
+ * Delete a file specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be created
+ * @return Whether the file could be deleted
*/
-bool Archive_RomFS::CreateDirectory(const std::string& path) const {
- ERROR_LOG(FILESYS, "Attempted to create a directory in ROMFS.");
+bool Archive_RomFS::DeleteFile(const FileSys::Path& path) const {
+ LOG_WARNING(Service_FS, "Attempted to delete a file from ROMFS.");
return false;
-};
+}
-/**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
-std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const std::string& path) const {
- return std::unique_ptr<Directory>(new Directory_RomFS);
+bool Archive_RomFS::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
+ return false;
}
/**
- * Read data from the archive
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
+ * Delete a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be deleted
*/
-size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
- DEBUG_LOG(FILESYS, "called offset=%llu, length=%d", offset, length);
- memcpy(buffer, &raw_data[(u32)offset], length);
- return length;
+bool Archive_RomFS::DeleteDirectory(const FileSys::Path& path) const {
+ LOG_WARNING(Service_FS, "Attempted to delete a directory from ROMFS.");
+ return false;
}
/**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
+ * Create a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be created
*/
-size_t Archive_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
- ERROR_LOG(FILESYS, "Attempted to write to ROMFS.");
- return 0;
+bool Archive_RomFS::CreateDirectory(const Path& path) const {
+ LOG_WARNING(Service_FS, "Attempted to create a directory in ROMFS.");
+ return false;
}
-/**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
-size_t Archive_RomFS::GetSize() const {
- return sizeof(u8) * raw_data.size();
+bool Archive_RomFS::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
+ return false;
}
/**
- * Set the size of the archive in bytes
+ * Open a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Opened directory, or nullptr
*/
-void Archive_RomFS::SetSize(const u64 size) {
- ERROR_LOG(FILESYS, "Attempted to set the size of ROMFS");
+std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const {
+ return std::make_unique<Directory_RomFS>();
}
} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
index ae2344e8..5b1ee633 100644
--- a/src/core/file_sys/archive_romfs.h
+++ b/src/core/file_sys/archive_romfs.h
@@ -8,7 +8,7 @@
#include "common/common_types.h"
-#include "core/file_sys/archive.h"
+#include "core/file_sys/archive_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -17,16 +17,11 @@
namespace FileSys {
/// File system interface to the RomFS archive
-class Archive_RomFS final : public Archive {
+class Archive_RomFS final : public ArchiveBackend {
public:
Archive_RomFS(const Loader::AppLoader& app_loader);
- ~Archive_RomFS() override;
- /**
- * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
- * @return IdCode of the archive
- */
- IdCode GetIdCode() const override { return IdCode::RomFS; };
+ std::string GetName() const override { return "RomFS"; }
/**
* Open a file specified by its path, using the specified mode
@@ -34,53 +29,55 @@ public:
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
- std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override;
+ std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
/**
- * Create a directory specified by its path
+ * Delete a file specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be created
+ * @return Whether the file could be deleted
*/
- bool CreateDirectory(const std::string& path) const override;
+ bool DeleteFile(const FileSys::Path& path) const override;
/**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
+ * Rename a File specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
*/
- std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override;
+ bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
/**
- * Read data from the archive
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
+ * Delete a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be deleted
*/
- size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
+ bool DeleteDirectory(const FileSys::Path& path) const override;
/**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
+ * Create a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Whether the directory could be created
*/
- size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
+ bool CreateDirectory(const Path& path) const override;
/**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
+ * Rename a Directory specified by its path
+ * @param src_path Source path relative to the archive
+ * @param dest_path Destination path relative to the archive
+ * @return Whether rename succeeded
*/
- size_t GetSize() const override;
-
+ bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
+
/**
- * Set the size of the archive in bytes
+ * Open a directory specified by its path
+ * @param path Path relative to the archive
+ * @return Opened directory, or nullptr
*/
- void SetSize(const u64 size) override;
+ std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
private:
+ friend class File_RomFS;
+
std::vector<u8> raw_data;
};
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
new file mode 100644
index 00000000..2414564e
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -0,0 +1,33 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <sys/stat.h>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+
+#include "core/file_sys/archive_savedata.h"
+#include "core/file_sys/disk_archive.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id)
+ : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) {
+ LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
+}
+
+bool Archive_SaveData::Initialize() {
+ if (!FileUtil::CreateFullPath(mount_point)) {
+ LOG_ERROR(Service_FS, "Unable to create SaveData path.");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h
new file mode 100644
index 00000000..b3e56113
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/disk_archive.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// File system interface to the SaveData archive
+class Archive_SaveData final : public DiskArchive {
+public:
+ Archive_SaveData(const std::string& mount_point, u64 program_id);
+
+ /**
+ * Initialize the archive.
+ * @return CreateSaveDataResult AlreadyExists if the SaveData folder already exists,
+ * Success if it was created properly and Failure if there was any error
+ */
+ bool Initialize();
+
+ std::string GetName() const override { return "SaveData"; }
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 66931e93..dccdf7f6 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -8,8 +8,7 @@
#include "common/file_util.h"
#include "core/file_sys/archive_sdmc.h"
-#include "core/file_sys/directory_sdmc.h"
-#include "core/file_sys/file_sdmc.h"
+#include "core/file_sys/disk_archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -17,113 +16,22 @@
namespace FileSys {
-Archive_SDMC::Archive_SDMC(const std::string& mount_point) {
- this->mount_point = mount_point;
- DEBUG_LOG(FILESYS, "Directory %s set as SDMC.", mount_point.c_str());
+Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) {
+ LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str());
}
-Archive_SDMC::~Archive_SDMC() {
-}
-
-/**
- * Initialize the archive.
- * @return true if it initialized successfully
- */
bool Archive_SDMC::Initialize() {
if (!Settings::values.use_virtual_sd) {
- WARN_LOG(FILESYS, "SDMC disabled by config.");
+ LOG_WARNING(Service_FS, "SDMC disabled by config.");
return false;
}
if (!FileUtil::CreateFullPath(mount_point)) {
- WARN_LOG(FILESYS, "Unable to create SDMC path.");
+ LOG_ERROR(Service_FS, "Unable to create SDMC path.");
return false;
}
return true;
}
-/**
- * Open a file specified by its path, using the specified mode
- * @param path Path relative to the archive
- * @param mode Mode to open the file with
- * @return Opened file, or nullptr
- */
-std::unique_ptr<File> Archive_SDMC::OpenFile(const std::string& path, const Mode mode) const {
- DEBUG_LOG(FILESYS, "called path=%s mode=%d", path.c_str(), mode);
- File_SDMC* file = new File_SDMC(this, path, mode);
- if (!file->Open())
- return nullptr;
- return std::unique_ptr<File>(file);
-}
-
-/**
- * Create a directory specified by its path
- * @param path Path relative to the archive
- * @return Whether the directory could be created
- */
-bool Archive_SDMC::CreateDirectory(const std::string& path) const {
- return FileUtil::CreateDir(GetMountPoint() + path);
-}
-
-/**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
-std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const std::string& path) const {
- DEBUG_LOG(FILESYS, "called path=%s", path.c_str());
- Directory_SDMC* directory = new Directory_SDMC(this, path);
- return std::unique_ptr<Directory>(directory);
-}
-
-/**
- * Read data from the archive
- * @param offset Offset in bytes to start reading archive from
- * @param length Length in bytes to read data from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
-size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
- return -1;
-}
-
-/**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
- */
-size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
- return -1;
-}
-
-/**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
-size_t Archive_SDMC::GetSize() const {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
- return 0;
-}
-
-/**
- * Set the size of the archive in bytes
- */
-void Archive_SDMC::SetSize(const u64 size) {
- ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
-}
-
-/**
- * Getter for the path used for this Archive
- * @return Mount point of that passthrough archive
- */
-std::string Archive_SDMC::GetMountPoint() const {
- return mount_point;
-}
-
} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 0e059b63..c84c6948 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -6,7 +6,7 @@
#include "common/common_types.h"
-#include "core/file_sys/archive.h"
+#include "core/file_sys/disk_archive.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,10 +15,9 @@
namespace FileSys {
/// File system interface to the SDMC archive
-class Archive_SDMC final : public Archive {
+class Archive_SDMC final : public DiskArchive {
public:
Archive_SDMC(const std::string& mount_point);
- ~Archive_SDMC() override;
/**
* Initialize the archive.
@@ -26,72 +25,7 @@ public:
*/
bool Initialize();
- /**
- * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
- * @return IdCode of the archive
- */
- IdCode GetIdCode() const override { return IdCode::SDMC; };
-
- /**
- * Open a file specified by its path, using the specified mode
- * @param path Path relative to the archive
- * @param mode Mode to open the file with
- * @return Opened file, or nullptr
- */
- std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override;
-
- /**
- * Create a directory specified by its path
- * @param path Path relative to the archive
- * @return Whether the directory could be created
- */
- bool CreateDirectory(const std::string& path) const override;
-
- /**
- * Open a directory specified by its path
- * @param path Path relative to the archive
- * @return Opened directory, or nullptr
- */
- std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override;
-
- /**
- * Read data from the archive
- * @param offset Offset in bytes to start reading archive from
- * @param length Length in bytes to read data from archive
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
- size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
-
- /**
- * Write data to the archive
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to archive
- * @param buffer Buffer to write data from
- * @param flush The flush parameters (0 == do not flush)
- * @return Number of bytes written
- */
- size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
-
- /**
- * Get the size of the archive in bytes
- * @return Size of the archive in bytes
- */
- size_t GetSize() const override;
-
- /**
- * Set the size of the archive in bytes
- */
- void SetSize(const u64 size) override;
-
- /**
- * Getter for the path used for this Archive
- * @return Mount point of that passthrough archive
- */
- std::string GetMountPoint() const;
-
-private:
- std::string mount_point;
+ std::string GetName() const override { return "SDMC"; }
};
} // namespace FileSys
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory_backend.h
index e1043133..188746a6 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory_backend.h
@@ -36,10 +36,16 @@ static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension i
static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry.");
static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry.");
-class Directory : NonCopyable {
+class DirectoryBackend : NonCopyable {
public:
- Directory() { }
- virtual ~Directory() { }
+ DirectoryBackend() { }
+ virtual ~DirectoryBackend() { }
+
+ /**
+ * Open the directory
+ * @return true if the directory opened correctly
+ */
+ virtual bool Open() = 0;
/**
* List files contained in the directory
diff --git a/src/core/file_sys/directory_romfs.cpp b/src/core/file_sys/directory_romfs.cpp
index 4e8f4c04..e6d57139 100644
--- a/src/core/file_sys/directory_romfs.cpp
+++ b/src/core/file_sys/directory_romfs.cpp
@@ -17,6 +17,10 @@ Directory_RomFS::Directory_RomFS() {
Directory_RomFS::~Directory_RomFS() {
}
+bool Directory_RomFS::Open() {
+ return false;
+}
+
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries
diff --git a/src/core/file_sys/directory_romfs.h b/src/core/file_sys/directory_romfs.h
index 4b71c4b1..b775f014 100644
--- a/src/core/file_sys/directory_romfs.h
+++ b/src/core/file_sys/directory_romfs.h
@@ -6,7 +6,7 @@
#include "common/common_types.h"
-#include "core/file_sys/directory.h"
+#include "core/file_sys/directory_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -14,12 +14,18 @@
namespace FileSys {
-class Directory_RomFS final : public Directory {
+class Directory_RomFS final : public DirectoryBackend {
public:
Directory_RomFS();
~Directory_RomFS() override;
/**
+ * Open the directory
+ * @return true if the directory opened correctly
+ */
+ bool Open() override;
+
+ /**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp
deleted file mode 100644
index fd558def..00000000
--- a/src/core/file_sys/directory_sdmc.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include <sys/stat.h>
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/directory_sdmc.h"
-#include "core/file_sys/archive_sdmc.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const std::string& path) {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../usr/bin can give the emulated program your installed programs.
- std::string absolute_path = archive->GetMountPoint() + path;
- FileUtil::ScanDirectoryTree(absolute_path, directory);
- children_iterator = directory.children.begin();
-}
-
-Directory_SDMC::~Directory_SDMC() {
- Close();
-}
-
-/**
- * List files contained in the directory
- * @param count Number of entries to return at once in entries
- * @param entries Buffer to read data into
- * @return Number of entries listed
- */
-u32 Directory_SDMC::Read(const u32 count, Entry* entries) {
- u32 entries_read = 0;
-
- while (entries_read < count && children_iterator != directory.children.cend()) {
- const FileUtil::FSTEntry& file = *children_iterator;
- const std::string& filename = file.virtualName;
- Entry& entry = entries[entries_read];
-
- WARN_LOG(FILESYS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
-
- // TODO(Link Mauve): use a proper conversion to UTF-16.
- for (int j = 0; j < FILENAME_LENGTH; ++j) {
- entry.filename[j] = filename[j];
- if (!filename[j])
- break;
- }
-
- FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
-
- entry.is_directory = file.isDirectory;
- entry.is_hidden = (filename[0] == '.');
- entry.is_read_only = 0;
- entry.file_size = file.size;
-
- // We emulate a SD card where the archive bit has never been cleared, as it would be on
- // most user SD cards.
- // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
- // file bit.
- entry.is_archive = !file.isDirectory;
-
- ++entries_read;
- ++children_iterator;
- }
- return entries_read;
-}
-
-/**
- * Close the directory
- * @return true if the directory closed correctly
- */
-bool Directory_SDMC::Close() const {
- return true;
-}
-
-} // namespace FileSys
diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h
deleted file mode 100644
index cb8d32fd..00000000
--- a/src/core/file_sys/directory_sdmc.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/directory.h"
-#include "core/file_sys/archive_sdmc.h"
-#include "core/loader/loader.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-class Directory_SDMC final : public Directory {
-public:
- Directory_SDMC();
- Directory_SDMC(const Archive_SDMC* archive, const std::string& path);
- ~Directory_SDMC() override;
-
- /**
- * List files contained in the directory
- * @param count Number of entries to return at once in entries
- * @param entries Buffer to read data into
- * @return Number of entries listed
- */
- u32 Read(const u32 count, Entry* entries) override;
-
- /**
- * Close the directory
- * @return true if the directory closed correctly
- */
- bool Close() const override;
-
-private:
- u32 total_entries_in_directory;
- FileUtil::FSTEntry directory;
-
- // We need to remember the last entry we returned, so a subsequent call to Read will continue
- // from the next one. This iterator will always point to the next unread entry.
- std::vector<FileUtil::FSTEntry>::iterator children_iterator;
-};
-
-} // namespace FileSys
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
new file mode 100644
index 00000000..eabf5805
--- /dev/null
+++ b/src/core/file_sys/disk_archive.cpp
@@ -0,0 +1,167 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <sys/stat.h>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+
+#include "core/file_sys/disk_archive.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
+ LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
+ DiskFile* file = new DiskFile(this, path, mode);
+ if (!file->Open())
+ return nullptr;
+ return std::unique_ptr<FileBackend>(file);
+}
+
+bool DiskArchive::DeleteFile(const FileSys::Path& path) const {
+ return FileUtil::Delete(GetMountPoint() + path.AsString());
+}
+
+bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
+}
+
+bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const {
+ return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
+}
+
+bool DiskArchive::CreateDirectory(const Path& path) const {
+ return FileUtil::CreateDir(GetMountPoint() + path.AsString());
+}
+
+bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
+ return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
+}
+
+std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
+ LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
+ DiskDirectory* directory = new DiskDirectory(this, path);
+ if (!directory->Open())
+ return nullptr;
+ return std::unique_ptr<DirectoryBackend>(directory);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) {
+ // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
+ // the root directory we set while opening the archive.
+ // For example, opening /../../etc/passwd can give the emulated program your users list.
+ this->path = archive->GetMountPoint() + path.AsString();
+ this->mode.hex = mode.hex;
+ this->archive = archive;
+}
+
+bool DiskFile::Open() {
+ if (!mode.create_flag && !FileUtil::Exists(path)) {
+ LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str());
+ return false;
+ }
+
+ std::string mode_string;
+ if (mode.create_flag)
+ mode_string = "w+";
+ else if (mode.write_flag)
+ mode_string = "r+"; // Files opened with Write access can be read from
+ else if (mode.read_flag)
+ mode_string = "r";
+
+ // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
+ mode_string += "b";
+
+ file = new FileUtil::IOFile(path, mode_string.c_str());
+ return true;
+}
+
+size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const {
+ file->Seek(offset, SEEK_SET);
+ return file->ReadBytes(buffer, length);
+}
+
+size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
+ file->Seek(offset, SEEK_SET);
+ size_t written = file->WriteBytes(buffer, length);
+ if (flush)
+ file->Flush();
+ return written;
+}
+
+size_t DiskFile::GetSize() const {
+ return static_cast<size_t>(file->GetSize());
+}
+
+bool DiskFile::SetSize(const u64 size) const {
+ file->Resize(size);
+ file->Flush();
+ return true;
+}
+
+bool DiskFile::Close() const {
+ return file->Close();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) {
+ // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
+ // the root directory we set while opening the archive.
+ // For example, opening /../../usr/bin can give the emulated program your installed programs.
+ this->path = archive->GetMountPoint() + path.AsString();
+ this->archive = archive;
+}
+
+bool DiskDirectory::Open() {
+ if (!FileUtil::IsDirectory(path))
+ return false;
+ FileUtil::ScanDirectoryTree(path, directory);
+ children_iterator = directory.children.begin();
+ return true;
+}
+
+u32 DiskDirectory::Read(const u32 count, Entry* entries) {
+ u32 entries_read = 0;
+
+ while (entries_read < count && children_iterator != directory.children.cend()) {
+ const FileUtil::FSTEntry& file = *children_iterator;
+ const std::string& filename = file.virtualName;
+ Entry& entry = entries[entries_read];
+
+ LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
+
+ // TODO(Link Mauve): use a proper conversion to UTF-16.
+ for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
+ entry.filename[j] = filename[j];
+ if (!filename[j])
+ break;
+ }
+
+ FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
+
+ entry.is_directory = file.isDirectory;
+ entry.is_hidden = (filename[0] == '.');
+ entry.is_read_only = 0;
+ entry.file_size = file.size;
+
+ // We emulate a SD card where the archive bit has never been cleared, as it would be on
+ // most user SD cards.
+ // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
+ // file bit.
+ entry.is_archive = !file.isDirectory;
+
+ ++entries_read;
+ ++children_iterator;
+ }
+ return entries_read;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
new file mode 100644
index 00000000..778c8395
--- /dev/null
+++ b/src/core/file_sys/disk_archive.h
@@ -0,0 +1,101 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive_backend.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/**
+ * Helper which implements a backend accessing the host machine's filesystem.
+ * This should be subclassed by concrete archive types, which will provide the
+ * base directory on the host filesystem and override any required functionality.
+ */
+class DiskArchive : public ArchiveBackend {
+public:
+ DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
+
+ virtual std::string GetName() const = 0;
+ std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
+ bool DeleteFile(const FileSys::Path& path) const override;
+ bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
+ bool DeleteDirectory(const FileSys::Path& path) const override;
+ bool CreateDirectory(const Path& path) const override;
+ bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
+ std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
+
+ /**
+ * Getter for the path used for this Archive
+ * @return Mount point of that passthrough archive
+ */
+ const std::string& GetMountPoint() const {
+ return mount_point;
+ }
+
+protected:
+ std::string mount_point;
+};
+
+class DiskFile : public FileBackend {
+public:
+ DiskFile();
+ DiskFile(const DiskArchive* archive, const Path& path, const Mode mode);
+
+ ~DiskFile() override {
+ Close();
+ }
+
+ bool Open() override;
+ size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
+ size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
+ size_t GetSize() const override;
+ bool SetSize(const u64 size) const override;
+ bool Close() const override;
+
+ void Flush() const override {
+ file->Flush();
+ }
+
+protected:
+ const DiskArchive* archive;
+ std::string path;
+ Mode mode;
+ FileUtil::IOFile* file;
+};
+
+class DiskDirectory : public DirectoryBackend {
+public:
+ DiskDirectory();
+ DiskDirectory(const DiskArchive* archive, const Path& path);
+
+ ~DiskDirectory() override {
+ Close();
+ }
+
+ bool Open() override;
+ u32 Read(const u32 count, Entry* entries) override;
+
+ bool Close() const override {
+ return true;
+ }
+
+protected:
+ const DiskArchive* archive;
+ std::string path;
+ u32 total_entries_in_directory;
+ FileUtil::FSTEntry directory;
+
+ // We need to remember the last entry we returned, so a subsequent call to Read will continue
+ // from the next one. This iterator will always point to the next unread entry.
+ std::vector<FileUtil::FSTEntry>::iterator children_iterator;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/file.h b/src/core/file_sys/file_backend.h
index 4013b6c3..539ec731 100644
--- a/src/core/file_sys/file.h
+++ b/src/core/file_sys/file_backend.h
@@ -13,10 +13,10 @@
namespace FileSys {
-class File : NonCopyable {
+class FileBackend : NonCopyable {
public:
- File() { }
- virtual ~File() { }
+ FileBackend() { }
+ virtual ~FileBackend() { }
/**
* Open the file
@@ -61,6 +61,11 @@ public:
* @return true if the file closed correctly
*/
virtual bool Close() const = 0;
+
+ /**
+ * Flushes the file
+ */
+ virtual void Flush() const = 0;
};
} // namespace FileSys
diff --git a/src/core/file_sys/file_romfs.cpp b/src/core/file_sys/file_romfs.cpp
index b55708df..5f38c270 100644
--- a/src/core/file_sys/file_romfs.cpp
+++ b/src/core/file_sys/file_romfs.cpp
@@ -5,24 +5,19 @@
#include "common/common_types.h"
#include "core/file_sys/file_romfs.h"
+#include "core/file_sys/archive_romfs.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
-File_RomFS::File_RomFS() {
-}
-
-File_RomFS::~File_RomFS() {
-}
-
/**
* Open the file
* @return true if the file opened correctly
*/
bool File_RomFS::Open() {
- return false;
+ return true;
}
/**
@@ -33,7 +28,9 @@ bool File_RomFS::Open() {
* @return Number of bytes read
*/
size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
- return -1;
+ LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length);
+ memcpy(buffer, &archive->raw_data[(u32)offset], length);
+ return length;
}
/**
@@ -45,7 +42,8 @@ size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
* @return Number of bytes written
*/
size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
- return -1;
+ LOG_WARNING(Service_FS, "Attempted to write to ROMFS.");
+ return 0;
}
/**
@@ -53,7 +51,7 @@ size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, co
* @return Size of the file in bytes
*/
size_t File_RomFS::GetSize() const {
- return -1;
+ return sizeof(u8) * archive->raw_data.size();
}
/**
@@ -62,6 +60,7 @@ size_t File_RomFS::GetSize() const {
* @return true if successful
*/
bool File_RomFS::SetSize(const u64 size) const {
+ LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS");
return false;
}
diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h
index 5196701d..32fa6b6d 100644
--- a/src/core/file_sys/file_romfs.h
+++ b/src/core/file_sys/file_romfs.h
@@ -6,7 +6,7 @@
#include "common/common_types.h"
-#include "core/file_sys/file.h"
+#include "core/file_sys/file_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -14,10 +14,11 @@
namespace FileSys {
-class File_RomFS final : public File {
+class Archive_RomFS;
+
+class File_RomFS final : public FileBackend {
public:
- File_RomFS();
- ~File_RomFS() override;
+ File_RomFS(const Archive_RomFS* archive) : archive(archive) {}
/**
* Open the file
@@ -62,6 +63,11 @@ public:
* @return true if the file closed correctly
*/
bool Close() const override;
+
+ void Flush() const override { }
+
+private:
+ const Archive_RomFS* archive;
};
} // namespace FileSys
diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp
deleted file mode 100644
index 26204392..00000000
--- a/src/core/file_sys/file_sdmc.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include <sys/stat.h>
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/file_sdmc.h"
-#include "core/file_sys/archive_sdmc.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-File_SDMC::File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode) {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../etc/passwd can give the emulated program your users list.
- this->path = archive->GetMountPoint() + path;
- this->mode.hex = mode.hex;
-}
-
-File_SDMC::~File_SDMC() {
- Close();
-}
-
-/**
- * Open the file
- * @return true if the file opened correctly
- */
-bool File_SDMC::Open() {
- if (!mode.create_flag && !FileUtil::Exists(path)) {
- ERROR_LOG(FILESYS, "Non-existing file %s can’t be open without mode create.", path.c_str());
- return false;
- }
-
- std::string mode_string;
- if (mode.read_flag && mode.write_flag)
- mode_string = "w+";
- else if (mode.read_flag)
- mode_string = "r";
- else if (mode.write_flag)
- mode_string = "w";
-
- file = new FileUtil::IOFile(path, mode_string.c_str());
- return true;
-}
-
-/**
- * Read data from the file
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from file
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
-size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
- file->Seek(offset, SEEK_SET);
- return file->ReadBytes(buffer, length);
-}
-
-/**
- * Write data to the file
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to file
- * @param flush The flush parameters (0 == do not flush)
- * @param buffer Buffer to read data from
- * @return Number of bytes written
- */
-size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
- file->Seek(offset, SEEK_SET);
- size_t written = file->WriteBytes(buffer, length);
- if (flush)
- file->Flush();
- return written;
-}
-
-/**
- * Get the size of the file in bytes
- * @return Size of the file in bytes
- */
-size_t File_SDMC::GetSize() const {
- return static_cast<size_t>(file->GetSize());
-}
-
-/**
- * Set the size of the file in bytes
- * @param size New size of the file
- * @return true if successful
- */
-bool File_SDMC::SetSize(const u64 size) const {
- file->Resize(size);
- file->Flush();
- return true;
-}
-
-/**
- * Close the file
- * @return true if the file closed correctly
- */
-bool File_SDMC::Close() const {
- return file->Close();
-}
-
-} // namespace FileSys
diff --git a/src/core/file_sys/file_sdmc.h b/src/core/file_sys/file_sdmc.h
deleted file mode 100644
index df032f7c..00000000
--- a/src/core/file_sys/file_sdmc.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-
-#include "core/file_sys/file.h"
-#include "core/file_sys/archive_sdmc.h"
-#include "core/loader/loader.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-class File_SDMC final : public File {
-public:
- File_SDMC();
- File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode);
- ~File_SDMC() override;
-
- /**
- * Open the file
- * @return true if the file opened correctly
- */
- bool Open() override;
-
- /**
- * Read data from the file
- * @param offset Offset in bytes to start reading data from
- * @param length Length in bytes of data to read from file
- * @param buffer Buffer to read data into
- * @return Number of bytes read
- */
- size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
-
- /**
- * Write data to the file
- * @param offset Offset in bytes to start writing data to
- * @param length Length in bytes of data to write to file
- * @param flush The flush parameters (0 == do not flush)
- * @param buffer Buffer to read data from
- * @return Number of bytes written
- */
- size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
-
- /**
- * Get the size of the file in bytes
- * @return Size of the file in bytes
- */
- size_t GetSize() const override;
-
- /**
- * Set the size of the file in bytes
- * @param size New size of the file
- * @return true if successful
- */
- bool SetSize(const u64 size) const override;
-
- /**
- * Close the file
- * @return true if the file closed correctly
- */
- bool Close() const override;
-
-private:
- std::string path;
- Mode mode;
- FileUtil::IOFile* file;
-};
-
-} // namespace FileSys
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index a45e6142..d8ba9e6c 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -1,8 +1,9 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "common/common_types.h"
+#include "common/log.h"
#include "core/hle/config_mem.h"
@@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
break;
default:
- ERROR_LOG(HLE, "unknown addr=0x%08X", addr);
+ LOG_ERROR(Kernel, "unknown addr=0x%08X", addr);
}
}
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp
index 1eb33eb8..e34229a5 100644
--- a/src/core/hle/coprocessor.cpp
+++ b/src/core/hle/coprocessor.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "core/hle/coprocessor.h"
#include "core/hle/hle.h"
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 55eaf062..b44479b2 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -50,7 +50,7 @@ template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){
template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
s32 param_1 = 0;
- s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
+ s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
(PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)));
Core::g_app_core->SetReg(1, (u32)param_1);
FuncReturn(retval);
@@ -103,7 +103,7 @@ template<s32 func(void*)> void Wrap() {
}
template<s32 func(s64*, u32, void*, s32)> void Wrap(){
- FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
+ FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
(s32)PARAM(3)));
}
@@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() {
FuncReturn(retval);
}
+template<s32 func(u32*, s32, s32)> void Wrap() {
+ u32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
+template<s32 func(s32*, u32, s32)> void Wrap() {
+ s32 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2));
+ Core::g_app_core->SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index b03894ad..cc3d5255 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <vector>
@@ -8,6 +8,7 @@
#include "core/hle/hle.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,7 +21,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n
const FunctionDef* GetSVCInfo(u32 opcode) {
u32 func_num = opcode & 0xFFFFFF; // 8 bits
if (func_num > 0xFF) {
- ERROR_LOG(HLE,"unknown svc=0x%02X", func_num);
+ LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num);
return nullptr;
}
return &g_module_db[0].func_table[func_num];
@@ -35,14 +36,12 @@ void CallSVC(u32 opcode) {
if (info->func) {
info->func();
} else {
- ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str());
+ LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str());
}
}
void Reschedule(const char *reason) {
-#ifdef _DEBUG
- _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
-#endif
+ _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
Core::g_app_core->PrepareReschedule();
g_reschedule = true;
}
@@ -58,18 +57,20 @@ void RegisterAllModules() {
void Init() {
Service::Init();
-
+ Service::FS::ArchiveInit();
+
RegisterAllModules();
- NOTICE_LOG(HLE, "initialized OK");
+ LOG_DEBUG(Kernel, "initialized OK");
}
void Shutdown() {
+ Service::FS::ArchiveShutdown();
Service::Shutdown();
g_module_db.clear();
- NOTICE_LOG(HLE, "shutdown OK");
+ LOG_DEBUG(Kernel, "shutdown OK");
}
} // namespace
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index bf4d8457..4ab258c6 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 2b21657d..9a921108 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -24,23 +24,12 @@ public:
Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; }
std::string name; ///< Name of address arbiter object (optional)
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
switch (type) {
// Signal thread(s) waiting for arbitrate address...
@@ -58,16 +47,16 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, handle);
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
HLE::Reschedule(__func__);
}
break;
default:
- ERROR_LOG(KERNEL, "unknown type=%d", type);
- return -1;
+ LOG_ERROR(Kernel, "unknown type=%d", type);
+ return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Create an address arbiter
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index a483fe46..8a5fb10b 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -11,7 +11,7 @@
// Address arbiters are an underlying kernel synchronization object that can be created/used via
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
-// semphores.
+// semphores.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
@@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
};
/// Arbitrate an address
-Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
/// Create an address arbiter
Handle CreateAddressArbiter(const std::string& name = "Unknown");
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
deleted file mode 100644
index 764082d7..00000000
--- a/src/core/hle/kernel/archive.cpp
+++ /dev/null
@@ -1,436 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common_types.h"
-#include "common/file_util.h"
-#include "common/math_util.h"
-
-#include "core/file_sys/archive.h"
-#include "core/file_sys/archive_sdmc.h"
-#include "core/file_sys/directory.h"
-#include "core/hle/service/service.h"
-#include "core/hle/kernel/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-// Command to access archive file
-enum class FileCommand : u32 {
- Dummy1 = 0x000100C6,
- Control = 0x040100C4,
- OpenSubFile = 0x08010100,
- Read = 0x080200C2,
- Write = 0x08030102,
- GetSize = 0x08040000,
- SetSize = 0x08050080,
- GetAttributes = 0x08060000,
- SetAttributes = 0x08070040,
- Close = 0x08080000,
- Flush = 0x08090000,
-};
-
-// Command to access directory
-enum class DirectoryCommand : u32 {
- Dummy1 = 0x000100C6,
- Control = 0x040100C4,
- Read = 0x08010042,
- Close = 0x08020000,
-};
-
-class Archive : public Object {
-public:
- std::string GetTypeName() const override { return "Archive"; }
- std::string GetName() const override { return name; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
- Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
-
- std::string name; ///< Name of archive (optional)
- FileSys::Archive* backend; ///< Archive backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
-
- switch (cmd) {
- // Read from archive...
- case FileCommand::Read:
- {
- u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- u32 length = cmd_buff[3];
- u32 address = cmd_buff[5];
-
- // Number of bytes read
- cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
- break;
- }
- // Write to archive...
- case FileCommand::Write:
- {
- u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- u32 length = cmd_buff[3];
- u32 flush = cmd_buff[4];
- u32 address = cmd_buff[6];
-
- // Number of bytes written
- cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
- break;
- }
- case FileCommand::GetSize:
- {
- u64 filesize = (u64) backend->GetSize();
- cmd_buff[2] = (u32) filesize; // Lower word
- cmd_buff[3] = (u32) (filesize >> 32); // Upper word
- break;
- }
- case FileCommand::SetSize:
- {
- backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
- break;
- }
- case FileCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<Archive>(GetHandle());
- CloseArchive(backend->GetIdCode());
- break;
- }
- // Unknown command...
- default:
- {
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- return -1;
- }
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class File : public Object {
-public:
- std::string GetTypeName() const override { return "File"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
- Kernel::HandleType GetHandleType() const override { return HandleType::File; }
-
- std::string path; ///< Path of the file
- std::unique_ptr<FileSys::File> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
- switch (cmd) {
-
- // Read from file...
- case FileCommand::Read:
- {
- u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
- u32 length = cmd_buff[3];
- u32 address = cmd_buff[5];
- DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x",
- GetTypeName().c_str(), GetName().c_str(), offset, length, address);
- cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
- break;
- }
-
- // Write to file...
- case FileCommand::Write:
- {
- u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
- u32 length = cmd_buff[3];
- u32 flush = cmd_buff[4];
- u32 address = cmd_buff[6];
- DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
- GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
- cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
- break;
- }
-
- case FileCommand::GetSize:
- {
- DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
- u64 size = backend->GetSize();
- cmd_buff[2] = (u32)size;
- cmd_buff[3] = size >> 32;
- break;
- }
-
- case FileCommand::SetSize:
- {
- u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
- DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), size);
- backend->SetSize(size);
- break;
- }
-
- case FileCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<File>(GetHandle());
- break;
- }
-
- // Unknown command...
- default:
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-class Directory : public Object {
-public:
- std::string GetTypeName() const override { return "Directory"; }
- std::string GetName() const override { return path; }
-
- static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
- Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
-
- std::string path; ///< Path of the directory
- std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
-
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = Service::GetCommandBuffer();
- DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
- switch (cmd) {
-
- // Read from directory...
- case DirectoryCommand::Read:
- {
- u32 count = cmd_buff[1];
- u32 address = cmd_buff[3];
- FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
- DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count);
-
- // Number of entries actually read
- cmd_buff[2] = backend->Read(count, entries);
- break;
- }
-
- case DirectoryCommand::Close:
- {
- DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
- Kernel::g_object_pool.Destroy<Directory>(GetHandle());
- break;
- }
-
- // Unknown command...
- default:
- ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
- cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
- return -1;
- }
- cmd_buff[1] = 0; // No error
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code) {
- auto itr = g_archive_map.find(id_code);
- if (itr == g_archive_map.end()) {
- return 0;
- }
- return itr->second;
-}
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code) {
- if (1 != g_archive_map.erase(id_code)) {
- ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code);
- return -1;
- }
-
- INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
- return 0;
-}
-
-/**
- * Mounts an archive
- * @param archive Pointer to the archive to mount
- * @return Result of operation, 0 on success, otherwise error code
- */
-Result MountArchive(Archive* archive) {
- FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
- if (0 != OpenArchive(id_code)) {
- ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
- return -1;
- }
- g_archive_map[id_code] = archive->GetHandle();
- INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
- return 0;
-}
-
-/**
- * Creates an Archive
- * @param handle Handle to newly created archive object
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Newly created Archive object
- */
-Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
- Archive* archive = new Archive;
- handle = Kernel::g_object_pool.Create(archive);
- archive->name = name;
- archive->backend = backend;
-
- MountArchive(archive);
-
- return archive;
-}
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
- Handle handle;
- CreateArchive(handle, backend, name);
- return handle;
-}
-
-/**
- * Open a File from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the File inside of the Archive
- * @param mode Mode under which to open the File
- * @return Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
- File* file = new File;
- Handle handle = Kernel::g_object_pool.Create(file);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- file->path = path;
- file->backend = archive->backend->OpenFile(path, mode);
-
- if (!file->backend)
- return 0;
-
- return handle;
-}
-
-/**
- * Create a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& path) {
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- if (archive == nullptr)
- return -1;
- if (archive->backend->CreateDirectory(path))
- return 0;
- return -1;
-}
-
-/**
- * Open a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) {
- Directory* directory = new Directory;
- Handle handle = Kernel::g_object_pool.Create(directory);
-
- Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
- directory->path = path;
- directory->backend = archive->backend->OpenDirectory(path);
-
- return handle;
-}
-
-/// Initialize archives
-void ArchiveInit() {
- g_archive_map.clear();
-
- // TODO(Link Mauve): Add the other archive types (see here for the known types:
- // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
- // archive type is SDMC, so it is the only one getting exposed.
-
- std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
- auto archive = new FileSys::Archive_SDMC(sdmc_directory);
- if (archive->Initialize())
- CreateArchive(archive, "SDMC");
- else
- ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
-}
-
-/// Shutdown archives
-void ArchiveShutdown() {
- g_archive_map.clear();
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
deleted file mode 100644
index 0230996b..00000000
--- a/src/core/hle/kernel/archive.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-#include "core/hle/kernel/kernel.h"
-#include "core/file_sys/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-/**
- * Opens an archive
- * @param id_code IdCode of the archive to open
- * @return Handle to archive if it exists, otherwise a null handle (0)
- */
-Handle OpenArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Closes an archive
- * @param id_code IdCode of the archive to open
- * @return true if it worked fine
- */
-Result CloseArchive(FileSys::Archive::IdCode id_code);
-
-/**
- * Creates an Archive
- * @param backend File system backend interface to the archive
- * @param name Optional name of Archive
- * @return Handle to newly created Archive object
- */
-Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
-
-/**
- * Open a File from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the File inside of the Archive
- * @param mode Mode under which to open the File
- * @return Opened File object
- */
-Handle OpenFileFromArchive(Handle archive_handle, const std::string& name, const FileSys::Mode mode);
-
-/**
- * Create a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Whether creation of directory succeeded
- */
-Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& name);
-
-/**
- * Open a Directory from an Archive
- * @param archive_handle Handle to an open Archive object
- * @param path Path to the Directory inside of the Archive
- * @return Opened Directory object
- */
-Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& name);
-
-/// Initialize archives
-void ArchiveInit();
-
-/// Shutdown archives
-void ArchiveShutdown();
-
-} // namespace FileSys
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 45ed79be..28808020 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <algorithm>
@@ -30,13 +30,8 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
std::string name; ///< Name of event (optional)
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- *wait = locked;
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = locked;
if (locked) {
Handle thread = GetCurrentThreadHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
@@ -47,7 +42,7 @@ public:
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
locked = true;
}
- return 0;
+ return MakeResult<bool>(wait);
}
};
@@ -57,12 +52,12 @@ public:
* @param permanent_locked Boolean permanent locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
evt->permanent_locked = permanent_locked;
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
* @param locked Boolean locked value to set event
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SetEventLocked(const Handle handle, const bool locked) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = locked;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -86,16 +81,16 @@ Result SetEventLocked(const Handle handle, const bool locked) {
* @param handle Handle to event to signal
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode SignalEvent(const Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
// Resume threads waiting for event to signal
bool event_caught = false;
for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
ResumeThreadFromWait( evt->waiting_threads[i]);
- // If any thread is signalled awake by this event, assume the event was "caught" and reset
+ // If any thread is signalled awake by this event, assume the event was "caught" and reset
// the event. This will result in the next thread waiting on the event to block. Otherwise,
// the event will not be reset, and the next thread to call WaitSynchronization on it will
// not block. Not sure if this is correct behavior, but it seems to work.
@@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) {
if (!evt->permanent_locked) {
evt->locked = event_caught;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) {
* @param handle Handle to event to clear
* @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle) {
- Event* evt = g_object_pool.GetFast<Event>(handle);
- _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+ResultCode ClearEvent(Handle handle) {
+ Event* evt = g_object_pool.Get<Event>(handle);
+ if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
evt->locked = true;
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index c39b3318..73aec4e7 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -15,31 +15,27 @@ namespace Kernel {
* Changes whether an event is locked or not
* @param handle Handle to event to change
* @param locked Boolean locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetEventLocked(const Handle handle, const bool locked);
+ResultCode SetEventLocked(const Handle handle, const bool locked);
/**
* Hackish function to set an events permanent lock state, used to pass through synch blocks
* @param handle Handle to event to change
* @param permanent_locked Boolean permanent locked value to set event
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked);
+ResultCode SetPermanentLock(Handle handle, const bool permanent_locked);
/**
* Signals an event
* @param handle Handle to event to signal
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result SignalEvent(const Handle handle);
+ResultCode SignalEvent(const Handle handle);
/**
* Clears an event
* @param handle Handle to event to clear
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ClearEvent(Handle handle);
+ResultCode ClearEvent(Handle handle);
/**
* Creates an event
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 88cbc1af..6a690e91 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,18 +1,20 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
+
+#include <algorithm>
#include "common/common.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/archive.h"
namespace Kernel {
Handle g_main_thread = 0;
ObjectPool g_object_pool;
+u64 g_program_id = 0;
ObjectPool::ObjectPool() {
next_id = INITIAL_NEXT_ID;
@@ -33,11 +35,11 @@ Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
return i + HANDLE_OFFSET;
}
}
- ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
+ LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use.");
return 0;
}
-bool ObjectPool::IsValid(Handle handle) {
+bool ObjectPool::IsValid(Handle handle) const {
int index = handle - HANDLE_OFFSET;
if (index < 0)
return false;
@@ -60,7 +62,7 @@ void ObjectPool::Clear() {
Object* &ObjectPool::operator [](Handle handle)
{
- _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
+ _dbg_assert_msg_(Kernel, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
return pool[handle - HANDLE_OFFSET];
}
@@ -68,37 +70,30 @@ void ObjectPool::List() {
for (int i = 0; i < MAX_COUNT; i++) {
if (occupied[i]) {
if (pool[i]) {
- INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
+ LOG_DEBUG(Kernel, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
pool[i]->GetName().c_str());
}
}
}
}
-int ObjectPool::GetCount() {
- int count = 0;
- for (int i = 0; i < MAX_COUNT; i++) {
- if (occupied[i])
- count++;
- }
- return count;
+int ObjectPool::GetCount() const {
+ return std::count(occupied.begin(), occupied.end(), true);
}
Object* ObjectPool::CreateByIDType(int type) {
- ERROR_LOG(COMMON, "Unimplemented: %d.", type);
+ LOG_ERROR(Kernel, "Unimplemented: %d.", type);
return nullptr;
}
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
- Kernel::ArchiveInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
- Kernel::ArchiveShutdown();
g_object_pool.Clear(); // Free all kernel objects
}
@@ -109,8 +104,6 @@ void Shutdown() {
* @return True on success, otherwise false
*/
bool LoadExec(u32 entry_point) {
- Init();
-
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 867d1b89..7123485b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,12 +1,13 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include "common/common.h"
+#include "core/hle/result.h"
typedef u32 Handle;
typedef s32 Result;
@@ -21,7 +22,7 @@ enum KernelHandle {
enum class HandleType : u32 {
Unknown = 0,
Port = 1,
- Service = 2,
+ Session = 2,
Event = 3,
Mutex = 4,
SharedMemory = 5,
@@ -29,12 +30,9 @@ enum class HandleType : u32 {
Thread = 7,
Process = 8,
AddressArbiter = 9,
- File = 10,
- Semaphore = 11,
- Archive = 12,
- Directory = 13,
+ Semaphore = 10,
};
-
+
enum {
DEFAULT_STACK_SIZE = 0x4000,
};
@@ -52,21 +50,13 @@ public:
virtual Kernel::HandleType GetHandleType() const = 0;
/**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
+ * Wait for kernel object to synchronize.
+ * @return True if the current thread should wait as a result of the wait
*/
- virtual Result SyncRequest(bool* wait) {
- ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
- return -1;
+ virtual ResultVal<bool> WaitSynchronization() {
+ LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
+ return UnimplementedFunction(ErrorModule::Kernel);
}
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- virtual Result WaitSynchronization(bool* wait) = 0;
};
class ObjectPool : NonCopyable {
@@ -80,38 +70,29 @@ public:
static Object* CreateByIDType(int type);
template <class T>
- u32 Destroy(Handle handle) {
- u32 error;
- if (Get<T>(handle, error)) {
+ void Destroy(Handle handle) {
+ if (Get<T>(handle)) {
occupied[handle - HANDLE_OFFSET] = false;
delete pool[handle - HANDLE_OFFSET];
}
- return error;
- };
+ }
- bool IsValid(Handle handle);
+ bool IsValid(Handle handle) const;
template <class T>
- T* Get(Handle handle, u32& outError) {
+ T* Get(Handle handle) {
if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
- // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
- if (handle != 0 && (u32)handle != 0x80020001) {
- WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ if (handle != 0) {
+ LOG_ERROR(Kernel, "Bad object handle %08x", handle);
}
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ return nullptr;
} else {
- // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
- // it just acted as a static case and everything worked. This means that we will never
- // see the Wrong type object error below, but we'll just have to live with that danger.
- T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]);
- if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
- WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
- outError = 0;//T::GetMissingErrorCode();
- return 0;
+ Object* t = pool[handle - HANDLE_OFFSET];
+ if (t->GetHandleType() != T::GetStaticHandleType()) {
+ LOG_ERROR(Kernel, "Wrong object type for %08x", handle);
+ return nullptr;
}
- outError = 0;//SCE_KERNEL_ERROR_OK;
- return t;
+ return static_cast<T*>(t);
}
}
@@ -119,7 +100,7 @@ public:
template <class T>
T *GetFast(Handle handle) {
const Handle realHandle = handle - HANDLE_OFFSET;
- _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
+ _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
return static_cast<T*>(pool[realHandle]);
}
@@ -139,9 +120,9 @@ public:
}
bool GetIDType(Handle handle, HandleType* type) const {
- if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
- !occupied[handle - HANDLE_OFFSET]) {
- ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
+ if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
+ !occupied[handle - HANDLE_OFFSET]) {
+ LOG_ERROR(Kernel, "Bad object handle %08X", handle);
return false;
}
Object* t = pool[handle - HANDLE_OFFSET];
@@ -152,10 +133,10 @@ public:
Object* &operator [](Handle handle);
void List();
void Clear();
- int GetCount();
+ int GetCount() const;
private:
-
+
enum {
MAX_COUNT = 0x1000,
HANDLE_OFFSET = 0x100,
@@ -170,6 +151,12 @@ private:
extern ObjectPool g_object_pool;
extern Handle g_main_thread;
+/// The ID code of the currently running game
+/// TODO(Subv): This variable should not be here,
+/// we need a way to store information about the currently loaded application
+/// for later query during runtime, maybe using the LDR service?
+extern u64 g_program_id;
+
/// Initialize the kernel
void Init();
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index fcfd061a..5a173e12 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
#include <vector>
@@ -27,32 +27,7 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
std::string name; ///< Name of mutex (optional)
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- // TODO(bunnei): ImplementMe
- locked = true;
- return 0;
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- *wait = locked;
-
- if (locked) {
- Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
- }
-
- return 0;
- }
+ ResultVal<bool> WaitSynchronization() override;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -60,21 +35,46 @@ public:
typedef std::multimap<Handle, Handle> MutexMap;
static MutexMap g_mutex_held_locks;
-void MutexAcquireLock(Mutex* mutex, Handle thread) {
+/**
+ * Acquires the specified mutex for the specified thread
+ * @param mutex Mutex that is to be acquired
+ * @param thread Thread that will acquired
+ */
+void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
mutex->lock_thread = thread;
}
-void MutexAcquireLock(Mutex* mutex) {
- Handle thread = GetCurrentThreadHandle();
+bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
MutexAcquireLock(mutex, thread);
+ Kernel::ResumeThreadFromWait(thread);
+ return true;
+}
+
+/**
+ * Resumes a thread waiting for the specified mutex
+ * @param mutex The mutex that some thread is waiting on
+ */
+void ResumeWaitingThread(Mutex* mutex) {
+ // Find the next waiting thread for the mutex...
+ if (mutex->waiting_threads.empty()) {
+ // Reset mutex lock thread handle, nothing is waiting
+ mutex->locked = false;
+ mutex->lock_thread = -1;
+ }
+ else {
+ // Resume the next waiting thread and re-lock the mutex
+ std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
+ ReleaseMutexForThread(mutex, *iter);
+ mutex->waiting_threads.erase(iter);
+ }
}
void MutexEraseLock(Mutex* mutex) {
Handle handle = mutex->GetHandle();
auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
- if ((*iter).second == handle) {
+ if (iter->second == handle) {
g_mutex_held_locks.erase(iter);
break;
}
@@ -82,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) {
mutex->lock_thread = -1;
}
+void ReleaseThreadMutexes(Handle thread) {
+ auto locked = g_mutex_held_locks.equal_range(thread);
+
+ // Release every mutex that the thread holds, and resume execution on the waiting threads
+ for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
+ Mutex* mutex = g_object_pool.GetFast<Mutex>(iter->second);
+ ResumeWaitingThread(mutex);
+ }
+
+ // Erase all the locks that this thread holds
+ g_mutex_held_locks.erase(thread);
+}
+
bool LockMutex(Mutex* mutex) {
// Mutex alread locked?
if (mutex->locked) {
@@ -91,43 +104,27 @@ bool LockMutex(Mutex* mutex) {
return true;
}
-bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
- MutexAcquireLock(mutex, thread);
- Kernel::ResumeThreadFromWait(thread);
- return true;
-}
-
bool ReleaseMutex(Mutex* mutex) {
MutexEraseLock(mutex);
- bool woke_threads = false;
-
- // Find the next waiting thread for the mutex...
- while (!woke_threads && !mutex->waiting_threads.empty()) {
- std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
- woke_threads |= ReleaseMutexForThread(mutex, *iter);
- mutex->waiting_threads.erase(iter);
- }
- // Reset mutex lock thread handle, nothing is waiting
- if (!woke_threads) {
- mutex->locked = false;
- mutex->lock_thread = -1;
- }
- return woke_threads;
+ ResumeWaitingThread(mutex);
+ return true;
}
/**
* Releases a mutex
* @param handle Handle to mutex to release
*/
-Result ReleaseMutex(Handle handle) {
- Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
-
- _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
+ResultCode ReleaseMutex(Handle handle) {
+ Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle);
+ if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!ReleaseMutex(mutex)) {
- return -1;
+ // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
+ // what error condition this is supposed to be signaling.
+ return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
+ ErrorSummary::NothingHappened, ErrorLevel::Temporary);
}
- return 0;
+ return RESULT_SUCCESS;
}
/**
@@ -167,4 +164,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
return handle;
}
+ResultVal<bool> Mutex::WaitSynchronization() {
+ bool wait = locked;
+ if (locked) {
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
+ }
+ else {
+ // Lock the mutex when the first thread accesses it
+ locked = true;
+ MutexAcquireLock(this);
+ }
+
+ return MakeResult<bool>(wait);
+}
} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 7d7b5137..7f4909a6 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -13,9 +13,8 @@ namespace Kernel {
/**
* Releases a mutex
* @param handle Handle to mutex to release
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result ReleaseMutex(Handle handle);
+ResultCode ReleaseMutex(Handle handle);
/**
* Creates a mutex
@@ -25,4 +24,10 @@ Result ReleaseMutex(Handle handle);
*/
Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
+/**
+ * Releases all the mutexes held by the specified thread
+ * @param thread Thread that is holding the mutexes
+ */
+void ReleaseThreadMutexes(Handle thread);
+
} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
new file mode 100644
index 00000000..6f56da8a
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -0,0 +1,94 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <queue>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/semaphore.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Semaphore : public Object {
+public:
+ std::string GetTypeName() const override { return "Semaphore"; }
+ std::string GetName() const override { return name; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; }
+
+ u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
+ u32 available_count; ///< Number of free slots left in the semaphore
+ std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
+ std::string name; ///< Name of semaphore (optional)
+
+ /**
+ * Tests whether a semaphore still has free slots
+ * @return Whether the semaphore is available
+ */
+ bool IsAvailable() const {
+ return available_count > 0;
+ }
+
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = !IsAvailable();
+
+ if (wait) {
+ Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
+ waiting_threads.push(GetCurrentThreadHandle());
+ } else {
+ --available_count;
+ }
+
+ return MakeResult<bool>(wait);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ResultCode CreateSemaphore(Handle* handle, u32 initial_count,
+ u32 max_count, const std::string& name) {
+
+ if (initial_count > max_count)
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+
+ Semaphore* semaphore = new Semaphore;
+ *handle = g_object_pool.Create(semaphore);
+
+ // When the semaphore is created, some slots are reserved for other threads,
+ // and the rest is reserved for the caller thread
+ semaphore->max_count = max_count;
+ semaphore->available_count = initial_count;
+ semaphore->name = name;
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
+ Semaphore* semaphore = g_object_pool.Get<Semaphore>(handle);
+ if (semaphore == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ if (semaphore->max_count - semaphore->available_count < release_count)
+ return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+
+ *count = semaphore->available_count;
+ semaphore->available_count += release_count;
+
+ // Notify some of the threads that the semaphore has been released
+ // stop once the semaphore is full again or there are no more waiting threads
+ while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
+ Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
+ semaphore->waiting_threads.pop();
+ --semaphore->available_count;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
new file mode 100644
index 00000000..f0075fdb
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+/**
+ * Creates a semaphore.
+ * @param handle Pointer to the handle of the newly created object
+ * @param initial_count Number of slots reserved for other threads
+ * @param max_count Maximum number of slots the semaphore can have
+ * @param name Optional name of semaphore
+ * @return ResultCode of the error
+ */
+ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown");
+
+/**
+ * Releases a certain number of slots from a semaphore.
+ * @param count The number of free slots the semaphore had before this call
+ * @param handle The handle of the semaphore to release
+ * @param release_count The number of slots to release
+ * @return ResultCode of the error
+ */
+ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
+
+} // namespace
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
new file mode 100644
index 00000000..06ae4bc3
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,58 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
+
+/**
+ * Returns a pointer to the command buffer in kernel memory
+ * @param offset Optional offset into command buffer
+ * @return Pointer to command buffer
+ */
+inline static u32* GetCommandBuffer(const int offset=0) {
+ return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
+}
+
+/**
+ * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS
+ * primitive for communication between different processes, and are used to implement service calls
+ * to the various system services.
+ *
+ * To make a service call, the client must write the command header and parameters to the buffer
+ * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
+ * SVC call with its Session handle. The kernel will read the command header, using it to marshall
+ * the parameters to the process at the server endpoint of the session. After the server replies to
+ * the request, the response is marshalled back to the caller's TLS buffer and control is
+ * transferred back to it.
+ *
+ * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC
+ * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called
+ * with the session handle, this class's SyncRequest method is called, which should read the TLS
+ * buffer and emulate the call accordingly. Since the code can directly read the emulated memory,
+ * no parameter marshalling is done.
+ *
+ * In the long term, this should be turned into the full-fledged IPC mechanism implemented by
+ * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
+ * opposed to HLE simulations.
+ */
+class Session : public Object {
+public:
+ std::string GetTypeName() const override { return "Session"; }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Session; }
+ Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Session; }
+
+ /**
+ * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls
+ * aren't supported yet.
+ */
+ virtual ResultVal<bool> SyncRequest() = 0;
+};
+
+}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 6bd5e272..3c8c502c 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include "common/common.h"
@@ -16,17 +16,6 @@ public:
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
- return 0;
- }
-
u32 base_address; ///< Address of shared memory block in RAM
MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
@@ -48,11 +37,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
return shared_memory;
}
-/**
- * Creates a shared memory object
- * @param name Optional name of shared memory object
- * @return Handle of newly created shared memory object
- */
Handle CreateSharedMemory(const std::string& name) {
Handle handle;
CreateSharedMemory(handle, name);
@@ -67,39 +51,36 @@ Handle CreateSharedMemory(const std::string& name) {
* @param other_permissions Memory block map other permissions (specified by SVC field)
* @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
MemoryPermission other_permissions) {
if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
- ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
- handle);
- return -1;
+ LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
+ handle, address);
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
- SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
shared_memory->base_address = address;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
- return 0;
+ return RESULT_SUCCESS;
}
-/**
- * Gets a pointer to the shared memory block
- * @param handle Shared memory block handle
- * @param offset Offset from the start of the shared memory block to get pointer
- * @return Pointer to the shared memory block from the specified offset
- */
-u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
- SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
- _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
+ SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (0 != shared_memory->base_address)
- return Memory::GetPointer(shared_memory->base_address + offset);
+ return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
- ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
- return nullptr;
+ LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
+ // TODO(yuriks): Verify error code.
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidState, ErrorLevel::Permanent);
}
} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 6204d8a4..bb778ec2 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
@@ -36,9 +36,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown");
* @param address Address in system memory to map shared memory block to
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
- * @return Result of operation, 0 on success, otherwise error code
*/
-Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
+ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions,
MemoryPermission other_permissions);
/**
@@ -47,6 +46,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
* @param offset Offset from the start of the shared memory block to get pointer
* @return Pointer to the shared memory block from the specified offset
*/
-u8* GetSharedMemoryPointer(Handle handle, u32 offset);
+ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset);
} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e15590c4..1c04701d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <algorithm>
#include <list>
@@ -11,10 +11,12 @@
#include "common/thread_queue_list.h"
#include "core/core.h"
-#include "core/mem_map.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/result.h"
+#include "core/mem_map.h"
namespace Kernel {
@@ -33,25 +35,23 @@ public:
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- if (status != THREADSTATUS_DORMANT) {
+ ResultVal<bool> WaitSynchronization() override {
+ const bool wait = status != THREADSTATUS_DORMANT;
+ if (wait) {
Handle thread = GetCurrentThreadHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
waiting_threads.push_back(thread);
}
WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
- *wait = true;
}
- return 0;
+
+ return MakeResult<bool>(wait);
}
ThreadContext context;
+ u32 thread_id;
+
u32 status;
u32 entry_point;
u32 stack_top;
@@ -64,6 +64,7 @@ public:
WaitType wait_type;
Handle wait_handle;
+ VAddr wait_address;
std::vector<Handle> waiting_threads;
@@ -71,17 +72,20 @@ public:
};
// Lists all thread ids that aren't deleted/etc.
-std::vector<Handle> g_thread_queue;
+static std::vector<Handle> thread_queue;
// Lists only ready thread ids.
-Common::ThreadQueueList<Handle> g_thread_ready_queue;
+static Common::ThreadQueueList<Handle> thread_ready_queue;
+
+static Handle current_thread_handle;
+static Thread* current_thread;
-Handle g_current_thread_handle;
-Thread* g_current_thread;
+static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
+static u32 next_thread_id; ///< The next available thread id
/// Gets the current thread
inline Thread* GetCurrentThread() {
- return g_current_thread;
+ return current_thread;
}
/// Gets the current thread handle
@@ -91,8 +95,8 @@ Handle GetCurrentThreadHandle() {
/// Sets the current thread
inline void SetCurrentThread(Thread* t) {
- g_current_thread = t;
- g_current_thread_handle = t->GetHandle();
+ current_thread = t;
+ current_thread_handle = t->GetHandle();
}
/// Saves the current CPU context
@@ -113,7 +117,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
t->context.pc = t->context.reg_15 = t->entry_point;
t->context.sp = t->stack_top;
t->context.cpsr = 0x1F; // Usermode
-
+
// TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a
// thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be
// agnostic of the CPU core.
@@ -124,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
}
t->wait_type = WAITTYPE_NONE;
t->wait_handle = 0;
+ t->wait_address = 0;
}
/// Change a thread to "ready" state
@@ -131,40 +136,44 @@ void ChangeReadyState(Thread* t, bool ready) {
Handle handle = t->GetHandle();
if (t->IsReady()) {
if (!ready) {
- g_thread_ready_queue.remove(t->current_priority, handle);
+ thread_ready_queue.remove(t->current_priority, handle);
}
} else if (ready) {
if (t->IsRunning()) {
- g_thread_ready_queue.push_front(t->current_priority, handle);
+ thread_ready_queue.push_front(t->current_priority, handle);
} else {
- g_thread_ready_queue.push_back(t->current_priority, handle);
+ thread_ready_queue.push_back(t->current_priority, handle);
}
t->status = THREADSTATUS_READY;
}
}
/// Verify that a thread has not been released from waiting
-inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
- if (type != thread->wait_type || wait_handle != thread->wait_handle)
- return false;
+static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
+ _dbg_assert_(Kernel, thread != nullptr);
+ return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting());
+}
- return true;
+/// Verify that a thread has not been released from waiting (with wait address)
+static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
+ _dbg_assert_(Kernel, thread != nullptr);
+ return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address);
}
/// Stops the current thread
-void StopThread(Handle handle, const char* reason) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
+ResultCode StopThread(Handle handle, const char* reason) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+
+ // Release all the mutexes that this thread holds
+ ReleaseThreadMutexes(handle);
+
ChangeReadyState(thread, false);
thread->status = THREADSTATUS_DORMANT;
- for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
- const Handle waiting_thread = thread->waiting_threads[i];
+ for (Handle waiting_handle : thread->waiting_threads) {
+ Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
- ResumeThreadFromWait(waiting_thread);
+ ResumeThreadFromWait(waiting_handle);
}
}
thread->waiting_threads.clear();
@@ -172,6 +181,9 @@ void StopThread(Handle handle, const char* reason) {
// Stopped threads are never waiting.
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
+ thread->wait_address = 0;
+
+ return RESULT_SUCCESS;
}
/// Changes a threads state
@@ -181,10 +193,10 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
t->status = new_status;
-
+
if (new_status == THREADSTATUS_WAIT) {
if (t->wait_type == WAITTYPE_NONE) {
- ERROR_LOG(KERNEL, "Waittype none not allowed");
+ LOG_ERROR(Kernel, "Waittype none not allowed");
}
}
}
@@ -195,13 +207,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
s32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
continue;
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ if (thread == nullptr)
+ continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
+
if(thread->current_priority <= priority) {
highest_priority_thread = handle;
priority = thread->current_priority;
@@ -216,12 +230,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
/// Arbitrate all threads currently waiting
void ArbitrateAllThreads(u32 arbiter, u32 address) {
-
+
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (const auto& handle : g_thread_queue) {
+ for (Handle handle : thread_queue) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
- // TODO(bunnei): Verify arbiter address...
- if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
+ if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
ResumeThreadFromWait(handle);
}
}
@@ -238,11 +252,11 @@ void CallThread(Thread* t) {
/// Switches CPU context to that of the specified thread
void SwitchContext(Thread* t) {
Thread* cur = GetCurrentThread();
-
+
// Save context for current thread
if (cur) {
SaveContext(cur->context);
-
+
if (cur->IsRunning()) {
ChangeReadyState(cur, true);
}
@@ -263,23 +277,18 @@ void SwitchContext(Thread* t) {
Thread* NextThread() {
Handle next;
Thread* cur = GetCurrentThread();
-
+
if (cur && cur->IsRunning()) {
- next = g_thread_ready_queue.pop_first_better(cur->current_priority);
+ next = thread_ready_queue.pop_first_better(cur->current_priority);
} else {
- next = g_thread_ready_queue.pop_first();
+ next = thread_ready_queue.pop_first();
}
if (next == 0) {
return nullptr;
}
- return Kernel::g_object_pool.GetFast<Thread>(next);
+ return Kernel::g_object_pool.Get<Thread>(next);
}
-/**
- * Puts the current thread in the wait state for the given type
- * @param wait_type Type of wait
- * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
- */
void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
Thread* thread = GetCurrentThread();
thread->wait_type = wait_type;
@@ -287,10 +296,14 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
+ WaitCurrentThread(wait_type, wait_handle);
+ GetCurrentThread()->wait_address = wait_address;
+}
+
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle) {
- u32 error;
- Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
+ Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
if (thread) {
thread->status &= ~THREADSTATUS_WAIT;
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
@@ -305,12 +318,12 @@ void DebugThreadQueue() {
if (!thread) {
return;
}
- INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
- for (u32 i = 0; i < g_thread_queue.size(); i++) {
- Handle handle = g_thread_queue[i];
- s32 priority = g_thread_ready_queue.contains(handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
+ for (u32 i = 0; i < thread_queue.size(); i++) {
+ Handle handle = thread_queue[i];
+ s32 priority = thread_ready_queue.contains(handle);
if (priority != -1) {
- INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
}
}
}
@@ -319,16 +332,17 @@ void DebugThreadQueue() {
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
s32 processor_id, u32 stack_top, int stack_size) {
- _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
- "CreateThread priority=%d, outside of allowable range!", priority)
+ _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
+ "priority=%d, outside of allowable range!", priority)
Thread* thread = new Thread;
handle = Kernel::g_object_pool.Create(thread);
- g_thread_queue.push_back(handle);
- g_thread_ready_queue.prepare(priority);
+ thread_queue.push_back(handle);
+ thread_ready_queue.prepare(priority);
+ thread->thread_id = next_thread_id++;
thread->status = THREADSTATUS_DORMANT;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
@@ -337,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
thread->processor_id = processor_id;
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
+ thread->wait_address = 0;
thread->name = name;
return thread;
@@ -347,28 +362,28 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
u32 stack_top, int stack_size) {
if (name == nullptr) {
- ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
+ LOG_ERROR(Kernel_SVC, "nullptr name");
return -1;
}
if ((u32)stack_size < 0x200) {
- ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name,
+ LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
stack_size);
return -1;
}
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
- WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X",
+ LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
name, priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
// validity of this
priority = new_priority;
}
if (!Memory::GetPointer(entry_point)) {
- ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point);
+ LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
return -1;
}
Handle handle;
- Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
+ Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
stack_size);
ResetThread(thread, arg, 0);
@@ -378,26 +393,30 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
}
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle) {
- Thread* thread = g_object_pool.GetFast<Thread>(handle);
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
- return thread->current_priority;
+ResultVal<u32> GetThreadPriority(const Handle handle) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
+
+ return MakeResult<u32>(thread->current_priority);
}
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority) {
+ResultCode SetThreadPriority(Handle handle, s32 priority) {
Thread* thread = nullptr;
if (!handle) {
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
} else {
- thread = g_object_pool.GetFast<Thread>(handle);
+ thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr) {
+ return InvalidHandle(ErrorModule::Kernel);
+ }
}
_assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
// If priority is invalid, clamp to valid range
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
- WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
+ LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
// validity of this
priority = new_priority;
@@ -405,37 +424,37 @@ Result SetThreadPriority(Handle handle, s32 priority) {
// Change thread priority
s32 old = thread->current_priority;
- g_thread_ready_queue.remove(old, handle);
+ thread_ready_queue.remove(old, handle);
thread->current_priority = priority;
- g_thread_ready_queue.prepare(thread->current_priority);
+ thread_ready_queue.prepare(thread->current_priority);
// Change thread status to "ready" and push to ready queue
if (thread->IsRunning()) {
thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
}
if (thread->IsReady()) {
- g_thread_ready_queue.push_back(thread->current_priority, handle);
+ thread_ready_queue.push_back(thread->current_priority, handle);
}
- return 0;
+ return RESULT_SUCCESS;
}
/// Sets up the primary application thread
Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
-
+
// Initialize new "main" thread
- Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
+ Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
-
+
ResetThread(thread, 0, 0);
-
+
// If running another thread already, set it to "ready" state
Thread* cur = GetCurrentThread();
if (cur && cur->IsRunning()) {
ChangeReadyState(cur, true);
}
-
+
// Run new "main" thread
SetCurrentThread(thread);
thread->status = THREADSTATUS_RUNNING;
@@ -451,13 +470,13 @@ void Reschedule() {
Thread* next = NextThread();
HLE::g_reschedule = false;
if (next > 0) {
- INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
-
+ LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
+
SwitchContext(next);
// Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
// by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
- // This results in the current thread yielding on a VBLANK once, and then it will be
+ // This results in the current thread yielding on a VBLANK once, and then it will be
// immediately placed back in the queue for execution.
if (prev->wait_type == WAITTYPE_VBLANK) {
ResumeThreadFromWait(prev->GetHandle());
@@ -465,9 +484,21 @@ void Reschedule() {
}
}
+ResultCode GetThreadId(u32* thread_id, Handle handle) {
+ Thread* thread = g_object_pool.Get<Thread>(handle);
+ if (thread == nullptr)
+ return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+
+ *thread_id = thread->thread_id;
+
+ return RESULT_SUCCESS;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
+ next_thread_id = INITIAL_THREAD_ID;
}
void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 39fa38b7..be7adfac 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -1,11 +1,15 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
+
+#include "core/mem_map.h"
+
#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
enum ThreadPriority {
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
@@ -55,7 +59,15 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
void Reschedule();
/// Stops the current thread
-void StopThread(Handle thread, const char* reason);
+ResultCode StopThread(Handle thread, const char* reason);
+
+/**
+ * Retrieves the ID of the specified thread handle
+ * @param thread_id Will contain the output thread id
+ * @param handle Handle to the thread we want
+ * @return Whether the function was successful or not
+ */
+ResultCode GetThreadId(u32* thread_id, Handle handle);
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle);
@@ -76,14 +88,22 @@ Handle GetCurrentThreadHandle();
*/
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
+/**
+ * Puts the current thread in the wait state for the given type
+ * @param wait_type Type of wait
+ * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
+ * @param wait_address Arbitration address used to resume from wait
+ */
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
+
/// Put current thread in a wait state - on WaitSynchronization
void WaitThread_Synchronization();
/// Get the priority of the thread specified by handle
-u32 GetThreadPriority(const Handle handle);
+ResultVal<u32> GetThreadPriority(const Handle handle);
/// Set the priority of the thread specified by handle
-Result SetThreadPriority(Handle handle, s32 priority);
+ResultCode SetThreadPriority(Handle handle, s32 priority);
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
new file mode 100644
index 00000000..14d2be4a
--- /dev/null
+++ b/src/core/hle/result.h
@@ -0,0 +1,402 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+#include "common/common_types.h"
+#include "common/bit_field.h"
+
+// All the constants in this file come from http://3dbrew.org/wiki/Error_codes
+
+/// Detailed description of the error. This listing is likely incomplete.
+enum class ErrorDescription : u32 {
+ Success = 0,
+ FS_NotFound = 100,
+ FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
+ InvalidSection = 1000,
+ TooLarge = 1001,
+ NotAuthorized = 1002,
+ AlreadyDone = 1003,
+ InvalidSize = 1004,
+ InvalidEnumValue = 1005,
+ InvalidCombination = 1006,
+ NoData = 1007,
+ Busy = 1008,
+ MisalignedAddress = 1009,
+ MisalignedSize = 1010,
+ OutOfMemory = 1011,
+ NotImplemented = 1012,
+ InvalidAddress = 1013,
+ InvalidPointer = 1014,
+ InvalidHandle = 1015,
+ NotInitialized = 1016,
+ AlreadyInitialized = 1017,
+ NotFound = 1018,
+ CancelRequested = 1019,
+ AlreadyExists = 1020,
+ OutOfRange = 1021,
+ Timeout = 1022,
+ InvalidResultValue = 1023,
+};
+
+/**
+ * Identifies the module which caused the error. Error codes can be propagated through a call
+ * chain, meaning that this doesn't always correspond to the module where the API call made is
+ * contained.
+ */
+enum class ErrorModule : u32 {
+ Common = 0,
+ Kernel = 1,
+ Util = 2,
+ FileServer = 3,
+ LoaderServer = 4,
+ TCB = 5,
+ OS = 6,
+ DBG = 7,
+ DMNT = 8,
+ PDN = 9,
+ GX = 10,
+ I2C = 11,
+ GPIO = 12,
+ DD = 13,
+ CODEC = 14,
+ SPI = 15,
+ PXI = 16,
+ FS = 17,
+ DI = 18,
+ HID = 19,
+ CAM = 20,
+ PI = 21,
+ PM = 22,
+ PM_LOW = 23,
+ FSI = 24,
+ SRV = 25,
+ NDM = 26,
+ NWM = 27,
+ SOC = 28,
+ LDR = 29,
+ ACC = 30,
+ RomFS = 31,
+ AM = 32,
+ HIO = 33,
+ Updater = 34,
+ MIC = 35,
+ FND = 36,
+ MP = 37,
+ MPWL = 38,
+ AC = 39,
+ HTTP = 40,
+ DSP = 41,
+ SND = 42,
+ DLP = 43,
+ HIO_LOW = 44,
+ CSND = 45,
+ SSL = 46,
+ AM_LOW = 47,
+ NEX = 48,
+ Friends = 49,
+ RDT = 50,
+ Applet = 51,
+ NIM = 52,
+ PTM = 53,
+ MIDI = 54,
+ MC = 55,
+ SWC = 56,
+ FatFS = 57,
+ NGC = 58,
+ CARD = 59,
+ CARDNOR = 60,
+ SDMC = 61,
+ BOSS = 62,
+ DBM = 63,
+ Config = 64,
+ PS = 65,
+ CEC = 66,
+ IR = 67,
+ UDS = 68,
+ PL = 69,
+ CUP = 70,
+ Gyroscope = 71,
+ MCU = 72,
+ NS = 73,
+ News = 74,
+ RO_1 = 75,
+ GD = 76,
+ CardSPI = 77,
+ EC = 78,
+ RO_2 = 79,
+ WebBrowser = 80,
+ Test = 81,
+ ENC = 82,
+ PIA = 83,
+
+ Application = 254,
+ InvalidResult = 255
+};
+
+/// A less specific error cause.
+enum class ErrorSummary : u32 {
+ Success = 0,
+ NothingHappened = 1,
+ WouldBlock = 2,
+ OutOfResource = 3, ///< There are no more kernel resources (memory, table slots) to
+ ///< execute the operation.
+ NotFound = 4, ///< A file or resource was not found.
+ InvalidState = 5,
+ NotSupported = 6, ///< The operation is not supported or not implemented.
+ InvalidArgument = 7, ///< Returned when a passed argument is invalid in the current runtime
+ ///< context. (Invalid handle, out-of-bounds pointer or size, etc.)
+ WrongArgument = 8, ///< Returned when a passed argument is in an incorrect format for use
+ ///< with the function. (E.g. Invalid enum value)
+ Canceled = 9,
+ StatusChanged = 10,
+ Internal = 11,
+
+ InvalidResult = 63
+};
+
+/// The severity of the error.
+enum class ErrorLevel : u32 {
+ Success = 0,
+ Info = 1,
+
+ Status = 25,
+ Temporary = 26,
+ Permanent = 27,
+ Usage = 28,
+ Reinitialize = 29,
+ Reset = 30,
+ Fatal = 31
+};
+
+/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
+union ResultCode {
+ u32 raw;
+
+ BitField<0, 10, ErrorDescription> description;
+ BitField<10, 8, ErrorModule> module;
+
+ BitField<21, 6, ErrorSummary> summary;
+ BitField<27, 5, ErrorLevel> level;
+
+ // The last bit of `level` is checked by apps and the kernel to determine if a result code is an error
+ BitField<31, 1, u32> is_error;
+
+ explicit ResultCode(u32 raw) : raw(raw) {}
+ ResultCode(ErrorDescription description_, ErrorModule module_,
+ ErrorSummary summary_, ErrorLevel level_) : raw(0) {
+ description = description_;
+ module = module_;
+ summary = summary_;
+ level = level_;
+ }
+
+ ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; }
+
+ bool IsSuccess() const {
+ return is_error == 0;
+ }
+
+ bool IsError() const {
+ return is_error == 1;
+ }
+};
+
+inline bool operator==(const ResultCode a, const ResultCode b) {
+ return a.raw == b.raw;
+}
+
+inline bool operator!=(const ResultCode a, const ResultCode b) {
+ return a.raw != b.raw;
+}
+
+// Convenience functions for creating some common kinds of errors:
+
+/// The default success `ResultCode`.
+const ResultCode RESULT_SUCCESS(0);
+
+/// Might be returned instead of a dummy success for unimplemented APIs.
+inline ResultCode UnimplementedFunction(ErrorModule module) {
+ return ResultCode(ErrorDescription::NotImplemented, module,
+ ErrorSummary::NotSupported, ErrorLevel::Permanent);
+}
+/// Returned when a function is passed an invalid handle.
+inline ResultCode InvalidHandle(ErrorModule module) {
+ return ResultCode(ErrorDescription::InvalidHandle, module,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+}
+
+/**
+ * This is an optional value type. It holds a `ResultCode` and, if that code is a success code,
+ * also holds a result of type `T`. If the code is an error code then trying to access the inner
+ * value fails, thus ensuring that the ResultCode of functions is always checked properly before
+ * their return value is used. It is similar in concept to the `std::optional` type
+ * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in
+ * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html).
+ *
+ * An example of how it could be used:
+ * \code
+ * ResultVal<int> Frobnicate(float strength) {
+ * if (strength < 0.f || strength > 1.0f) {
+ * // Can't frobnicate too weakly or too strongly
+ * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common,
+ * ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ * } else {
+ * // Frobnicated! Give caller a cookie
+ * return MakeResult<int>(42);
+ * }
+ * }
+ * \endcode
+ *
+ * \code
+ * ResultVal<int> frob_result = Frobnicate(0.75f);
+ * if (frob_result) {
+ * // Frobbed ok
+ * printf("My cookie is %d\n", *frob_result);
+ * } else {
+ * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex);
+ * }
+ * \endcode
+ */
+template <typename T>
+class ResultVal {
+public:
+ /// Constructs an empty `ResultVal` with the given error code. The code must not be a success code.
+ ResultVal(ResultCode error_code = ResultCode(-1))
+ : result_code(error_code)
+ {
+ assert(error_code.IsError());
+ UpdateDebugPtr();
+ }
+
+ /**
+ * Similar to the non-member function `MakeResult`, with the exception that you can manually
+ * specify the success code. `success_code` must not be an error code.
+ */
+ template <typename... Args>
+ static ResultVal WithCode(ResultCode success_code, Args&&... args) {
+ ResultVal<T> result;
+ result.emplace(success_code, std::forward<Args>(args)...);
+ return result;
+ }
+
+ ResultVal(const ResultVal& o)
+ : result_code(o.result_code)
+ {
+ if (!o.empty()) {
+ new (&storage) T(*o.GetPointer());
+ }
+ UpdateDebugPtr();
+ }
+
+ ResultVal(ResultVal&& o)
+ : result_code(o.result_code)
+ {
+ if (!o.empty()) {
+ new (&storage) T(std::move(*o.GetPointer()));
+ }
+ UpdateDebugPtr();
+ }
+
+ ~ResultVal() {
+ if (!empty()) {
+ GetPointer()->~T();
+ }
+ }
+
+ ResultVal& operator=(const ResultVal& o) {
+ if (*this) {
+ if (o) {
+ *GetPointer() = *o.GetPointer();
+ } else {
+ GetPointer()->~T();
+ }
+ } else {
+ if (o) {
+ new (&storage) T(*o.GetPointer());
+ }
+ }
+ result_code = o.result_code;
+ UpdateDebugPtr();
+
+ return *this;
+ }
+
+ /**
+ * Replaces the current result with a new constructed result value in-place. The code must not
+ * be an error code.
+ */
+ template <typename... Args>
+ void emplace(ResultCode success_code, Args&&... args) {
+ assert(success_code.IsSuccess());
+ if (!empty()) {
+ GetPointer()->~T();
+ }
+ new (&storage) T(std::forward<Args>(args)...);
+ result_code = success_code;
+ UpdateDebugPtr();
+ }
+
+ /// Returns true if the `ResultVal` contains an error code and no value.
+ bool empty() const { return result_code.IsError(); }
+
+ /// Returns true if the `ResultVal` contains a return value.
+ bool Succeeded() const { return result_code.IsSuccess(); }
+ /// Returns true if the `ResultVal` contains an error code and no value.
+ bool Failed() const { return empty(); }
+
+ ResultCode Code() const { return result_code; }
+
+ const T& operator* () const { return *GetPointer(); }
+ T& operator* () { return *GetPointer(); }
+ const T* operator->() const { return GetPointer(); }
+ T* operator->() { return GetPointer(); }
+
+ /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
+ template <typename U>
+ T ValueOr(U&& value) const {
+ return !empty() ? *GetPointer() : std::move(value);
+ }
+
+private:
+ typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
+
+ StorageType storage;
+ ResultCode result_code;
+#if _DEBUG
+ // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the
+ // need to cast `storage` to a pointer or pay attention to `result_code`.
+ const T* debug_ptr;
+#endif
+
+ void UpdateDebugPtr() {
+#if _DEBUG
+ debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
+#endif
+ }
+
+ const T* GetPointer() const {
+ assert(!empty());
+ return static_cast<const T*>(static_cast<const void*>(&storage));
+ }
+
+ T* GetPointer() {
+ assert(!empty());
+ return static_cast<T*>(static_cast<void*>(&storage));
+ }
+};
+
+/**
+ * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct
+ * `T` with and creates a success `ResultVal` contained the constructed value.
+ */
+template <typename T, typename... Args>
+ResultVal<T> MakeResult(Args&&... args) {
+ return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...);
+}
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index b39603bd..311682ab 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -11,6 +11,24 @@
namespace AC_U {
+/**
+ * AC_U::GetWifiStatus service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
+ */
+void GetWifiStatus(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] = 0; // Connection type set to none
+
+ LOG_WARNING(Service_AC, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, nullptr, "CreateDefaultConfig"},
{0x00040006, nullptr, "ConnectAsync"},
@@ -18,16 +36,16 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080004, nullptr, "CloseAsync"},
{0x00090002, nullptr, "GetCloseResult"},
{0x000A0000, nullptr, "GetLastErrorCode"},
- {0x000D0000, nullptr, "GetWifiStatus"},
+ {0x000D0000, GetWifiStatus, "GetWifiStatus"},
{0x000E0042, nullptr, "GetCurrentAPInfo"},
{0x00100042, nullptr, "GetCurrentNZoneInfo"},
{0x00110042, nullptr, "GetNZoneApNumService"},
- {0x00240042, nullptr, "AddDenyApType "},
- {0x00270002, nullptr, "GetInfraPriority "},
+ {0x00240042, nullptr, "AddDenyApType"},
+ {0x00270002, nullptr, "GetInfraPriority"},
{0x002D0082, nullptr, "SetRequestEulaVersion"},
{0x00300004, nullptr, "RegisterDisconnectEvent"},
{0x003C0042, nullptr, "GetAPSSIDList"},
- {0x003E0042, nullptr, "IsConnected "},
+ {0x003E0042, nullptr, "IsConnected"},
{0x00400042, nullptr, "SetClientVersion"},
};
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h
index 3c5958d2..c91b2835 100644
--- a/src/core/hle/service/ac_u.h
+++ b/src/core/hle/service/ac_u.h
@@ -9,7 +9,7 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace AC_U
-// socket service "ac:u"
+// socket service "ac:u"
namespace AC_U {
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "ac:u";
}
};
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp
new file mode 100644
index 00000000..b8b06418
--- /dev/null
+++ b/src/core/hle/service/am_app.cpp
@@ -0,0 +1,24 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/am_app.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_APP
+
+namespace AM_APP {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/am_app.h b/src/core/hle/service/am_app.h
new file mode 100644
index 00000000..86a5f5b7
--- /dev/null
+++ b/src/core/hle/service/am_app.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_APP
+
+namespace AM_APP {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "am:app";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp
new file mode 100644
index 00000000..403cac35
--- /dev/null
+++ b/src/core/hle/service/am_net.cpp
@@ -0,0 +1,47 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/am_net.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_NET
+
+namespace AM_NET {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x08010000, nullptr, "OpenTicket"},
+ {0x08020002, nullptr, "TicketAbortInstall"},
+ {0x08030002, nullptr, "TicketFinalizeInstall"},
+ {0x08040100, nullptr, "InstallTitleBegin"},
+ {0x08050000, nullptr, "InstallTitleAbort"},
+ {0x080600C0, nullptr, "InstallTitleResume"},
+ {0x08070000, nullptr, "InstallTitleAbortTMD"},
+ {0x08080000, nullptr, "InstallTitleFinish"},
+ {0x080A0000, nullptr, "OpenTMD"},
+ {0x080B0002, nullptr, "TMDAbortInstall"},
+ {0x080C0042, nullptr, "TMDFinalizeInstall"},
+ {0x080E0040, nullptr, "OpenContentCreate"},
+ {0x080F0002, nullptr, "ContentAbortInstall"},
+ {0x08100040, nullptr, "OpenContentResume"},
+ {0x08120002, nullptr, "ContentFinalizeInstall"},
+ {0x08130000, nullptr, "GetTotalContents"},
+ {0x08140042, nullptr, "GetContentIndexes"},
+ {0x08150044, nullptr, "GetContentsInfo"},
+ {0x08190108, nullptr, "Unknown"},
+ {0x081B00C2, nullptr, "InstallTitlesFinish"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/am_net.h b/src/core/hle/service/am_net.h
new file mode 100644
index 00000000..4816e169
--- /dev/null
+++ b/src/core/hle/service/am_net.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace AM_NET
+
+namespace AM_NET {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "am:net";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index 617b6add..ebfba4d8 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -4,10 +4,12 @@
#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 "apt_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,6 +17,20 @@
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 Handle shared_font_mem = 0;
+
+static Handle lock_handle = 0;
+static std::vector<u8> shared_font;
+
/// Signals used by APT functions
enum class SignalType : u32 {
None = 0x0,
@@ -24,83 +40,178 @@ enum class SignalType : u32 {
};
void Initialize(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
Kernel::SetEventLocked(cmd_buff[3], true);
Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
+ _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock");
+ Kernel::ReleaseMutex(lock_handle);
+
cmd_buff[1] = 0; // No error
- DEBUG_LOG(KERNEL, "called");
+
+ LOG_DEBUG(Service_APT, "called");
}
void GetLockHandle(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
+
+ if (0 == lock_handle) {
+ // TODO(bunnei): Verify if this is created here or at application boot?
+ lock_handle = Kernel::CreateMutex(false, "APT_U:Lock");
+ Kernel::ReleaseMutex(lock_handle);
+ }
cmd_buff[1] = 0; // No error
- cmd_buff[5] = Kernel::CreateMutex(false, "APT_U:Lock");
- DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]);
+
+ // 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] = lock_handle;
+ LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
}
void Enable(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
cmd_buff[1] = 0; // No error
- WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk);
+ LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
}
void InquireNotification(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[2];
cmd_buff[1] = 0; // No error
cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id);
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
+/**
+ * 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 : 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 ReceiveParameter(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0x10;
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
cmd_buff[5] = 0;
cmd_buff[6] = 0;
cmd_buff[7] = 0;
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
/**
-* APT_U::GlanceParameter service function
-* 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
-*/
+ * 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 = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
+
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0;
cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
- cmd_buff[4] = 0;
+ cmd_buff[4] = 0x10; // Parameter buffer size (16)
cmd_buff[5] = 0;
cmd_buff[6] = 0;
cmd_buff[7] = 0;
- cmd_buff[8] = 0;
- WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+
+ LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
+}
+
+/**
+ * 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] = 0; // 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) {
+ LOG_TRACE(Kernel_SVC, "called");
+
+ 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] = 0; // No error
+ cmd_buff[2] = SHARED_FONT_VADDR;
+ cmd_buff[4] = shared_font_mem;
+ } 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);
+ }
}
const Interface::FunctionInfo FunctionTable[] = {
@@ -171,14 +282,14 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
{0x00420080, nullptr, "SleepSystem"},
{0x00430040, nullptr, "NotifyToWait"},
- {0x00440000, nullptr, "GetSharedFont"},
+ {0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
{0x00460104, nullptr, "Wrap"},
{0x00470104, nullptr, "Unwrap"},
{0x00480100, nullptr, "GetProgramInfo"},
{0x00490180, nullptr, "Reboot"},
{0x004A0040, nullptr, "GetCaptureInfo"},
- {0x004B00C2, nullptr, "AppletUtility"},
+ {0x004B00C2, AppletUtility, "AppletUtility"},
{0x004C0000, nullptr, "SetFatalErrDispMode"},
{0x004D0080, nullptr, "GetAppletProgramInfo"},
{0x004E0000, nullptr, "HardwareResetAsync"},
@@ -190,6 +301,32 @@ const Interface::FunctionInfo FunctionTable[] = {
// 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(file.GetSize());
+ file.ReadBytes(shared_font.data(), file.GetSize());
+
+ // Create shared font memory object
+ shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem");
+ } else {
+ LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
+ shared_font_mem = 0;
+ }
+
+ lock_handle = 0;
+
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h
index 5af39e08..30673040 100644
--- a/src/core/hle/service/apt_u.h
+++ b/src/core/hle/service/apt_u.h
@@ -13,8 +13,8 @@ namespace APT_U {
// 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
-// service handle immediately once finished using the service. The commands for APT:U and APT:S are
-// exactly the same, however certain commands are only accessible with APT:S(NS module will call
+// service handle immediately once finished using the service. The commands for APT:U and APT:S are
+// exactly the same, however certain commands are only accessible with APT:S(NS module will call
// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
/// Interface to "APT:U" service
diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp
new file mode 100644
index 00000000..b2ff4a75
--- /dev/null
+++ b/src/core/hle/service/boss_u.cpp
@@ -0,0 +1,28 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/boss_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_U
+
+namespace BOSS_U {
+
+ const Interface::FunctionInfo FunctionTable[] = {
+ {0x00020100, nullptr, "GetStorageInfo"},
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Interface class
+
+ Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ }
+
+ Interface::~Interface() {
+ }
+
+} // namespace
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h
new file mode 100644
index 00000000..af39b8e6
--- /dev/null
+++ b/src/core/hle/service/boss_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace BOSS_U
+
+namespace BOSS_U {
+
+ class Interface : public Service::Interface {
+ public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const {
+ return "boss:U";
+ }
+ };
+
+} // namespace
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp
new file mode 100644
index 00000000..25d90351
--- /dev/null
+++ b/src/core/hle/service/cecd_u.cpp
@@ -0,0 +1,24 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cecd_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_U
+
+namespace CECD_U {
+
+// Empty arrays are illegal -- commented out until an entry is added.
+//const Interface::FunctionInfo FunctionTable[] = { };
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h
new file mode 100644
index 00000000..969e1ed1
--- /dev/null
+++ b/src/core/hle/service/cecd_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CECD_U
+
+namespace CECD_U {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "cecd:u";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg_i.cpp
new file mode 100644
index 00000000..88d13d45
--- /dev/null
+++ b/src/core/hle/service/cfg_i.cpp
@@ -0,0 +1,59 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cfg_i.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_I
+
+namespace CFG_I {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x04010082, nullptr, "GetConfigInfoBlk8"},
+ {0x04020082, nullptr, "GetConfigInfoBlk4"},
+ {0x04030000, nullptr, "UpdateConfigNANDSavegame"},
+ {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x04060000, nullptr, "SecureInfoGetRegion"},
+ {0x04070000, nullptr, "SecureInfoGetByte101"},
+ {0x04080042, nullptr, "SecureInfoGetSerialNo"},
+ {0x04090000, nullptr, "UpdateConfigBlk00040003"},
+ {0x08010082, nullptr, "GetConfigInfoBlk8"},
+ {0x08020082, nullptr, "GetConfigInfoBlk4"},
+ {0x08030000, nullptr, "UpdateConfigNANDSavegame"},
+ {0x080400C2, nullptr, "CreateConfigInfoBlk"},
+ {0x08050000, nullptr, "DeleteConfigNANDSavefile"},
+ {0x08060000, nullptr, "FormatConfig"},
+ {0x08070000, nullptr, "Unknown"},
+ {0x08080000, nullptr, "UpdateConfigBlk1"},
+ {0x08090000, nullptr, "UpdateConfigBlk2"},
+ {0x080A0000, nullptr, "UpdateConfigBlk3"},
+ {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
+ {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
+ {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
+ {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
+ {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x08100000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x08110084, nullptr, "SetSecureInfo"},
+ {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
+ {0x08130000, nullptr, "VerifySigSecureInfo"},
+ {0x08140042, nullptr, "SecureInfoGetData"},
+ {0x08150042, nullptr, "SecureInfoGetSignature"},
+ {0x08160000, nullptr, "SecureInfoGetRegion"},
+ {0x08170000, nullptr, "SecureInfoGetByte101"},
+ {0x08180042, nullptr, "SecureInfoGetSerialNo"},
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg_i.h
new file mode 100644
index 00000000..fe343c96
--- /dev/null
+++ b/src/core/hle/service/cfg_i.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_I
+
+namespace CFG_I {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "cfg:i";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp
index 822b0e2b..2e9d7bf2 100644
--- a/src/core/hle/service/cfg_u.cpp
+++ b/src/core/hle/service/cfg_u.cpp
@@ -11,6 +11,94 @@
namespace CFG_U {
+// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
+#define C(code) ((code)[0] | ((code)[1] << 8))
+
+static const std::array<u16, 187> country_codes = {
+ 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7
+ C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15
+ C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23
+ C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31
+ C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39
+ C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47
+ C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55
+ 0, 0, 0, 0, 0, 0, 0, 0, // 56-63
+ C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71
+ C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79
+ C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87
+ C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95
+ C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103
+ C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111
+ C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119
+ C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127
+ C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135
+ C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143
+ C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151
+ C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159
+ C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167
+ C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175
+ C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183
+ C("SM"), C("VA"), C("BM") // 184-186
+};
+
+#undef C
+
+/**
+ * CFG_User::GetCountryCodeString service function
+ * Inputs:
+ * 1 : Country Code ID
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Country's 2-char string
+ */
+static void GetCountryCodeString(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 country_code_id = cmd_buffer[1];
+
+ if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
+ LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id);
+ cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
+ return;
+ }
+
+ cmd_buffer[1] = 0;
+ cmd_buffer[2] = country_codes[country_code_id];
+}
+
+/**
+ * CFG_User::GetCountryCodeID service function
+ * Inputs:
+ * 1 : Country Code 2-char string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Country Code ID
+ */
+static void GetCountryCodeID(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u16 country_code = cmd_buffer[1];
+ u16 country_code_id = 0;
+
+ // The following algorithm will fail if the first country code isn't 0.
+ _dbg_assert_(Service_CFG, country_codes[0] == 0);
+
+ for (size_t id = 0; id < country_codes.size(); ++id) {
+ if (country_codes[id] == country_code) {
+ country_code_id = id;
+ break;
+ }
+ }
+
+ if (0 == country_code_id) {
+ LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8);
+ cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
+ cmd_buffer[2] = 0xFFFF;
+ return;
+ }
+
+ cmd_buffer[1] = 0;
+ cmd_buffer[2] = country_code_id;
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010082, nullptr, "GetConfigInfoBlk2"},
{0x00020000, nullptr, "SecureInfoGetRegion"},
@@ -20,8 +108,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00060000, nullptr, "GetModelNintendo2DS"},
{0x00070040, nullptr, "unknown"},
{0x00080080, nullptr, "unknown"},
- {0x00090080, nullptr, "GetCountryCodeString"},
- {0x000A0040, nullptr, "GetCountryCodeID"},
+ {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
+ {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg_u.h
index 7525bd7c..8075d19a 100644
--- a/src/core/hle/service/cfg_u.h
+++ b/src/core/hle/service/cfg_u.h
@@ -19,7 +19,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "cfg:u";
}
};
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
new file mode 100644
index 00000000..6e59a9bf
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -0,0 +1,39 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/csnd_snd.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CSND_SND
+
+namespace CSND_SND {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010140, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Shutdown"},
+ {0x00030040, nullptr, "Unknown"},
+ {0x00040080, nullptr, "Unknown"},
+ {0x00050000, nullptr, "Unknown"},
+ {0x00060000, nullptr, "Unknown"},
+ {0x00070000, nullptr, "Unknown"},
+ {0x00080040, nullptr, "Unknown"},
+ {0x00090082, nullptr, "FlushDCache"},
+ {0x000A0082, nullptr, "StoreDCache"},
+ {0x000B0082, nullptr, "InvalidateDCache"},
+ {0x000C0000, nullptr, "Unknown"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
new file mode 100644
index 00000000..31cc85b0
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CSND_SND
+
+namespace CSND_SND {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "csnd:SND";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 9e84ac93..bd82063c 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -4,6 +4,7 @@
#include "common/log.h"
#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/dsp_dsp.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,38 +12,181 @@
namespace DSP_DSP {
+static u32 read_pipe_count;
+static Handle semaphore_event;
+static Handle interrupt_event;
+
+/**
+ * DSP_DSP::ConvertProcessAddressFromDspDram service function
+ * Inputs:
+ * 1 : Address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address)
+ */
+void ConvertProcessAddressFromDspDram(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 addr = cmd_buff[1];
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000);
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr);
+}
+
+/**
+ * DSP_DSP::LoadComponent service function
+ * Inputs:
+ * 1 : Size
+ * 2 : Unknown (observed only half word used)
+ * 3 : Unknown (observed only half word used)
+ * 4 : (size << 4) | 0xA
+ * 5 : Buffer address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Component loaded, 0 on not loaded, 1 on loaded
+ */
+void LoadComponent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
+
+ // TODO(bunnei): Implement real DSP firmware loading
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::GetSemaphoreEventHandle service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Semaphore event handle
+ */
+void GetSemaphoreEventHandle(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[3] = semaphore_event; // Event handle
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::RegisterInterruptEvents service function
+ * Inputs:
+ * 1 : Parameter 0 (purpose unknown)
+ * 2 : Parameter 1 (purpose unknown)
+ * 4 : Interrupt event handle
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void RegisterInterruptEvents(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ interrupt_event = static_cast<Handle>(cmd_buff[4]);
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::WriteReg0x10 service function
+ * Inputs:
+ * 1 : Unknown (observed only half word used)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void WriteReg0x10(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ Kernel::SignalEvent(interrupt_event);
+
+ cmd_buff[1] = 0; // No error
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::ReadPipeIfPossible service function
+ * Inputs:
+ * 1 : Unknown
+ * 2 : Unknown
+ * 3 : Size in bytes of read (observed only lower half word used)
+ * 0x41 : Virtual address to read from DSP pipe to in memory
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Number of bytes read from pipe
+ */
+void ReadPipeIfPossible(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
+ VAddr addr = cmd_buff[0x41];
+
+ // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
+ // TODO: Remove this hack :)
+ static const std::array<u16, 16> canned_read_pipe = {
+ 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
+ 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
+ };
+
+ u32 initial_size = read_pipe_count;
+
+ for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
+ if (read_pipe_count < canned_read_pipe.size()) {
+ Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
+ read_pipe_count++;
+ } else {
+ LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
+ break;
+ }
+ }
+
+ cmd_buff[1] = 0; // No error
+ cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr);
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, nullptr, "RecvData"},
- {0x00020040, nullptr, "RecvDataIsReady"},
- {0x00030080, nullptr, "SendData"},
- {0x00040040, nullptr, "SendDataIsEmpty"},
- {0x00070040, nullptr, "WriteReg0x10"},
- {0x00080000, nullptr, "GetSemaphore"},
- {0x00090040, nullptr, "ClearSemaphore"},
- {0x000B0000, nullptr, "CheckSemaphoreRequest"},
- {0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"},
- {0x000D0082, nullptr, "WriteProcessPipe"},
- {0x001000C0, nullptr, "ReadPipeIfPossible"},
- {0x001100C2, nullptr, "LoadComponent"},
- {0x00120000, nullptr, "UnloadComponent"},
- {0x00130082, nullptr, "FlushDataCache"},
- {0x00140082, nullptr, "InvalidateDCache "},
- {0x00150082, nullptr, "RegisterInterruptEvents"},
- {0x00160000, nullptr, "GetSemaphoreEventHandle"},
- {0x00170040, nullptr, "SetSemaphoreMask"},
- {0x00180040, nullptr, "GetPhysicalAddress"},
- {0x00190040, nullptr, "GetVirtualAddress" },
- {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
- {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
- {0x001C0082, nullptr, "SetIirFilterEQ"},
- {0x001F0000, nullptr, "GetHeadphoneStatus"},
- {0x00210000, nullptr, "GetIsDspOccupied"},
+ {0x00010040, nullptr, "RecvData"},
+ {0x00020040, nullptr, "RecvDataIsReady"},
+ {0x00030080, nullptr, "SendData"},
+ {0x00040040, nullptr, "SendDataIsEmpty"},
+ {0x00070040, WriteReg0x10, "WriteReg0x10"},
+ {0x00080000, nullptr, "GetSemaphore"},
+ {0x00090040, nullptr, "ClearSemaphore"},
+ {0x000B0000, nullptr, "CheckSemaphoreRequest"},
+ {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
+ {0x000D0082, nullptr, "WriteProcessPipe"},
+ {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
+ {0x001100C2, LoadComponent, "LoadComponent"},
+ {0x00120000, nullptr, "UnloadComponent"},
+ {0x00130082, nullptr, "FlushDataCache"},
+ {0x00140082, nullptr, "InvalidateDCache"},
+ {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"},
+ {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"},
+ {0x00170040, nullptr, "SetSemaphoreMask"},
+ {0x00180040, nullptr, "GetPhysicalAddress"},
+ {0x00190040, nullptr, "GetVirtualAddress"},
+ {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
+ {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
+ {0x001C0082, nullptr, "SetIirFilterEQ"},
+ {0x001F0000, nullptr, "GetHeadphoneStatus"},
+ {0x00210000, nullptr, "GetIsDspOccupied"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
+ semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
+ interrupt_event = 0;
+ read_pipe_count = 0;
+
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index c439ed26..9431b62f 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -19,8 +19,8 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
- return "dsp:DSP";
+ std::string GetPortName() const override {
+ return "dsp::DSP";
}
};
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 917b2f8c..785c351e 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -20,8 +20,8 @@ namespace ERR_F {
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
-
+
Interface::~Interface() {
}
-
+
} // namespace
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h
index 5da66326..6d7141c1 100644
--- a/src/core/hle/service/err_f.h
+++ b/src/core/hle/service/err_f.h
@@ -19,9 +19,9 @@ namespace ERR_F {
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "err:f";
}
};
-
+
} // namespace
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp
new file mode 100644
index 00000000..58023e53
--- /dev/null
+++ b/src/core/hle/service/frd_u.cpp
@@ -0,0 +1,35 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/frd_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_U
+
+namespace FRD_U {
+
+ const Interface::FunctionInfo FunctionTable[] = {
+ {0x00050000, nullptr, "GetFriendKey"},
+ {0x00080000, nullptr, "GetMyPresence"},
+ {0x00100040, nullptr, "GetPassword"},
+ {0x00190042, nullptr, "GetFriendFavoriteGame"},
+ {0x001A00C4, nullptr, "GetFriendInfo"},
+ {0x001B0080, nullptr, "IsOnFriendList"},
+ {0x001C0042, nullptr, "DecodeLocalFriendCode"},
+ {0x001D0002, nullptr, "SetCurrentlyPlayingText"},
+ {0x00320042, nullptr, "SetClientSdkVersion"}
+ };
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Interface class
+
+ Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+ }
+
+ Interface::~Interface() {
+ }
+
+} // namespace
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h
new file mode 100644
index 00000000..4020c666
--- /dev/null
+++ b/src/core/hle/service/frd_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FRD_U
+
+namespace FRD_U {
+
+ class Interface : public Service::Interface {
+ public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "frd:u";
+ }
+ };
+
+} // namespace
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
new file mode 100644
index 00000000..9c383473
--- /dev/null
+++ b/src/core/hle/service/fs/archive.cpp
@@ -0,0 +1,431 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <memory>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "common/file_util.h"
+#include "common/math_util.h"
+
+#include "core/file_sys/archive_savedata.h"
+#include "core/file_sys/archive_backend.h"
+#include "core/file_sys/archive_sdmc.h"
+#include "core/file_sys/directory_backend.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/kernel/session.h"
+#include "core/hle/result.h"
+
+// Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map.
+// Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
+namespace std {
+ template <>
+ struct hash<Service::FS::ArchiveIdCode> {
+ typedef Service::FS::ArchiveIdCode argument_type;
+ typedef std::size_t result_type;
+
+ result_type operator()(const argument_type& id_code) const {
+ typedef std::underlying_type<argument_type>::type Type;
+ return std::hash<Type>()(static_cast<Type>(id_code));
+ }
+ };
+}
+
+namespace Service {
+namespace FS {
+
+// Command to access archive file
+enum class FileCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ OpenSubFile = 0x08010100,
+ Read = 0x080200C2,
+ Write = 0x08030102,
+ GetSize = 0x08040000,
+ SetSize = 0x08050080,
+ GetAttributes = 0x08060000,
+ SetAttributes = 0x08070040,
+ Close = 0x08080000,
+ Flush = 0x08090000,
+};
+
+// Command to access directory
+enum class DirectoryCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ Read = 0x08010042,
+ Close = 0x08020000,
+};
+
+class Archive {
+public:
+ Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code)
+ : backend(std::move(backend)), id_code(id_code) {
+ }
+
+ std::string GetName() const { return "Archive: " + backend->GetName(); }
+
+ ArchiveIdCode id_code; ///< Id code of the archive
+ std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface
+};
+
+class File : public Kernel::Session {
+public:
+ File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path)
+ : backend(std::move(backend)), path(path) {
+ }
+
+ std::string GetName() const override { return "Path: " + path.DebugStr(); }
+
+ FileSys::Path path; ///< Path of the file
+ std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface
+
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from file...
+ case FileCommand::Read:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 address = cmd_buff[5];
+ LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
+ GetTypeName().c_str(), GetName().c_str(), offset, length, address);
+ cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
+ break;
+ }
+
+ // Write to file...
+ case FileCommand::Write:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 flush = cmd_buff[4];
+ u32 address = cmd_buff[6];
+ LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
+ GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
+ cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
+ break;
+ }
+
+ case FileCommand::GetSize:
+ {
+ LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
+ u64 size = backend->GetSize();
+ cmd_buff[2] = (u32)size;
+ cmd_buff[3] = size >> 32;
+ break;
+ }
+
+ case FileCommand::SetSize:
+ {
+ u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
+ LOG_TRACE(Service_FS, "SetSize %s %s size=%llu",
+ GetTypeName().c_str(), GetName().c_str(), size);
+ backend->SetSize(size);
+ break;
+ }
+
+ case FileCommand::Close:
+ {
+ LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
+ Kernel::g_object_pool.Destroy<File>(GetHandle());
+ break;
+ }
+
+ case FileCommand::Flush:
+ {
+ LOG_TRACE(Service_FS, "Flush");
+ backend->Flush();
+ break;
+ }
+
+ // Unknown command...
+ default:
+ LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
+ ResultCode error = UnimplementedFunction(ErrorModule::FS);
+ cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
+ return error;
+ }
+ cmd_buff[1] = 0; // No error
+ return MakeResult<bool>(false);
+ }
+};
+
+class Directory : public Kernel::Session {
+public:
+ Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path)
+ : backend(std::move(backend)), path(path) {
+ }
+
+ std::string GetName() const override { return "Directory: " + path.DebugStr(); }
+
+ FileSys::Path path; ///< Path of the directory
+ std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface
+
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from directory...
+ case DirectoryCommand::Read:
+ {
+ u32 count = cmd_buff[1];
+ u32 address = cmd_buff[3];
+ auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
+ LOG_TRACE(Service_FS, "Read %s %s: count=%d",
+ GetTypeName().c_str(), GetName().c_str(), count);
+
+ // Number of entries actually read
+ cmd_buff[2] = backend->Read(count, entries);
+ break;
+ }
+
+ case DirectoryCommand::Close:
+ {
+ LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
+ Kernel::g_object_pool.Destroy<Directory>(GetHandle());
+ break;
+ }
+
+ // Unknown command...
+ default:
+ LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
+ ResultCode error = UnimplementedFunction(ErrorModule::FS);
+ cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
+ return MakeResult<bool>(false);
+ }
+ cmd_buff[1] = 0; // No error
+ return MakeResult<bool>(false);
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Map of registered archives, identified by id code. Once an archive is registered here, it is
+ * never removed until the FS service is shut down.
+ */
+static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map;
+
+/**
+ * Map of active archive handles. Values are pointers to the archives in `idcode_map`.
+ */
+static std::unordered_map<ArchiveHandle, Archive*> handle_map;
+static ArchiveHandle next_handle;
+
+static Archive* GetArchive(ArchiveHandle handle) {
+ auto itr = handle_map.find(handle);
+ return (itr == handle_map.end()) ? nullptr : itr->second;
+}
+
+ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
+ LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code);
+
+ auto itr = id_code_map.find(id_code);
+ if (itr == id_code_map.end()) {
+ if (id_code == ArchiveIdCode::SaveData) {
+ // When a SaveData archive is created for the first time, it is not yet formatted
+ // and the save file/directory structure expected by the game has not yet been initialized.
+ // Returning the NotFormatted error code will signal the game to provision the SaveData archive
+ // with the files and folders that it expects.
+ // The FormatSaveData service call will create the SaveData archive when it is called.
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
+ // TODO: Verify error against hardware
+ return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Permanent);
+ }
+
+ // This should never even happen in the first place with 64-bit handles,
+ while (handle_map.count(next_handle) != 0) {
+ ++next_handle;
+ }
+ handle_map.emplace(next_handle, itr->second.get());
+ return MakeResult<ArchiveHandle>(next_handle++);
+}
+
+ResultCode CloseArchive(ArchiveHandle handle) {
+ if (handle_map.erase(handle) == 0)
+ return InvalidHandle(ErrorModule::FS);
+ else
+ return RESULT_SUCCESS;
+}
+
+// TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in
+// http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22
+ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) {
+ auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code));
+
+ bool inserted = result.second;
+ _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code");
+
+ auto& archive = result.first->second;
+ LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code);
+ return RESULT_SUCCESS;
+}
+
+ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
+ if (backend == nullptr) {
+ return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+ }
+
+ auto file = std::make_unique<File>(std::move(backend), path);
+ Handle handle = Kernel::g_object_pool.Create(file.release());
+ return MakeResult<Handle>(handle);
+}
+
+ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->DeleteFile(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
+ Archive* src_archive = GetArchive(src_archive_handle);
+ Archive* dest_archive = GetArchive(dest_archive_handle);
+ if (src_archive == nullptr || dest_archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (src_archive == dest_archive) {
+ if (src_archive->backend->RenameFile(src_path, dest_path))
+ return RESULT_SUCCESS;
+ } else {
+ // TODO: Implement renaming across archives
+ return UnimplementedFunction(ErrorModule::FS);
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->DeleteDirectory(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (archive->backend->CreateDirectory(path))
+ return RESULT_SUCCESS;
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::Canceled, ErrorLevel::Status);
+}
+
+ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
+ Archive* src_archive = GetArchive(src_archive_handle);
+ Archive* dest_archive = GetArchive(dest_archive_handle);
+ if (src_archive == nullptr || dest_archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ if (src_archive == dest_archive) {
+ if (src_archive->backend->RenameDirectory(src_path, dest_path))
+ return RESULT_SUCCESS;
+ } else {
+ // TODO: Implement renaming across archives
+ return UnimplementedFunction(ErrorModule::FS);
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+/**
+ * Open a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Opened Directory object
+ */
+ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
+ Archive* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return InvalidHandle(ErrorModule::FS);
+
+ std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path);
+ if (backend == nullptr) {
+ return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Permanent);
+ }
+
+ auto directory = std::make_unique<Directory>(std::move(backend), path);
+ Handle handle = Kernel::g_object_pool.Create(directory.release());
+ return MakeResult<Handle>(handle);
+}
+
+ResultCode FormatSaveData() {
+ // TODO(Subv): Actually wipe the savedata folder after creating or opening it
+
+ // Do not create the archive again if it already exists
+ if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
+
+ // Create the SaveData archive
+ std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
+ auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
+ Kernel::g_program_id);
+
+ if (savedata_archive->Initialize()) {
+ CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
+ return RESULT_SUCCESS;
+ } else {
+ LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
+ savedata_archive->GetMountPoint().c_str());
+ return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
+ }
+}
+
+/// Initialize archives
+void ArchiveInit() {
+ next_handle = 1;
+
+ // TODO(Link Mauve): Add the other archive types (see here for the known types:
+ // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
+ // archive type is SDMC, so it is the only one getting exposed.
+
+ std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
+ auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
+ if (sdmc_archive->Initialize())
+ CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
+ else
+ LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
+}
+
+/// Shutdown archives
+void ArchiveShutdown() {
+ handle_map.clear();
+ id_code_map.clear();
+}
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
new file mode 100644
index 00000000..a128276b
--- /dev/null
+++ b/src/core/hle/service/fs/archive.h
@@ -0,0 +1,125 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive_backend.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
+
+namespace Service {
+namespace FS {
+
+/// Supported archive types
+enum class ArchiveIdCode : u32 {
+ RomFS = 0x00000003,
+ SaveData = 0x00000004,
+ ExtSaveData = 0x00000006,
+ SharedExtSaveData = 0x00000007,
+ SystemSaveData = 0x00000008,
+ SDMC = 0x00000009,
+ SDMCWriteOnly = 0x0000000A,
+};
+
+typedef u64 ArchiveHandle;
+
+/**
+ * Opens an archive
+ * @param id_code IdCode of the archive to open
+ * @return Handle to the opened archive
+ */
+ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code);
+
+/**
+ * Closes an archive
+ * @param id_code IdCode of the archive to open
+ */
+ResultCode CloseArchive(ArchiveHandle handle);
+
+/**
+ * Creates an Archive
+ * @param backend File system backend interface to the archive
+ * @param id_code Id code used to access this type of archive
+ */
+ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code);
+
+/**
+ * Open a File from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the File inside of the Archive
+ * @param mode Mode under which to open the File
+ * @return Handle to the opened File object
+ */
+ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
+
+/**
+ * Delete a File from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the File inside of the Archive
+ * @return Whether deletion succeeded
+ */
+ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Rename a File between two Archives
+ * @param src_archive_handle Handle to the source Archive object
+ * @param src_path Path to the File inside of the source Archive
+ * @param dest_archive_handle Handle to the destination Archive object
+ * @param dest_path Path to the File inside of the destination Archive
+ * @return Whether rename succeeded
+ */
+ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
+
+/**
+ * Delete a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Whether deletion succeeded
+ */
+ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Create a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Whether creation of directory succeeded
+ */
+ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Rename a Directory between two Archives
+ * @param src_archive_handle Handle to the source Archive object
+ * @param src_path Path to the Directory inside of the source Archive
+ * @param dest_archive_handle Handle to the destination Archive object
+ * @param dest_path Path to the Directory inside of the destination Archive
+ * @return Whether rename succeeded
+ */
+ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
+ ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
+
+/**
+ * Open a Directory from an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @param path Path to the Directory inside of the Archive
+ * @return Handle to the opened File object
+ */
+ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
+
+/**
+ * Creates a blank SaveData archive.
+ * @return ResultCode 0 on success or the corresponding code on error
+ */
+ResultCode FormatSaveData();
+
+/// Initialize archives
+void ArchiveInit();
+
+/// Shutdown archives
+void ArchiveShutdown();
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
new file mode 100644
index 00000000..f99d84b2
--- /dev/null
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -0,0 +1,529 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common.h"
+#include "common/file_util.h"
+#include "common/scope_exit.h"
+#include "common/string_util.h"
+#include "core/hle/result.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/hle/service/fs/fs_user.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace FS_User
+
+namespace Service {
+namespace FS {
+
+static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) {
+ return (u64)low_word | ((u64)high_word << 32);
+}
+
+static void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
+ // http://3dbrew.org/wiki/FS:Initialize#Request
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_DEBUG(Service_FS, "called");
+}
+
+/**
+ * FS_User::OpenFile service function
+ * Inputs:
+ * 1 : Transaction
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Low path type
+ * 5 : Low path size
+ * 6 : Open flags
+ * 7 : Attributes
+ * 8 : (LowPathSize << 14) | 2
+ * 9 : Low path data pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : File handle
+ */
+static void OpenFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 filename_size = cmd_buff[5];
+ FileSys::Mode mode; mode.hex = cmd_buff[6];
+ u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
+ u32 filename_ptr = cmd_buff[9];
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes);
+
+ ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
+ }
+}
+
+/**
+ * FS_User::OpenFileDirectly service function
+ * Inputs:
+ * 1 : Transaction
+ * 2 : Archive ID
+ * 3 : Archive low path type
+ * 4 : Archive low path size
+ * 5 : File low path type
+ * 6 : File low path size
+ * 7 : Flags
+ * 8 : Attributes
+ * 9 : (ArchiveLowPathSize << 14) | 0x802
+ * 10 : Archive low path
+ * 11 : (FileLowPathSize << 14) | 2
+ * 12 : File low path
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : File handle
+ */
+static void OpenFileDirectly(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]);
+ auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
+ u32 archivename_size = cmd_buff[4];
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]);
+ u32 filename_size = cmd_buff[6];
+ FileSys::Mode mode; mode.hex = cmd_buff[7];
+ u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
+ u32 archivename_ptr = cmd_buff[10];
+ u32 filename_ptr = cmd_buff[12];
+ FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d",
+ archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
+
+ if (archive_path.GetType() != FileSys::Empty) {
+ LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
+ cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
+ cmd_buff[3] = 0;
+ return;
+ }
+
+ ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id);
+ if (archive_handle.Failed()) {
+ LOG_ERROR(Service_FS, "failed to get a handle for archive");
+ cmd_buff[1] = archive_handle.Code().raw;
+ cmd_buff[3] = 0;
+ return;
+ }
+ SCOPE_EXIT({ CloseArchive(*archive_handle); });
+
+ ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
+ }
+}
+
+/*
+ * FS_User::DeleteFile service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : File path string type
+ * 5 : File path string size
+ * 7 : File path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 filename_size = cmd_buff[5];
+ u32 filename_ptr = cmd_buff[7];
+
+ FileSys::Path file_path(filename_type, filename_size, filename_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
+ filename_type, filename_size, file_path.DebugStr().c_str());
+
+ cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw;
+}
+
+/*
+ * FS_User::RenameFile service function
+ * Inputs:
+ * 2 : Source archive handle lower word
+ * 3 : Source archive handle upper word
+ * 4 : Source file path type
+ * 5 : Source file path size
+ * 6 : Dest archive handle lower word
+ * 7 : Dest archive handle upper word
+ * 8 : Dest file path type
+ * 9 : Dest file path size
+ * 11: Source file path string data
+ * 13: Dest file path string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void RenameFile(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 src_filename_size = cmd_buff[5];
+ ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);;
+ auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
+ u32 dest_filename_size = cmd_buff[9];
+ u32 src_filename_ptr = cmd_buff[11];
+ u32 dest_filename_ptr = cmd_buff[13];
+
+ FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr);
+ FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr);
+
+ LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
+ src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(),
+ dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str());
+
+ cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw;
+}
+
+/*
+ * FS_User::DeleteDirectory service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Directory path string type
+ * 5 : Directory path string size
+ * 7 : Directory path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void DeleteDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 dirname_size = cmd_buff[5];
+ u32 dirname_ptr = cmd_buff[7];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
+ dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw;
+}
+
+/*
+ * FS_User::CreateDirectory service function
+ * Inputs:
+ * 2 : Archive handle lower word
+ * 3 : Archive handle upper word
+ * 4 : Directory path string type
+ * 5 : Directory path string size
+ * 8 : Directory path string data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void CreateDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 dirname_size = cmd_buff[5];
+ u32 dirname_ptr = cmd_buff[8];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw;
+}
+
+/*
+ * FS_User::RenameDirectory service function
+ * Inputs:
+ * 2 : Source archive handle lower word
+ * 3 : Source archive handle upper word
+ * 4 : Source dir path type
+ * 5 : Source dir path size
+ * 6 : Dest archive handle lower word
+ * 7 : Dest archive handle upper word
+ * 8 : Dest dir path type
+ * 9 : Dest dir path size
+ * 11: Source dir path string data
+ * 13: Dest dir path string
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void RenameDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
+ auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
+ u32 src_dirname_size = cmd_buff[5];
+ ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);
+ auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
+ u32 dest_dirname_size = cmd_buff[9];
+ u32 src_dirname_ptr = cmd_buff[11];
+ u32 dest_dirname_ptr = cmd_buff[13];
+
+ FileSys::Path src_dir_path(src_dirname_type, src_dirname_size, src_dirname_ptr);
+ FileSys::Path dest_dir_path(dest_dirname_type, dest_dirname_size, dest_dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
+ src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(),
+ dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str());
+
+ cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw;
+}
+
+/**
+ * FS_User::OpenDirectory service function
+ * Inputs:
+ * 1 : Archive handle low word
+ * 2 : Archive handle high word
+ * 3 : Low path type
+ * 4 : Low path size
+ * 7 : (LowPathSize << 14) | 2
+ * 8 : Low path data pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Directory handle
+ */
+static void OpenDirectory(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
+ u32 dirname_size = cmd_buff[4];
+ u32 dirname_ptr = cmd_buff[6];
+
+ FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
+
+ LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
+
+ ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[3] = *handle;
+ } else {
+ LOG_ERROR(Service_FS, "failed to get a handle for directory");
+ }
+}
+
+/**
+ * FS_User::OpenArchive service function
+ * Inputs:
+ * 1 : Archive ID
+ * 2 : Archive low path type
+ * 3 : Archive low path size
+ * 4 : (LowPathSize << 14) | 2
+ * 5 : Archive low path
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Archive handle lower word (unused)
+ * 3 : Archive handle upper word (same as file handle)
+ */
+static void OpenArchive(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
+ auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
+ u32 archivename_size = cmd_buff[3];
+ u32 archivename_ptr = cmd_buff[5];
+ FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
+
+ LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
+
+ if (archive_path.GetType() != FileSys::Empty) {
+ LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
+ cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
+ return;
+ }
+
+ ResultVal<ArchiveHandle> handle = OpenArchive(archive_id);
+ cmd_buff[1] = handle.Code().raw;
+ if (handle.Succeeded()) {
+ cmd_buff[2] = *handle & 0xFFFFFFFF;
+ cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF;
+ } else {
+ cmd_buff[2] = cmd_buff[3] = 0;
+ LOG_ERROR(Service_FS, "failed to get a handle for archive");
+ }
+}
+
+/**
+ * FS_User::CloseArchive service function
+ * Inputs:
+ * 0 : 0x080E0080
+ * 1 : Archive handle low word
+ * 2 : Archive handle high word
+ * Outputs:
+ * 0 : ??? TODO(yuriks): Verify return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void CloseArchive(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ cmd_buff[1] = CloseArchive(archive_handle).raw;
+}
+
+/*
+* FS_User::IsSdmcDetected service function
+* Outputs:
+* 1 : Result of function, 0 on success, otherwise error code
+* 2 : Whether the Sdmc could be detected
+*/
+static void IsSdmcDetected(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = 0;
+ cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
+
+ LOG_DEBUG(Service_FS, "called");
+}
+
+/**
+ * FS_User::FormatSaveData service function
+ * Inputs:
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void FormatSaveData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ LOG_DEBUG(Service_FS, "(STUBBED)");
+
+ // TODO(Subv): Find out what the inputs and outputs of this function are
+
+ cmd_buff[1] = FormatSaveData().raw;
+}
+
+/**
+ * FS_User::FormatThisUserSaveData service function
+ * Inputs:
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void FormatThisUserSaveData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ LOG_DEBUG(Service_FS, "(STUBBED)");
+
+ // TODO(Subv): Find out what the inputs and outputs of this function are
+
+ cmd_buff[1] = FormatSaveData().raw;
+}
+
+const FSUserInterface::FunctionInfo FunctionTable[] = {
+ {0x000100C6, nullptr, "Dummy1"},
+ {0x040100C4, nullptr, "Control"},
+ {0x08010002, Initialize, "Initialize"},
+ {0x080201C2, OpenFile, "OpenFile"},
+ {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
+ {0x08040142, DeleteFile, "DeleteFile"},
+ {0x08050244, RenameFile, "RenameFile"},
+ {0x08060142, DeleteDirectory, "DeleteDirectory"},
+ {0x08070142, nullptr, "DeleteDirectoryRecursively"},
+ {0x08080202, nullptr, "CreateFile"},
+ {0x08090182, CreateDirectory, "CreateDirectory"},
+ {0x080A0244, RenameDirectory, "RenameDirectory"},
+ {0x080B0102, OpenDirectory, "OpenDirectory"},
+ {0x080C00C2, OpenArchive, "OpenArchive"},
+ {0x080D0144, nullptr, "ControlArchive"},
+ {0x080E0080, CloseArchive, "CloseArchive"},
+ {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
+ {0x08100200, nullptr, "CreateSystemSaveData"},
+ {0x08110040, nullptr, "DeleteSystemSaveData"},
+ {0x08120080, nullptr, "GetFreeBytes"},
+ {0x08130000, nullptr, "GetCardType"},
+ {0x08140000, nullptr, "GetSdmcArchiveResource"},
+ {0x08150000, nullptr, "GetNandArchiveResource"},
+ {0x08160000, nullptr, "GetSdmcFatfsErro"},
+ {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
+ {0x08180000, nullptr, "IsSdmcWritable"},
+ {0x08190042, nullptr, "GetSdmcCid"},
+ {0x081A0042, nullptr, "GetNandCid"},
+ {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
+ {0x081C0000, nullptr, "GetNandSpeedInfo"},
+ {0x081D0042, nullptr, "GetSdmcLog"},
+ {0x081E0042, nullptr, "GetNandLog"},
+ {0x081F0000, nullptr, "ClearSdmcLog"},
+ {0x08200000, nullptr, "ClearNandLog"},
+ {0x08210000, nullptr, "CardSlotIsInserted"},
+ {0x08220000, nullptr, "CardSlotPowerOn"},
+ {0x08230000, nullptr, "CardSlotPowerOff"},
+ {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
+ {0x08250040, nullptr, "CardNorDirectCommand"},
+ {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
+ {0x08270082, nullptr, "CardNorDirectRead"},
+ {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
+ {0x08290082, nullptr, "CardNorDirectWrite"},
+ {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
+ {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
+ {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
+ {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
+ {0x082E0040, nullptr, "GetProductInfo"},
+ {0x082F0040, nullptr, "GetProgramLaunchInfo"},
+ {0x08300182, nullptr, "CreateExtSaveData"},
+ {0x08310180, nullptr, "CreateSharedExtSaveData"},
+ {0x08320102, nullptr, "ReadExtSaveDataIcon"},
+ {0x08330082, nullptr, "EnumerateExtSaveData"},
+ {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
+ {0x08350080, nullptr, "DeleteExtSaveData"},
+ {0x08360080, nullptr, "DeleteSharedExtSaveData"},
+ {0x08370040, nullptr, "SetCardSpiBaudRate"},
+ {0x08380040, nullptr, "SetCardSpiBusMode"},
+ {0x08390000, nullptr, "SendInitializeInfoTo9"},
+ {0x083A0100, nullptr, "GetSpecialContentIndex"},
+ {0x083B00C2, nullptr, "GetLegacyRomHeader"},
+ {0x083C00C2, nullptr, "GetLegacyBannerData"},
+ {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
+ {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
+ {0x083F00C0, nullptr, "GetExtDataBlockSize"},
+ {0x08400040, nullptr, "AbnegateAccessRight"},
+ {0x08410000, nullptr, "DeleteSdmcRoot"},
+ {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
+ {0x08430000, nullptr, "InitializeCtrFileSystem"},
+ {0x08440000, nullptr, "CreateSeed"},
+ {0x084500C2, nullptr, "GetFormatInfo"},
+ {0x08460102, nullptr, "GetLegacyRomHeader2"},
+ {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
+ {0x08480042, nullptr, "GetSdmcCtrRootPath"},
+ {0x08490040, nullptr, "GetArchiveResource"},
+ {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
+ {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
+ {0x084C0242, FormatSaveData, "FormatSaveData"},
+ {0x084D0102, nullptr, "GetLegacySubBannerData"},
+ {0x084E0342, nullptr, "UpdateSha256Context"},
+ {0x084F0102, nullptr, "ReadSpecialFile"},
+ {0x08500040, nullptr, "GetSpecialFileSize"},
+ {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
+ {0x08610042, nullptr, "InitializeWithSdkVersion"},
+ {0x08620040, nullptr, "SetPriority"},
+ {0x08630000, nullptr, "GetPriority"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+FSUserInterface::FSUserInterface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+FSUserInterface::~FSUserInterface() {
+}
+
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/fs/fs_user.h
index 00538254..80e3804e 100644
--- a/src/core/hle/service/fs_user.h
+++ b/src/core/hle/service/fs/fs_user.h
@@ -9,15 +9,16 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace FS_User
-namespace FS_User {
+namespace Service {
+namespace FS {
/// Interface to "fs:USER" service
-class Interface : public Service::Interface {
+class FSUserInterface : public Service::Interface {
public:
- Interface();
+ FSUserInterface();
- ~Interface();
+ ~FSUserInterface();
/**
* Gets the string port name used by CTROS for the service
@@ -28,4 +29,5 @@ public:
}
};
-} // namespace
+} // namespace FS
+} // namespace Service
diff --git a/src/core/hle/service/fs_user.cpp b/src/core/hle/service/fs_user.cpp
deleted file mode 100644
index 48d806e2..00000000
--- a/src/core/hle/service/fs_user.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common.h"
-
-#include "fs_user.h"
-#include "core/settings.h"
-#include "core/hle/kernel/archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Namespace FS_User
-
-namespace FS_User {
-
-// Command to access archive file
-enum class LowPathType : u32 {
- Invalid = 0,
- Empty = 1,
- Binary = 2,
- Char = 3,
- Wchar = 4
-};
-
-std::string GetStringFromCmdBuff(const u32 pointer, const u32 size) {
- auto data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
- return std::string(data, size - 1);
-}
-
-// We currently return 0 for success and -1 for failure in cmd_buff[1]. -1 was chosen because it
-// puts all the sections of the http://3dbrew.org/wiki/Error_codes to something non-zero, to make
-// sure we don't mislead the application into thinking something worked.
-
-void Initialize(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
- // http://3dbrew.org/wiki/FS:Initialize#Request
- cmd_buff[1] = 0;
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenFile(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- u32 transaction = cmd_buff[1];
- // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
- u32 size = cmd_buff[5];
- FileSys::Mode mode; mode.hex = cmd_buff[6];
- u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
- u32 pointer = cmd_buff[9];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "file LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string file_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d mode=%d attrs=%d data=%s", type, size, mode, attributes, file_name.c_str());
-
- Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenFileDirectly(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- u32 transaction = cmd_buff[1];
- auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]);
- LowPathType archive_type = static_cast<LowPathType>(cmd_buff[3]);
- u32 archive_size = cmd_buff[4];
- LowPathType file_type = static_cast<LowPathType>(cmd_buff[5]);
- u32 size = cmd_buff[6];
- FileSys::Mode mode; mode.hex = cmd_buff[7];
- u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
- u32 archive_pointer = cmd_buff[10];
- u32 pointer = cmd_buff[12];
-
- if (archive_type != LowPathType::Empty) {
- ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string archive_name = GetStringFromCmdBuff(archive_pointer, archive_size);
- std::string file_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "archive_type=%d archive_size=%d archive_data=%s "
- "file_type=%d file_size=%d file_mode=%d file_attrs=%d file_data=%s",
- archive_type, archive_size, archive_name.c_str(),
- file_type, size, mode, attributes, file_name.c_str());
-
- // TODO(Link Mauve): check if we should even get a handle for the archive, and don't leak it.
- Handle archive_handle = Kernel::OpenArchive(archive_id);
- if (archive_handle) {
- cmd_buff[1] = 0;
- // cmd_buff[2] isn't used according to 3dmoo's implementation.
- cmd_buff[3] = archive_handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- return;
- }
-
- if (file_type != LowPathType::Char) {
- WARN_LOG(KERNEL, "file LowPath type other than char is currently unsupported; returning archive handle instead");
- return;
- }
-
- Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-/*
- * FS_User::CreateDirectory service function
- * Inputs:
- * 2 : Archive handle lower word
- * 3 : Archive handle upper word
- * 4 : Directory path string type
- * 5 : Directory path string size
- * 8 : Directory path string data
- * Outputs:
- * 1 : Result of function, 0 on success, otherwise error code
- */
-void CreateDirectory(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
- u32 name_size = cmd_buff[5];
- u32 name_offset = cmd_buff[8];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string dir_name = GetStringFromCmdBuff(name_offset, name_size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, name_size, dir_name.c_str());
-
- cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_name);
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenDirectory(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
- // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
- Handle archive_handle = static_cast<Handle>(cmd_buff[2]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[3]);
- u32 size = cmd_buff[4];
- u32 pointer = cmd_buff[6];
-
- if (type != LowPathType::Char) {
- ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string dir_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, dir_name.c_str());
-
- Handle handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_name);
- if (handle) {
- cmd_buff[1] = 0;
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for directory %s", dir_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-void OpenArchive(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- auto arch_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]);
- LowPathType type = static_cast<LowPathType>(cmd_buff[2]);
- u32 size = cmd_buff[3];
- u32 pointer = cmd_buff[5];
-
- if (type != LowPathType::Empty) {
- ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
- cmd_buff[1] = -1;
- return;
- }
-
- std::string archive_name = GetStringFromCmdBuff(pointer, size);
-
- DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, archive_name.c_str());
-
- Handle handle = Kernel::OpenArchive(arch_id);
- if (handle) {
- cmd_buff[1] = 0;
- // cmd_buff[2] isn't used according to 3dmoo's implementation.
- cmd_buff[3] = handle;
- } else {
- ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
- // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
- cmd_buff[1] = -1;
- }
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-/*
-* FS_User::IsSdmcDetected service function
-* Outputs:
-* 1 : Result of function, 0 on success, otherwise error code
-* 2 : Whether the Sdmc could be detected
-*/
-void IsSdmcDetected(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
-
- cmd_buff[1] = 0;
- cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
-
- DEBUG_LOG(KERNEL, "called");
-}
-
-const Interface::FunctionInfo FunctionTable[] = {
- {0x000100C6, nullptr, "Dummy1"},
- {0x040100C4, nullptr, "Control"},
- {0x08010002, Initialize, "Initialize"},
- {0x080201C2, OpenFile, "OpenFile"},
- {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
- {0x08040142, nullptr, "DeleteFile"},
- {0x08050244, nullptr, "RenameFile"},
- {0x08060142, nullptr, "DeleteDirectory"},
- {0x08070142, nullptr, "DeleteDirectoryRecursively"},
- {0x08080202, nullptr, "CreateFile"},
- {0x08090182, CreateDirectory, "CreateDirectory"},
- {0x080A0244, nullptr, "RenameDirectory"},
- {0x080B0102, OpenDirectory, "OpenDirectory"},
- {0x080C00C2, OpenArchive, "OpenArchive"},
- {0x080D0144, nullptr, "ControlArchive"},
- {0x080E0080, nullptr, "CloseArchive"},
- {0x080F0180, nullptr, "FormatThisUserSaveData"},
- {0x08100200, nullptr, "CreateSystemSaveData"},
- {0x08110040, nullptr, "DeleteSystemSaveData"},
- {0x08120080, nullptr, "GetFreeBytes"},
- {0x08130000, nullptr, "GetCardType"},
- {0x08140000, nullptr, "GetSdmcArchiveResource"},
- {0x08150000, nullptr, "GetNandArchiveResource"},
- {0x08160000, nullptr, "GetSdmcFatfsErro"},
- {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
- {0x08180000, nullptr, "IsSdmcWritable"},
- {0x08190042, nullptr, "GetSdmcCid"},
- {0x081A0042, nullptr, "GetNandCid"},
- {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
- {0x081C0000, nullptr, "GetNandSpeedInfo"},
- {0x081D0042, nullptr, "GetSdmcLog"},
- {0x081E0042, nullptr, "GetNandLog"},
- {0x081F0000, nullptr, "ClearSdmcLog"},
- {0x08200000, nullptr, "ClearNandLog"},
- {0x08210000, nullptr, "CardSlotIsInserted"},
- {0x08220000, nullptr, "CardSlotPowerOn"},
- {0x08230000, nullptr, "CardSlotPowerOff"},
- {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
- {0x08250040, nullptr, "CardNorDirectCommand"},
- {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
- {0x08270082, nullptr, "CardNorDirectRead"},
- {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
- {0x08290082, nullptr, "CardNorDirectWrite"},
- {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
- {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
- {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
- {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
- {0x082E0040, nullptr, "GetProductInfo"},
- {0x082F0040, nullptr, "GetProgramLaunchInfo"},
- {0x08300182, nullptr, "CreateExtSaveData"},
- {0x08310180, nullptr, "CreateSharedExtSaveData"},
- {0x08320102, nullptr, "ReadExtSaveDataIcon"},
- {0x08330082, nullptr, "EnumerateExtSaveData"},
- {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
- {0x08350080, nullptr, "DeleteExtSaveData"},
- {0x08360080, nullptr, "DeleteSharedExtSaveData"},
- {0x08370040, nullptr, "SetCardSpiBaudRate"},
- {0x08380040, nullptr, "SetCardSpiBusMode"},
- {0x08390000, nullptr, "SendInitializeInfoTo9"},
- {0x083A0100, nullptr, "GetSpecialContentIndex"},
- {0x083B00C2, nullptr, "GetLegacyRomHeader"},
- {0x083C00C2, nullptr, "GetLegacyBannerData"},
- {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
- {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
- {0x083F00C0, nullptr, "GetExtDataBlockSize"},
- {0x08400040, nullptr, "AbnegateAccessRight"},
- {0x08410000, nullptr, "DeleteSdmcRoot"},
- {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
- {0x08430000, nullptr, "InitializeCtrFileSystem"},
- {0x08440000, nullptr, "CreateSeed"},
- {0x084500C2, nullptr, "GetFormatInfo"},
- {0x08460102, nullptr, "GetLegacyRomHeader2"},
- {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
- {0x08480042, nullptr, "GetSdmcCtrRootPath"},
- {0x08490040, nullptr, "GetArchiveResource"},
- {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
- {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
- {0x084C0242, nullptr, "FormatSaveData"},
- {0x084D0102, nullptr, "GetLegacySubBannerData"},
- {0x084E0342, nullptr, "UpdateSha256Context"},
- {0x084F0102, nullptr, "ReadSpecialFile"},
- {0x08500040, nullptr, "GetSpecialFileSize"},
- {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
- {0x08610042, nullptr, "InitializeWithSdkVersion"},
- {0x08620040, nullptr, "SetPriority"},
- {0x08630000, nullptr, "GetPriority"},
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interface class
-
-Interface::Interface() {
- Register(FunctionTable, ARRAY_SIZE(FunctionTable));
-}
-
-Interface::~Interface() {
-}
-
-} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 6119e630..db802714 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -28,41 +28,36 @@ u32 g_thread_id = 1; ///< Thread index into interrupt relay queue, 1
/// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(u32 thread_id) {
- if (0 == g_shared_memory)
- return nullptr;
-
- return Kernel::GetSharedMemoryPointer(g_shared_memory,
- 0x800 + (thread_id * sizeof(CommandBuffer)));
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer)));
+ return ptr.ValueOr(nullptr);
}
static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
- if (0 == g_shared_memory)
- return nullptr;
-
- _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
+ _dbg_assert_msg_(Service_GSP, screen_index < 2, "Invalid screen index");
// For each thread there are two FrameBufferUpdate fields
u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
- return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
+ return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr));
}
/// Gets a pointer to the interrupt relay queue for a given thread index
static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
- return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory,
- sizeof(InterruptRelayQueue) * thread_id);
+ ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id);
+ return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr));
}
-void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
+static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
// TODO: Return proper error codes
if (base_address + size_in_bytes >= 0x420000) {
- ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)",
+ LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)",
base_address, size_in_bytes);
return;
}
// size should be word-aligned
if ((size_in_bytes % 4) != 0) {
- ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes);
+ LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes);
return;
}
@@ -76,8 +71,8 @@ void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
}
/// Write a GSP GPU hardware register
-void WriteHWRegs(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void WriteHWRegs(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
@@ -87,20 +82,20 @@ void WriteHWRegs(Service::Interface* self) {
}
/// Read a GSP GPU hardware register
-void ReadHWRegs(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void ReadHWRegs(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 reg_addr = cmd_buff[1];
u32 size = cmd_buff[2];
// TODO: Return proper error codes
if (reg_addr + size >= 0x420000) {
- ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
+ LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
return;
}
// size should be word-aligned
if ((size % 4) != 0) {
- ERROR_LOG(GPU, "Invalid size 0x%08x", size);
+ LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size);
return;
}
@@ -115,7 +110,7 @@ void ReadHWRegs(Service::Interface* self) {
}
}
-void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
+static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
u32 base_address = 0x400000;
if (info.active_fb == 0) {
WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left1), 4, &info.address_left);
@@ -140,8 +135,8 @@ void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
* Outputs:
* 1: Result code
*/
-void SetBufferSwap(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void SetBufferSwap(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 screen_id = cmd_buff[1];
FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
SetBufferSwap(screen_id, *fb_info);
@@ -159,15 +154,16 @@ void SetBufferSwap(Service::Interface* self) {
* 2 : Thread index into GSP command buffer
* 4 : Handle to GSP shared memory
*/
-void RegisterInterruptRelayQueue(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void RegisterInterruptRelayQueue(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1];
g_interrupt_event = cmd_buff[3];
g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
_assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!");
- cmd_buff[2] = g_thread_id++; // ThreadID
+ cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init
+ cmd_buff[2] = g_thread_id++; // Thread ID
cmd_buff[4] = g_shared_memory; // GSP shared memory
Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct?
@@ -177,14 +173,15 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
* Signals that the specified interrupt type has occurred to userland code
* @param interrupt_id ID of interrupt that is being signalled
* @todo This should probably take a thread_id parameter and only signal this thread?
+ * @todo This probably does not belong in the GSP module, instead move to video_core
*/
void SignalInterrupt(InterruptId interrupt_id) {
if (0 == g_interrupt_event) {
- WARN_LOG(GSP, "cannot synchronize until GSP event has been created!");
+ LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
return;
}
if (0 == g_shared_memory) {
- WARN_LOG(GSP, "cannot synchronize until GSP shared memory has been created!");
+ LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
return;
}
for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -202,7 +199,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
}
/// Executes the next GSP command
-void ExecuteCommand(const Command& command, u32 thread_id) {
+static void ExecuteCommand(const Command& command, u32 thread_id) {
// Utility function to convert register ID to address
auto WriteGPURegister = [](u32 id, u32 data) {
GPU::Write<u32>(0x1EF00000 + 4 * id, data);
@@ -215,6 +212,7 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
memcpy(Memory::GetPointer(command.dma_request.dest_address),
Memory::GetPointer(command.dma_request.source_address),
command.dma_request.size);
+ SignalInterrupt(InterruptId::DMA);
break;
// ctrulib homebrew sends all relevant command list data with this command,
@@ -223,13 +221,13 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
case CommandId::SET_COMMAND_LIST_LAST:
{
auto& params = command.set_command_list_last;
+
WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3);
- WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3);
+ WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size);
// TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
- SignalInterrupt(InterruptId::P3D);
break;
}
@@ -247,6 +245,8 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
+
+ SignalInterrupt(InterruptId::PSC0);
break;
}
@@ -260,14 +260,9 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
- // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to
- // work well enough for running demos. Need to figure out how these all work and trigger
- // them correctly.
- SignalInterrupt(InterruptId::PSC0);
+ // TODO(bunnei): Determine if these interrupts should be signalled here.
SignalInterrupt(InterruptId::PSC1);
SignalInterrupt(InterruptId::PPF);
- SignalInterrupt(InterruptId::P3D);
- SignalInterrupt(InterruptId::DMA);
// Update framebuffer information if requested
for (int screen_id = 0; screen_id < 2; ++screen_id) {
@@ -303,12 +298,14 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
}
default:
- ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value());
+ LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value());
}
}
/// This triggers handling of the GX command written to the command buffer in shared memory.
-void TriggerCmdReqQueue(Service::Interface* self) {
+static void TriggerCmdReqQueue(Service::Interface* self) {
+
+ LOG_TRACE(Service_GSP, "called");
// Iterate through each thread's command queue...
for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -325,6 +322,9 @@ void TriggerCmdReqQueue(Service::Interface* self) {
command_buffer->number_commands = command_buffer->number_commands - 1;
}
}
+
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = 0; // No error
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
index 0eb32ba4..eb2d3596 100644
--- a/src/core/hle/service/hid_user.cpp
+++ b/src/core/hle/service/hid_user.cpp
@@ -34,10 +34,7 @@ static s16 next_circle_y = 0;
* Gets a pointer to the PadData structure inside HID shared memory
*/
static inline PadData* GetPadData() {
- if (0 == shared_mem)
- return nullptr;
-
- return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0));
+ return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr));
}
/**
@@ -47,7 +44,7 @@ static inline PadData* GetPadData() {
*
* Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
*/
-void UpdateNextCirclePadState() {
+static void UpdateNextCirclePadState() {
static const s16 max_value = 0x9C;
next_circle_x = next_state.circle_left ? -max_value : 0x0;
next_circle_x += next_state.circle_right ? max_value : 0x0;
@@ -58,7 +55,7 @@ void UpdateNextCirclePadState() {
/**
* Sets a Pad state (button or button combo) as pressed
*/
-void PadButtonPress(PadState pad_state) {
+void PadButtonPress(const PadState& pad_state) {
next_state.hex |= pad_state.hex;
UpdateNextCirclePadState();
}
@@ -66,7 +63,7 @@ void PadButtonPress(PadState pad_state) {
/**
* Sets a Pad state (button or button combo) as released
*/
-void PadButtonRelease(PadState pad_state) {
+void PadButtonRelease(const PadState& pad_state) {
next_state.hex &= ~pad_state.hex;
UpdateNextCirclePadState();
}
@@ -155,8 +152,8 @@ void PadUpdateComplete() {
* 7 : Gyroscope event
* 8 : Event signaled by HID_User
*/
-void GetIPCHandles(Service::Interface* self) {
- u32* cmd_buff = Service::GetCommandBuffer();
+static void GetIPCHandles(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
cmd_buff[3] = shared_mem;
@@ -166,7 +163,7 @@ void GetIPCHandles(Service::Interface* self) {
cmd_buff[7] = event_gyroscope;
cmd_buff[8] = event_debug_pad;
- DEBUG_LOG(KERNEL, "called");
+ LOG_TRACE(Service_HID, "called");
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h
index 9f6c4d5e..8f53befd 100644
--- a/src/core/hle/service/hid_user.h
+++ b/src/core/hle/service/hid_user.h
@@ -15,7 +15,7 @@
namespace HID_User {
-/**
+/**
* Structure of a Pad controller state.
*/
struct PadState {
@@ -93,8 +93,8 @@ const PadState PAD_CIRCLE_UP = {{1u << 30}};
const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
// Methods for updating the HID module's state
-void PadButtonPress(PadState pad_state);
-void PadButtonRelease(PadState pad_state);
+void PadButtonPress(const PadState& pad_state);
+void PadButtonRelease(const PadState& pad_state);
void PadUpdateComplete();
/**
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp
new file mode 100644
index 00000000..be15db23
--- /dev/null
+++ b/src/core/hle/service/ir_rst.cpp
@@ -0,0 +1,36 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ir_rst.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_RST
+
+namespace IR_RST {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010000, nullptr, "GetHandles"},
+ {0x00020080, nullptr, "Initialize"},
+ {0x00030000, nullptr, "Shutdown"},
+ {0x00040000, nullptr, "Unknown"},
+ {0x00050000, nullptr, "Unknown"},
+ {0x00060000, nullptr, "Unknown"},
+ {0x00070080, nullptr, "Unknown"},
+ {0x00080000, nullptr, "Unknown"},
+ {0x00090000, nullptr, "Unknown"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h
new file mode 100644
index 00000000..73effd7e
--- /dev/null
+++ b/src/core/hle/service/ir_rst.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_RST
+
+namespace IR_RST {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ir:rst";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp
new file mode 100644
index 00000000..aa9db6f6
--- /dev/null
+++ b/src/core/hle/service/ir_u.cpp
@@ -0,0 +1,45 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ir_u.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_U
+
+namespace IR_U {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010000, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Shutdown"},
+ {0x00030042, nullptr, "StartSendTransfer"},
+ {0x00040000, nullptr, "WaitSendTransfer"},
+ {0x000500C2, nullptr, "StartRecvTransfer"},
+ {0x00060000, nullptr, "WaitRecvTransfer"},
+ {0x00070080, nullptr, "GetRecvTransferCount"},
+ {0x00080000, nullptr, "GetSendState"},
+ {0x00090040, nullptr, "SetBitRate"},
+ {0x000A0000, nullptr, "GetBitRate"},
+ {0x000B0040, nullptr, "SetIRLEDState"},
+ {0x000C0000, nullptr, "GetIRLEDRecvState"},
+ {0x000D0000, nullptr, "GetSendFinishedEvent"},
+ {0x000E0000, nullptr, "GetRecvFinishedEvent"},
+ {0x000F0000, nullptr, "GetTransferState"},
+ {0x00100000, nullptr, "GetErrorStatus"},
+ {0x00110040, nullptr, "SetSleepModeActive"},
+ {0x00120040, nullptr, "SetSleepModeState"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h
new file mode 100644
index 00000000..86d98d07
--- /dev/null
+++ b/src/core/hle/service/ir_u.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace IR_U
+
+namespace IR_U {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ir:u";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
new file mode 100644
index 00000000..91b1a6fc
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -0,0 +1,28 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ldr_ro.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace LDR_RO
+
+namespace LDR_RO {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C2, nullptr, "Initialize"},
+ {0x00020082, nullptr, "CRR_Load"},
+ {0x00030042, nullptr, "CRR_Unload"},
+ {0x000402C2, nullptr, "CRO_LoadAndFix"},
+ {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"}
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro.h
new file mode 100644
index 00000000..32d7c29c
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace LDR_RO
+
+namespace LDR_RO {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "ldr:ro";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 58051f13..d6f30e9a 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000D0040, nullptr, "SetClamp"},
{0x000E0000, nullptr, "GetClamp"},
{0x000F0040, nullptr, "unknown_input1"},
- {0x00100040, nullptr, "unknown_input2"},
+ {0x00100040, nullptr, "unknown_input2"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index 72ba048e..2a495f3a 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "mic:u";
}
};
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp
new file mode 100644
index 00000000..04c1e0cf
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.cpp
@@ -0,0 +1,31 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/nim_aoc.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NIM_AOC
+
+namespace NIM_AOC {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00030042, nullptr, "SetApplicationId"},
+ {0x00040042, nullptr, "SetTin"},
+ {0x000902D0, nullptr, "ListContentSetsEx"},
+ {0x00180000, nullptr, "GetBalance"},
+ {0x001D0000, nullptr, "GetCustomerSupportCode"},
+ {0x00210000, nullptr, "Initialize"},
+ {0x00240282, nullptr, "CalculateContentsRequiredSize"},
+ {0x00250000, nullptr, "RefreshServerTime"},
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h
new file mode 100644
index 00000000..2cc67311
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace NIM_AOC
+
+namespace NIM_AOC {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "nim:aoc";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h
index a956ca81..69d2c200 100644
--- a/src/core/hle/service/nwm_uds.h
+++ b/src/core/hle/service/nwm_uds.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "nwm:UDS";
}
};
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
new file mode 100644
index 00000000..90e9b1bf
--- /dev/null
+++ b/src/core/hle/service/pm_app.cpp
@@ -0,0 +1,35 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/pm_app.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PM_APP
+
+namespace PM_APP {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010140, nullptr, "LaunchTitle"},
+ {0x00020082, nullptr, "LaunchFIRMSetParams"},
+ {0x00030080, nullptr, "TerminateProcesse"},
+ {0x00040100, nullptr, "TerminateProcessTID"},
+ {0x000500C0, nullptr, "TerminateProcessTID_unknown"},
+ {0x00070042, nullptr, "GetFIRMLaunchParams"},
+ {0x00080100, nullptr, "GetTitleExheaderFlags"},
+ {0x00090042, nullptr, "SetFIRMLaunchParams"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+Interface::~Interface() {
+}
+
+} // namespace
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h
new file mode 100644
index 00000000..28c38f58
--- /dev/null
+++ b/src/core/hle/service/pm_app.h
@@ -0,0 +1,27 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PM_APP
+
+namespace PM_APP {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+ ~Interface();
+ /**
+ * Gets the string port name used by CTROS for the service
+ * @return Port name of service
+ */
+ std::string GetPortName() const override {
+ return "pm:app";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index f6a14d50..b8c0f6da 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -11,19 +11,105 @@
namespace PTM_U {
+/// 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;
+
+ LOG_TRACE(Service_PTM, "PTM_U::GetShellState called");
+}
+
+/**
+ * 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, nullptr, "GetAdapterState"},
- {0x00060000, nullptr, "GetShellState "},
- {0x00070000, nullptr, "GetBatteryLevel"},
- {0x00080000, nullptr, "GetBatteryChargeState"},
+ {0x00050000, GetAdapterState, "GetAdapterState"},
+ {0x00060000, GetShellState, "GetShellState"},
+ {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
+ {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
{0x00090000, nullptr, "GetPedometerState"},
{0x000A0042, nullptr, "GetStepHistoryEntry"},
- {0x000B00C2, nullptr, "GetStepHistory "},
- {0x000C0000, nullptr, "GetTotalStepCount "},
+ {0x000B00C2, nullptr, "GetStepHistory"},
+ {0x000C0000, nullptr, "GetTotalStepCount"},
{0x000D0040, nullptr, "SetPedometerRecordingMode"},
{0x000E0000, nullptr, "GetPedometerRecordingMode"},
{0x000F0084, nullptr, "GetStepHistoryAll"},
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h
index 82749fa3..f8d9f57b 100644
--- a/src/core/hle/service/ptm_u.h
+++ b/src/core/hle/service/ptm_u.h
@@ -21,7 +21,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "ptm:u";
}
};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b144a77d..2230045e 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,16 +7,28 @@
#include "core/hle/service/service.h"
#include "core/hle/service/ac_u.h"
+#include "core/hle/service/am_app.h"
+#include "core/hle/service/am_net.h"
#include "core/hle/service/apt_u.h"
+#include "core/hle/service/boss_u.h"
+#include "core/hle/service/cecd_u.h"
+#include "core/hle/service/cfg_i.h"
#include "core/hle/service/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_user.h"
+#include "core/hle/service/fs/fs_user.h"
+#include "core/hle/service/frd_u.h"
#include "core/hle/service/gsp_gpu.h"
#include "core/hle/service/hid_user.h"
+#include "core/hle/service/ir_rst.h"
+#include "core/hle/service/ir_u.h"
+#include "core/hle/service/ldr_ro.h"
#include "core/hle/service/mic_u.h"
+#include "core/hle/service/nim_aoc.h"
#include "core/hle/service/ndm_u.h"
#include "core/hle/service/nwm_uds.h"
+#include "core/hle/service/pm_app.h"
#include "core/hle/service/ptm_u.h"
#include "core/hle/service/soc_u.h"
#include "core/hle/service/srv.h"
@@ -54,7 +66,7 @@ void Manager::DeleteService(const std::string& port_name) {
/// Get a Service Interface from its Handle
Interface* Manager::FetchFromHandle(Handle handle) {
- return Kernel::g_object_pool.GetFast<Interface>(handle);
+ return Kernel::g_object_pool.Get<Interface>(handle);
}
/// Get a Service Interface from its port
@@ -73,30 +85,42 @@ Interface* Manager::FetchFromPortName(const std::string& port_name) {
/// Initialize ServiceManager
void Init() {
g_manager = new Manager;
-
+
g_manager->AddService(new SRV::Interface);
g_manager->AddService(new AC_U::Interface);
+ g_manager->AddService(new AM_APP::Interface);
+ g_manager->AddService(new AM_NET::Interface);
g_manager->AddService(new APT_U::Interface);
+ g_manager->AddService(new BOSS_U::Interface);
+ g_manager->AddService(new CECD_U::Interface);
+ g_manager->AddService(new CFG_I::Interface);
g_manager->AddService(new CFG_U::Interface);
+ g_manager->AddService(new CSND_SND::Interface);
g_manager->AddService(new DSP_DSP::Interface);
g_manager->AddService(new ERR_F::Interface);
- g_manager->AddService(new FS_User::Interface);
+ g_manager->AddService(new FRD_U::Interface);
+ g_manager->AddService(new FS::FSUserInterface);
g_manager->AddService(new GSP_GPU::Interface);
g_manager->AddService(new HID_User::Interface);
+ g_manager->AddService(new IR_RST::Interface);
+ g_manager->AddService(new IR_U::Interface);
+ g_manager->AddService(new LDR_RO::Interface);
g_manager->AddService(new MIC_U::Interface);
+ g_manager->AddService(new NIM_AOC::Interface);
g_manager->AddService(new NDM_U::Interface);
g_manager->AddService(new NWM_UDS::Interface);
+ g_manager->AddService(new PM_APP::Interface);
g_manager->AddService(new PTM_U::Interface);
g_manager->AddService(new SOC_U::Interface);
g_manager->AddService(new SSL_C::Interface);
- NOTICE_LOG(HLE, "initialized OK");
+ LOG_DEBUG(Service, "initialized OK");
}
/// Shutdown ServiceManager
void Shutdown() {
delete g_manager;
- NOTICE_LOG(HLE, "shutdown OK");
+ LOG_DEBUG(Service, "shutdown OK");
}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2f5a866c..9cd90615 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -10,9 +10,11 @@
#include <string>
#include "common/common.h"
+#include "common/string_util.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/session.h"
#include "core/hle/svc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,30 +22,19 @@
namespace Service {
-static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters)
-static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
-
-/**
- * Returns a pointer to the command buffer in kernel memory
- * @param offset Optional offset into command buffer
- * @return Pointer to command buffer
- */
-inline static u32* GetCommandBuffer(const int offset=0) {
- return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
-}
+static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
class Manager;
/// Interface to a CTROS service
-class Interface : public Kernel::Object {
+class Interface : public Kernel::Session {
+ // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
+ // just something that encapsulates a session and acts as a helper to implement service
+ // processes.
+
friend class Manager;
public:
-
std::string GetName() const override { return GetPortName(); }
- std::string GetTypeName() const override { return GetPortName(); }
-
- static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
- Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; }
typedef void (*Function)(Interface*);
@@ -75,48 +66,31 @@ public:
m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
}
- /**
- * Synchronize kernel object
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result SyncRequest(bool* wait) override {
- u32* cmd_buff = GetCommandBuffer();
+ ResultVal<bool> SyncRequest() override {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
auto itr = m_functions.find(cmd_buff[0]);
- if (itr == m_functions.end()) {
- ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X",
- GetPortName().c_str(), cmd_buff[0]);
+ if (itr == m_functions.end() || itr->second.func == nullptr) {
+ // Number of params == bits 0-5 + bits 6-11
+ int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
- // TODO(bunnei): Hack - ignore error
- u32* cmd_buff = Service::GetCommandBuffer();
- cmd_buff[1] = 0;
- return 0;
- }
- if (itr->second.func == nullptr) {
- ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s",
- GetPortName().c_str(), itr->second.name.c_str());
+ std::string error = "unknown/unimplemented function '%s': port=%s";
+ for (int i = 1; i <= num_params; ++i) {
+ error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
+ }
+
+ std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
+
+ LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str());
// TODO(bunnei): Hack - ignore error
- u32* cmd_buff = Service::GetCommandBuffer();
cmd_buff[1] = 0;
- return 0;
- }
+ return MakeResult<bool>(false);
+ }
itr->second.func(this);
- return 0; // TODO: Implement return from actual function
- }
-
- /**
- * Wait for kernel object to synchronize
- * @param wait Boolean wait set if current thread should wait as a result of sync operation
- * @return Result of operation, 0 on success, otherwise error code
- */
- Result WaitSynchronization(bool* wait) override {
- // TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "unimplemented function");
- return 0;
+ return MakeResult<bool>(false); // TODO: Implement return from actual function
}
protected:
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
index e27a2b1f..d5590a68 100644
--- a/src/core/hle/service/soc_u.h
+++ b/src/core/hle/service/soc_u.h
@@ -19,7 +19,7 @@ public:
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
- std::string GetPortName() const {
+ std::string GetPortName() const override {
return "soc:U";
}
};
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 6c02a43d..165fd7aa 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -11,20 +11,20 @@
namespace SRV {
-Handle g_event_handle = 0;
+static Handle g_event_handle = 0;
-void Initialize(Service::Interface* self) {
- DEBUG_LOG(OSHLE, "called");
+static void Initialize(Service::Interface* self) {
+ LOG_DEBUG(Service_SRV, "called");
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
}
-void GetProcSemaphore(Service::Interface* self) {
- DEBUG_LOG(OSHLE, "called");
+static void GetProcSemaphore(Service::Interface* self) {
+ LOG_TRACE(Service_SRV, "called");
- u32* cmd_buff = Service::GetCommandBuffer();
+ u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(bunnei): Change to a semaphore once these have been implemented
g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event");
@@ -34,21 +34,21 @@ void GetProcSemaphore(Service::Interface* self) {
cmd_buff[3] = g_event_handle;
}
-void GetServiceHandle(Service::Interface* self) {
- Result res = 0;
- u32* cmd_buff = Service::GetCommandBuffer();
+static void GetServiceHandle(Service::Interface* self) {
+ ResultCode res = RESULT_SUCCESS;
+ u32* cmd_buff = Kernel::GetCommandBuffer();
std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
if (nullptr != service) {
cmd_buff[3] = service->GetHandle();
- DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
+ LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
} else {
- ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
- res = -1;
+ LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
+ res = UnimplementedFunction(ErrorModule::SRV);
}
- cmd_buff[1] = res;
+ cmd_buff[1] = res.raw;
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 2aa1303f..47e9bf77 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#include <map>
@@ -12,10 +12,12 @@
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/function_wrappers.h"
+#include "core/hle/result.h"
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -29,8 +31,8 @@ enum ControlMemoryOperation {
};
/// Map application or GSP heap memory
-Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
- DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
+static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
+ LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
operation, addr0, addr1, size, permissions);
switch (operation) {
@@ -42,19 +44,19 @@ Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 siz
// Map GSP heap memory
case MEMORY_OPERATION_GSP_HEAP:
- *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions);
+ *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions);
break;
// Unknown ControlMemory operation
default:
- ERROR_LOG(SVC, "unknown operation=0x%08X", operation);
+ LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
}
return 0;
}
/// Maps a memory block to specified address
-Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
- DEBUG_LOG(SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
+static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
+ LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
@@ -67,20 +69,20 @@ Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permis
case Kernel::MemoryPermission::WriteExecute:
case Kernel::MemoryPermission::ReadWriteExecute:
case Kernel::MemoryPermission::DontCare:
- Kernel::MapSharedMemory(handle, addr, permissions_type,
+ Kernel::MapSharedMemory(handle, addr, permissions_type,
static_cast<Kernel::MemoryPermission>(other_permissions));
break;
default:
- ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions);
+ LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
}
return 0;
}
/// Connect to an OS service given the port name, returns the handle to the port to out
-Result ConnectToPort(Handle* out, const char* port_name) {
+static Result ConnectToPort(Handle* out, const char* port_name) {
Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
- DEBUG_LOG(SVC, "called port_name=%s", port_name);
+ LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
_assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
*out = service->GetHandle();
@@ -89,78 +91,80 @@ Result ConnectToPort(Handle* out, const char* port_name) {
}
/// Synchronize to an OS service
-Result SendSyncRequest(Handle handle) {
- Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+static Result SendSyncRequest(Handle handle) {
+ Kernel::Session* session = Kernel::g_object_pool.Get<Kernel::Session>(handle);
+ if (session == nullptr) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
- _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
- DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str());
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
- bool wait = false;
- Result res = object->SyncRequest(&wait);
- if (wait) {
+ ResultVal<bool> wait = session->SyncRequest();
+ if (wait.Succeeded() && *wait) {
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
}
- return res;
+ return wait.Code().raw;
}
/// Close a handle
-Result CloseHandle(Handle handle) {
+static Result CloseHandle(Handle handle) {
// ImplementMe
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
return 0;
}
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
-Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
+static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
- bool wait = false;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
+ if (!Kernel::g_object_pool.IsValid(handle)) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
+ _dbg_assert_(Kernel, object != nullptr);
- DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
object->GetName().c_str(), nano_seconds);
- _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!");
-
- Result res = object->WaitSynchronization(&wait);
+ ResultVal<bool> wait = object->WaitSynchronization();
// Check for next thread to schedule
- if (wait) {
+ if (wait.Succeeded() && *wait) {
HLE::Reschedule(__func__);
- return 0;
}
- return res;
+ return wait.Code().raw;
}
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
+static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
s64 nano_seconds) {
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
bool unlock_all = true;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
- DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
+ LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
handle_count, (wait_all ? "true" : "false"), nano_seconds);
// Iterate through each handle, synchronize kernel object
for (s32 i = 0; i < handle_count; i++) {
- bool wait = false;
+ if (!Kernel::g_object_pool.IsValid(handles[i])) {
+ return InvalidHandle(ErrorModule::Kernel).raw;
+ }
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
- _assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object "
- "is nullptr!", handles[i]);
-
- DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
+ LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
object->GetName().c_str());
- Result res = object->WaitSynchronization(&wait);
+ // TODO(yuriks): Verify how the real function behaves when an error happens here
+ ResultVal<bool> wait_result = object->WaitSynchronization();
+ bool wait = wait_result.Succeeded() && *wait_result;
if (!wait && !wait_all) {
*out = i;
- return 0;
+ return RESULT_SUCCESS.raw;
} else {
unlock_all = false;
}
@@ -168,55 +172,57 @@ Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wa
if (wait_all && unlock_all) {
*out = handle_count;
- return 0;
+ return RESULT_SUCCESS.raw;
}
// Check for next thread to schedule
HLE::Reschedule(__func__);
- return 0;
+ return RESULT_SUCCESS.raw;
}
/// Create an address arbiter (to allocate access to shared resources)
-Result CreateAddressArbiter(u32* arbiter) {
- DEBUG_LOG(SVC, "called");
+static Result CreateAddressArbiter(u32* arbiter) {
+ LOG_TRACE(Kernel_SVC, "called");
Handle handle = Kernel::CreateAddressArbiter();
*arbiter = handle;
return 0;
}
/// Arbitrate address
-Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
- return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address,
- value);
+static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter,
+ address, type, value);
+ return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
+ address, value).raw;
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
-void OutputDebugString(const char* string) {
- OS_LOG(SVC, "%s", string);
+static void OutputDebugString(const char* string) {
+ LOG_DEBUG(Debug_Emulated, "%s", string);
}
/// Get resource limit
-Result GetResourceLimit(Handle* resource_limit, Handle process) {
+static Result GetResourceLimit(Handle* resource_limit, Handle process) {
// With regards to proceess values:
- // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
+ // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
// the current KThread.
*resource_limit = 0xDEADBEEF;
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
return 0;
}
/// Get resource limit current values
-Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
+static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
s32 name_count) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
resource_limit, names, name_count);
Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
return 0;
}
/// Creates a new thread
-Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
std::string name;
if (Symbols::HasSymbol(entry_point)) {
TSymbol symbol = Symbols::GetSymbol(entry_point);
@@ -230,18 +236,18 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p
Core::g_app_core->SetReg(1, thread);
- DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
- "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
+ LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
+ "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
name.c_str(), arg, stack_top, priority, processor_id, thread);
-
+
return 0;
}
/// Called when a thread exits
-u32 ExitThread() {
+static u32 ExitThread() {
Handle thread = Kernel::GetCurrentThreadHandle();
- DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
+ LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
Kernel::StopThread(thread, __func__);
HLE::Reschedule(__func__);
@@ -249,55 +255,73 @@ u32 ExitThread() {
}
/// Gets the priority for the specified thread
-Result GetThreadPriority(s32* priority, Handle handle) {
- *priority = Kernel::GetThreadPriority(handle);
- return 0;
+static Result GetThreadPriority(s32* priority, Handle handle) {
+ ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle);
+ if (priority_result.Succeeded()) {
+ *priority = *priority_result;
+ }
+ return priority_result.Code().raw;
}
/// Sets the priority for the specified thread
-Result SetThreadPriority(Handle handle, s32 priority) {
- return Kernel::SetThreadPriority(handle, priority);
+static Result SetThreadPriority(Handle handle, s32 priority) {
+ return Kernel::SetThreadPriority(handle, priority).raw;
}
/// Create a mutex
-Result CreateMutex(Handle* mutex, u32 initial_locked) {
+static Result CreateMutex(Handle* mutex, u32 initial_locked) {
*mutex = Kernel::CreateMutex((initial_locked != 0));
- DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X",
+ LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
initial_locked ? "true" : "false", *mutex);
return 0;
}
/// Release a mutex
-Result ReleaseMutex(Handle handle) {
- DEBUG_LOG(SVC, "called handle=0x%08X", handle);
- _assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!");
- Kernel::ReleaseMutex(handle);
- return 0;
+static Result ReleaseMutex(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle);
+ ResultCode res = Kernel::ReleaseMutex(handle);
+ return res.raw;
}
-/// Get current thread ID
-Result GetThreadId(u32* thread_id, Handle thread) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread);
- return 0;
+/// Get the ID for the specified thread.
+static Result GetThreadId(u32* thread_id, Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
+ ResultCode result = Kernel::GetThreadId(thread_id, handle);
+ return result.raw;
+}
+
+/// Creates a semaphore
+static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) {
+ ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count);
+ LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
+ initial_count, max_count, *semaphore);
+ return res.raw;
+}
+
+/// Releases a certain number of slots in a semaphore
+static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) {
+ LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore);
+ ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count);
+ return res.raw;
}
/// Query memory
-Result QueryMemory(void* info, void* out, u32 addr) {
- ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
+static Result QueryMemory(void* info, void* out, u32 addr) {
+ LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
return 0;
}
/// Create an event
-Result CreateEvent(Handle* evt, u32 reset_type) {
+static Result CreateEvent(Handle* evt, u32 reset_type) {
*evt = Kernel::CreateEvent((ResetType)reset_type);
- DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X",
+ LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
reset_type, *evt);
return 0;
}
/// Duplicates a kernel handle
-Result DuplicateHandle(Handle* out, Handle handle) {
- DEBUG_LOG(SVC, "called handle=0x%08X", handle);
+static Result DuplicateHandle(Handle* out, Handle handle) {
+ LOG_WARNING(Kernel_SVC, "(STUBBED) called handle=0x%08X", handle);
// Translate kernel handles -> real handles
if (handle == Kernel::CurrentThread) {
@@ -305,7 +329,7 @@ Result DuplicateHandle(Handle* out, Handle handle) {
}
_assert_msg_(KERNEL, (handle != Kernel::CurrentProcess),
"(UNIMPLEMENTED) process handle duplication!");
-
+
// TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate.
*out = handle;
@@ -313,26 +337,27 @@ Result DuplicateHandle(Handle* out, Handle handle) {
}
/// Signals an event
-Result SignalEvent(Handle evt) {
- Result res = Kernel::SignalEvent(evt);
- DEBUG_LOG(SVC, "called event=0x%08X", evt);
- return res;
+static Result SignalEvent(Handle evt) {
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
+ return Kernel::SignalEvent(evt).raw;
}
/// Clears an event
-Result ClearEvent(Handle evt) {
- Result res = Kernel::ClearEvent(evt);
- DEBUG_LOG(SVC, "called event=0x%08X", evt);
- return res;
+static Result ClearEvent(Handle evt) {
+ LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
+ return Kernel::ClearEvent(evt).raw;
}
/// Sleep the current thread
-void SleepThread(s64 nanoseconds) {
- DEBUG_LOG(SVC, "called nanoseconds=%lld", nanoseconds);
+static void SleepThread(s64 nanoseconds) {
+ LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
+
+ // Check for next thread to schedule
+ HLE::Reschedule(__func__);
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
-s64 GetSystemTick() {
+static s64 GetSystemTick() {
return (s64)Core::g_app_core->GetTicks();
}
@@ -358,8 +383,8 @@ const HLE::FunctionDef SVC_Table[] = {
{0x12, nullptr, "Run"},
{0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
{0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
- {0x15, nullptr, "CreateSemaphore"},
- {0x16, nullptr, "ReleaseSemaphore"},
+ {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"},
+ {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"},
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 1d125faf..6be393d0 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -1,6 +1,6 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
-// Refer to the license.txt file included.
+// Refer to the license.txt file included.
#pragma once
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 3ad801c6..da78b85e 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -35,7 +35,7 @@ inline void Read(T &var, const u32 raw_addr) {
// Reads other than u32 are untested, so I'd rather have them abort than silently fail
if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
- ERROR_LOG(GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
+ LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
return;
}
@@ -49,7 +49,7 @@ inline void Write(u32 addr, const T data) {
// Writes other than u32 are untested, so I'd rather have them abort than silently fail
if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
- ERROR_LOG(GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
+ LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
return;
}
@@ -73,7 +73,7 @@ inline void Write(u32 addr, const T data) {
for (u32* ptr = start; ptr < end; ++ptr)
*ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
- DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
+ LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
}
break;
}
@@ -105,7 +105,7 @@ inline void Write(u32 addr, const T data) {
}
default:
- ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value());
+ LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value());
break;
}
@@ -132,16 +132,16 @@ inline void Write(u32 addr, const T data) {
}
default:
- ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
+ LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
break;
}
}
}
- DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
+ LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
config.output_height * config.output_width * 4,
- config.GetPhysicalInputAddress(), config.input_width, config.input_height,
- config.GetPhysicalOutputAddress(), config.output_width, config.output_height,
+ config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
+ config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height,
config.output_format.Value());
}
break;
@@ -154,8 +154,7 @@ inline void Write(u32 addr, const T data) {
if (config.trigger & 1)
{
u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress()));
- u32 size = config.size << 3;
- Pica::CommandProcessor::ProcessCommandList(buffer, size);
+ Pica::CommandProcessor::ProcessCommandList(buffer, config.size);
}
break;
}
@@ -252,12 +251,12 @@ void Init() {
framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
framebuffer_sub.active_fb = 0;
- NOTICE_LOG(GPU, "initialized OK");
+ LOG_DEBUG(HW_GPU, "initialized OK");
}
/// Shutdown hardware
void Shutdown() {
- NOTICE_LOG(GPU, "shutdown OK");
+ LOG_DEBUG(HW_GPU, "shutdown OK");
}
} // namespace
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 3fa7b9cc..86cd5e68 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -169,7 +169,7 @@ struct Regs {
INSERT_PADDING_WORDS(0x331);
struct {
- // command list size
+ // command list size (in bytes)
u32 size;
INSERT_PADDING_WORDS(0x1);
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 33f75c50..af42b41f 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -6,7 +6,6 @@
#include "core/hw/hw.h"
#include "core/hw/gpu.h"
-#include "core/hw/ndma.h"
namespace HW {
@@ -39,36 +38,26 @@ enum {
template <typename T>
inline void Read(T &var, const u32 addr) {
switch (addr & 0xFFFFF000) {
-
- // TODO(bunnei): What is the virtual address of NDMA?
- // case VADDR_NDMA:
- // NDMA::Read(var, addr);
- // break;
case VADDR_GPU:
GPU::Read(var, addr);
break;
default:
- ERROR_LOG(HW, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
+ LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
}
}
template <typename T>
inline void Write(u32 addr, const T data) {
switch (addr & 0xFFFFF000) {
-
- // TODO(bunnei): What is the virtual address of NDMA?
- // case VADDR_NDMA
- // NDMA::Write(addr, data);
- // break;
case VADDR_GPU:
GPU::Write(addr, data);
break;
default:
- ERROR_LOG(HW, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
+ LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
}
}
@@ -87,19 +76,17 @@ template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
void Update() {
GPU::Update();
- NDMA::Update();
}
/// Initialize hardware
void Init() {
GPU::Init();
- NDMA::Init();
- NOTICE_LOG(HW, "initialized OK");
+ LOG_DEBUG(HW, "initialized OK");
}
/// Shutdown hardware
void Shutdown() {
- NOTICE_LOG(HW, "shutdown OK");
+ LOG_DEBUG(HW, "shutdown OK");
}
} \ No newline at end of file
diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp
deleted file mode 100644
index e29a773f..00000000
--- a/src/core/hw/ndma.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#include "common/common_types.h"
-
-#include "core/hw/ndma.h"
-
-namespace NDMA {
-
-template <typename T>
-inline void Read(T &var, const u32 addr) {
- ERROR_LOG(NDMA, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
-}
-
-template <typename T>
-inline void Write(u32 addr, const T data) {
- ERROR_LOG(NDMA, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
-}
-
-// Explicitly instantiate template functions because we aren't defining this in the header:
-
-template void Read<u64>(u64 &var, const u32 addr);
-template void Read<u32>(u32 &var, const u32 addr);
-template void Read<u16>(u16 &var, const u32 addr);
-template void Read<u8>(u8 &var, const u32 addr);
-
-template void Write<u64>(u32 addr, const u64 data);
-template void Write<u32>(u32 addr, const u32 data);
-template void Write<u16>(u32 addr, const u16 data);
-template void Write<u8>(u32 addr, const u8 data);
-
-/// Update hardware
-void Update() {
-}
-
-/// Initialize hardware
-void Init() {
- NOTICE_LOG(GPU, "initialized OK");
-}
-
-/// Shutdown hardware
-void Shutdown() {
- NOTICE_LOG(GPU, "shutdown OK");
-}
-
-} // namespace
diff --git a/src/core/hw/ndma.h b/src/core/hw/ndma.h
deleted file mode 100644
index d8fa3d40..00000000
--- a/src/core/hw/ndma.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-
-namespace NDMA {
-
-template <typename T>
-inline void Read(T &var, const u32 addr);
-
-template <typename T>
-inline void Write(u32 addr, const T data);
-
-/// Update hardware
-void Update();
-
-/// Initialize hardware
-void Init();
-
-/// Shutdown hardware
-void Shutdown();
-
-} // namespace
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
new file mode 100644
index 00000000..0437e537
--- /dev/null
+++ b/src/core/loader/3dsx.cpp
@@ -0,0 +1,236 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <vector>
+
+#include "core/file_sys/archive_romfs.h"
+#include "core/loader/elf.h"
+#include "core/loader/ncch.h"
+#include "core/hle/service/fs/archive.h"
+#include "core/mem_map.h"
+
+#include "3dsx.h"
+
+
+namespace Loader {
+
+
+/**
+ * File layout:
+ * - File header
+ * - Code, rodata and data relocation table headers
+ * - Code segment
+ * - Rodata segment
+ * - Loadable (non-BSS) part of the data segment
+ * - Code relocation table
+ * - Rodata relocation table
+ * - Data relocation table
+ *
+ * Memory layout before relocations are applied:
+ * [0..codeSegSize) -> code segment
+ * [codeSegSize..rodataSegSize) -> rodata segment
+ * [rodataSegSize..dataSegSize) -> data segment
+ *
+ * Memory layout after relocations are applied: well, however the loader sets it up :)
+ * The entrypoint is always the start of the code segment.
+ * The BSS section must be cleared manually by the application.
+ */
+enum THREEDSX_Error {
+ ERROR_NONE = 0,
+ ERROR_READ = 1,
+ ERROR_FILE = 2,
+ ERROR_ALLOC = 3
+};
+static const u32 RELOCBUFSIZE = 512;
+
+// File header
+static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
+#pragma pack(1)
+struct THREEDSX_Header
+{
+ u32 magic;
+ u16 header_size, reloc_hdr_size;
+ u32 format_ver;
+ u32 flags;
+
+ // Sizes of the code, rodata and data segments +
+ // size of the BSS section (uninitialized latter half of the data segment)
+ u32 code_seg_size, rodata_seg_size, data_seg_size, bss_size;
+};
+
+// Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts.
+struct THREEDSX_RelocHdr
+{
+ // # of absolute relocations (that is, fix address to post-relocation memory layout)
+ u32 cross_segment_absolute;
+ // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
+ u32 cross_segment_relative;
+ // more?
+
+ // Relocations are written in this order:
+ // - Absolute relocations
+ // - Relative relocations
+};
+
+// Relocation entry: from the current pointer, skip X words and patch Y words
+struct THREEDSX_Reloc
+{
+ u16 skip, patch;
+};
+#pragma pack()
+
+struct THREEloadinfo
+{
+ u8* seg_ptrs[3]; // code, rodata & data
+ u32 seg_addrs[3];
+ u32 seg_sizes[3];
+};
+
+class THREEDSXReader {
+public:
+ static int Load3DSXFile(const std::string& filename, u32 base_addr);
+};
+
+static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets)
+{
+ if (addr < offsets[0])
+ return loadinfo->seg_addrs[0] + addr;
+ if (addr < offsets[1])
+ return loadinfo->seg_addrs[1] + addr - offsets[0];
+ return loadinfo->seg_addrs[2] + addr - offsets[1];
+}
+
+int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
+{
+ FileUtil::IOFile file(filename, "rb");
+ if (!file.IsOpen()) {
+ return ERROR_FILE;
+ }
+ THREEDSX_Header hdr;
+ if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr))
+ return ERROR_READ;
+
+ THREEloadinfo loadinfo;
+ //loadinfo segments must be a multiple of 0x1000
+ loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) &~0xFFF;
+ loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF;
+ loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF;
+ u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] };
+ u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF;
+ u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size;
+ u32 n_reloc_tables = hdr.reloc_hdr_size / 4;
+ std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables);
+
+ loadinfo.seg_addrs[0] = base_addr;
+ loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0];
+ loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1];
+ loadinfo.seg_ptrs[0] = &all_mem[0];
+ loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0];
+ loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1];
+
+ // Skip header for future compatibility
+ file.Seek(hdr.header_size, SEEK_SET);
+
+ // Read the relocation headers
+ u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size);
+
+ for (u32 current_segment = 0; current_segment < 3; current_segment++) {
+ if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4)
+ return ERROR_READ;
+ }
+
+ // Read the segments
+ if (file.ReadBytes(loadinfo.seg_ptrs[0], hdr.code_seg_size) != hdr.code_seg_size)
+ return ERROR_READ;
+ if (file.ReadBytes(loadinfo.seg_ptrs[1], hdr.rodata_seg_size) != hdr.rodata_seg_size)
+ return ERROR_READ;
+ if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) != hdr.data_seg_size - hdr.bss_size)
+ return ERROR_READ;
+
+ // BSS clear
+ memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size);
+
+ // Relocate the segments
+ for (u32 current_segment = 0; current_segment < 3; current_segment++) {
+ for (u32 current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) {
+ u32 n_relocs = relocs[current_segment*n_reloc_tables + current_segment_reloc_table];
+ if (current_segment_reloc_table >= 2) {
+ // We are not using this table - ignore it because we don't know what it dose
+ file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR);
+ continue;
+ }
+ static THREEDSX_Reloc reloc_table[RELOCBUFSIZE];
+
+ u32* pos = (u32*)loadinfo.seg_ptrs[current_segment];
+ u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4);
+
+ while (n_relocs) {
+ u32 remaining = std::min(RELOCBUFSIZE, n_relocs);
+ n_relocs -= remaining;
+
+ if (file.ReadBytes(reloc_table, remaining*sizeof(THREEDSX_Reloc)) != remaining*sizeof(THREEDSX_Reloc))
+ return ERROR_READ;
+
+ for (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
+ LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n",
+ current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch);
+ pos += reloc_table[current_inprogress].skip;
+ s32 num_patches = reloc_table[current_inprogress].patch;
+ while (0 < num_patches && pos < end_pos) {
+ u32 in_addr = (char*)pos - (char*)&all_mem[0];
+ u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
+ LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n",
+ base_addr + in_addr, addr, current_segment_reloc_table, *pos);
+ switch (current_segment_reloc_table) {
+ case 0: *pos = (addr); break;
+ case 1: *pos = (addr - in_addr); break;
+ default: break; //this should never happen
+ }
+ pos++;
+ num_patches--;
+ }
+ }
+ }
+ }
+ }
+
+ // Write the data
+ memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]);
+
+ LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000);
+ LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000);
+ LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000);
+ LOG_DEBUG(Loader, "BSS: %u pages\n", bss_load_size / 0x1000);
+
+ return ERROR_NONE;
+}
+
+ /// AppLoader_DSX constructor
+ AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) {
+ }
+
+ /// AppLoader_DSX destructor
+ AppLoader_THREEDSX::~AppLoader_THREEDSX() {
+ }
+
+ /**
+ * Loads a 3DSX file
+ * @return Success on success, otherwise Error
+ */
+ ResultStatus AppLoader_THREEDSX::Load() {
+ LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str());
+ FileUtil::IOFile file(filename, "rb");
+ if (file.IsOpen()) {
+
+ THREEDSXReader reader;
+ reader.Load3DSXFile(filename, 0x00100000);
+ Kernel::LoadExec(0x00100000);
+ } else {
+ return ResultStatus::Error;
+ }
+ return ResultStatus::Success;
+ }
+
+} // namespace Loader
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
new file mode 100644
index 00000000..848d3ef8
--- /dev/null
+++ b/src/core/loader/3dsx.h
@@ -0,0 +1,32 @@
+// Copyright 2014 Dolphin Emulator Project / Citra Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Loader namespace
+
+namespace Loader {
+
+/// Loads an 3DSX file
+class AppLoader_THREEDSX final : public AppLoader {
+public:
+ AppLoader_THREEDSX(const std::string& filename);
+ ~AppLoader_THREEDSX() override;
+
+ /**
+ * Load the bootable file
+ * @return ResultStatus result of function
+ */
+ ResultStatus Load() override;
+
+private:
+ std::string filename;
+ bool is_loaded;
+};
+
+} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 389d5a8c..c95882f4 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -254,18 +254,18 @@ const char *ElfReader::GetSectionName(int section) const {
}
bool ElfReader::LoadInto(u32 vaddr) {
- DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx);
+ LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx);
// Should we relocate?
relocate = (header->e_type != ET_EXEC);
if (relocate) {
- DEBUG_LOG(MASTER_LOG, "Relocatable module");
+ LOG_DEBUG(Loader, "Relocatable module");
entryPoint += vaddr;
} else {
- DEBUG_LOG(MASTER_LOG, "Prerelocated executable");
+ LOG_DEBUG(Loader, "Prerelocated executable");
}
- INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum);
+ LOG_DEBUG(Loader, "%i segments:", header->e_phnum);
// First pass : Get the bits into RAM
u32 segment_addr[32];
@@ -273,17 +273,17 @@ bool ElfReader::LoadInto(u32 vaddr) {
for (int i = 0; i < header->e_phnum; i++) {
Elf32_Phdr *p = segments + i;
- INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
+ LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
p->p_filesz, p->p_memsz);
if (p->p_type == PT_LOAD) {
segment_addr[i] = base_addr + p->p_vaddr;
memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz);
- INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
+ LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
p->p_memsz);
}
}
- INFO_LOG(MASTER_LOG, "Done loading.");
+ LOG_DEBUG(Loader, "Done loading.");
return true;
}
@@ -346,7 +346,7 @@ AppLoader_ELF::~AppLoader_ELF() {
* @return True on success, otherwise false
*/
ResultStatus AppLoader_ELF::Load() {
- INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str());
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index a268e021..480274d2 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -5,9 +5,10 @@
#include <memory>
#include "core/file_sys/archive_romfs.h"
+#include "core/loader/3dsx.h"
#include "core/loader/elf.h"
#include "core/loader/ncch.h"
-#include "core/hle/kernel/archive.h"
+#include "core/hle/service/fs/archive.h"
#include "core/mem_map.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -22,7 +23,7 @@ namespace Loader {
*/
FileType IdentifyFile(const std::string &filename) {
if (filename.size() == 0) {
- ERROR_LOG(LOADER, "invalid filename %s", filename.c_str());
+ LOG_ERROR(Loader, "invalid filename %s", filename.c_str());
return FileType::Error;
}
@@ -42,6 +43,8 @@ FileType IdentifyFile(const std::string &filename) {
return FileType::CCI;
} else if (extension == ".bin") {
return FileType::BIN;
+ } else if (extension == ".3dsx") {
+ return FileType::THREEDSX;
}
return FileType::Unknown;
}
@@ -52,10 +55,14 @@ FileType IdentifyFile(const std::string &filename) {
* @return ResultStatus result of function
*/
ResultStatus LoadFile(const std::string& filename) {
- INFO_LOG(LOADER, "Loading file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading file %s...", filename.c_str());
switch (IdentifyFile(filename)) {
+ //3DSX file format...
+ case FileType::THREEDSX:
+ return AppLoader_THREEDSX(filename).Load();
+
// Standard ELF file format...
case FileType::ELF:
return AppLoader_ELF(filename).Load();
@@ -67,7 +74,8 @@ ResultStatus LoadFile(const std::string& filename) {
// Load application and RomFS
if (ResultStatus::Success == app_loader.Load()) {
- Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS");
+ Kernel::g_program_id = app_loader.GetProgramId();
+ Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
return ResultStatus::Success;
}
break;
@@ -76,7 +84,7 @@ ResultStatus LoadFile(const std::string& filename) {
// Raw BIN file format...
case FileType::BIN:
{
- INFO_LOG(LOADER, "Loading BIN file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str());
FileUtil::IOFile file(filename, "rb");
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 68f84300..0f836d28 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -22,6 +22,7 @@ enum class FileType {
CIA,
ELF,
BIN,
+ THREEDSX, //3DSX
};
/// Return type for functions in Loader namespace
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 1e5501e6..4d23656e 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -24,7 +24,7 @@ static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
* @param size Size of compressed buffer
* @return Size of decompressed buffer
*/
-u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
+static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
u32 offset_size = *(u32*)(buffer + size - 4);
return offset_size + size;
}
@@ -37,7 +37,7 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
* @param decompressed_size Size of decompressed buffer
* @return True on success, otherwise false
*/
-bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
+static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
u8* footer = compressed + compressed_size - 8;
u32 buffer_top_and_bottom = *(u32*)footer;
u32 out = decompressed_size;
@@ -118,7 +118,7 @@ AppLoader_NCCH::~AppLoader_NCCH() {
* @return ResultStatus result of function
*/
ResultStatus AppLoader_NCCH::LoadExec() const {
- if (!is_loaded)
+ if (!is_loaded)
return ResultStatus::ErrorNotLoaded;
std::vector<u8> code;
@@ -140,13 +140,13 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
// Iterate through the ExeFs archive until we find the .code file...
FileUtil::IOFile file(filename, "rb");
if (file.IsOpen()) {
+ LOG_DEBUG(Loader, "%d sections:", kMaxSections);
for (int i = 0; i < kMaxSections; i++) {
// Load the specified section...
if (strcmp((const char*)exefs_header.section[i].name, name) == 0) {
- INFO_LOG(LOADER, "ExeFS section %d:", i);
- INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name);
- INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset);
- INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
+ LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", i,
+ exefs_header.section[i].offset, exefs_header.section[i].size,
+ exefs_header.section[i].name);
s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
sizeof(ExeFs_Header)+ncch_offset);
@@ -181,11 +181,11 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
}
}
} else {
- ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
+ LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
return ResultStatus::Error;
}
return ResultStatus::ErrorNotUsed;
-}
+}
/**
* Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
@@ -194,7 +194,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
* @return True on success, otherwise false
*/
ResultStatus AppLoader_NCCH::Load() {
- INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str());
+ LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str());
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
@@ -205,12 +205,12 @@ ResultStatus AppLoader_NCCH::Load() {
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
- WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!");
+ LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
ncch_offset = 0x4000;
file.Seek(ncch_offset, 0);
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
}
-
+
// Verify we are loading the correct file type...
if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
return ResultStatus::ErrorInvalidFormat;
@@ -222,17 +222,17 @@ ResultStatus AppLoader_NCCH::Load() {
is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
entry_point = exheader_header.codeset_info.text.address;
- INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name);
- INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no");
- INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point);
+ LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name);
+ LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
+ LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
// Read ExeFS...
exefs_offset = ncch_header.exefs_offset * kBlockSize;
u32 exefs_size = ncch_header.exefs_size * kBlockSize;
- INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset);
- INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size);
+ LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
+ LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
file.Seek(exefs_offset + ncch_offset, 0);
file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
@@ -243,7 +243,7 @@ ResultStatus AppLoader_NCCH::Load() {
return ResultStatus::Success;
} else {
- ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
+ LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
}
return ResultStatus::Error;
}
@@ -297,8 +297,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
- INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset);
- INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size);
+ LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
+ LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
buffer.resize(romfs_size);
@@ -307,12 +307,16 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
return ResultStatus::Success;
}
- NOTICE_LOG(LOADER, "RomFS unused");
+ LOG_DEBUG(Loader, "NCCH has no RomFS");
return ResultStatus::ErrorNotUsed;
} else {
- ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str());
+ LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
}
return ResultStatus::Error;
}
+u64 AppLoader_NCCH::GetProgramId() const {
+ return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]);
+}
+
} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index f40a258b..2fe2a7d8 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -191,6 +191,12 @@ public:
*/
ResultStatus ReadRomFS(std::vector<u8>& buffer) const override;
+ /*
+ * Gets the program id from the NCCH header
+ * @return u64 Program id
+ */
+ u64 GetProgramId() const;
+
private:
/**
@@ -215,7 +221,7 @@ private:
u32 entry_point;
u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
u32 exefs_offset;
-
+
NCCH_Header ncch_header;
ExeFs_Header exefs_header;
ExHeader_Header exheader_header;
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp
index cf12f24d..d1c44ed2 100644
--- a/src/core/mem_map.cpp
+++ b/src/core/mem_map.cpp
@@ -11,38 +11,38 @@
namespace Memory {
-u8* g_base = NULL; ///< The base pointer to the auto-mirrored arena.
+u8* g_base = nullptr; ///< The base pointer to the auto-mirrored arena.
-MemArena g_arena; ///< The MemArena class
+static MemArena arena; ///< The MemArena class
-u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here
-u8* g_system_mem = NULL; ///< System memory
-u8* g_heap = NULL; ///< Application heap (main memory)
-u8* g_heap_gsp = NULL; ///< GSP heap (main memory)
-u8* g_vram = NULL; ///< Video memory (VRAM) pointer
-u8* g_shared_mem = NULL; ///< Shared memory
-u8* g_kernel_mem; ///< Kernel memory
+u8* g_exefs_code = nullptr; ///< ExeFS:/.code is loaded here
+u8* g_system_mem = nullptr; ///< System memory
+u8* g_heap = nullptr; ///< Application heap (main memory)
+u8* g_heap_linear = nullptr; ///< Linear heap
+u8* g_vram = nullptr; ///< Video memory (VRAM) pointer
+u8* g_shared_mem = nullptr; ///< Shared memory
+u8* g_kernel_mem; ///< Kernel memory
-u8* g_physical_bootrom = NULL; ///< Bootrom physical memory
-u8* g_uncached_bootrom = NULL;
+static u8* physical_bootrom = nullptr; ///< Bootrom physical memory
+static u8* uncached_bootrom = nullptr;
-u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here
-u8* g_physical_system_mem = NULL; ///< System physical memory
-u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM)
-u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory
-u8* g_physical_vram = NULL; ///< Video physical memory (VRAM)
-u8* g_physical_shared_mem = NULL; ///< Physical shared memory
-u8* g_physical_kernel_mem; ///< Kernel memory
+static u8* physical_exefs_code = nullptr; ///< Phsical ExeFS:/.code is loaded here
+static u8* physical_system_mem = nullptr; ///< System physical memory
+static u8* physical_fcram = nullptr; ///< Main physical memory (FCRAM)
+static u8* physical_heap_gsp = nullptr; ///< GSP heap physical memory
+static u8* physical_vram = nullptr; ///< Video physical memory (VRAM)
+static u8* physical_shared_mem = nullptr; ///< Physical shared memory
+static u8* physical_kernel_mem; ///< Kernel memory
// We don't declare the IO region in here since its handled by other means.
static MemoryView g_views[] = {
- {&g_exefs_code, &g_physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0},
- {&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
- {&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
- {&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
- {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
- {&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
- {&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0},
+ {&g_exefs_code, &physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0},
+ {&g_vram, &physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
+ {&g_heap, &physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
+ {&g_shared_mem, &physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
+ {&g_system_mem, &physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
+ {&g_kernel_mem, &physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
+ {&g_heap_linear, &physical_heap_gsp, HEAP_LINEAR_VADDR, HEAP_LINEAR_SIZE, 0},
};
/*static MemoryView views[] =
@@ -69,20 +69,20 @@ void Init() {
g_views[i].size = FCRAM_SIZE;
}
- g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &g_arena);
+ g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena);
- NOTICE_LOG(MEMMAP, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap,
- g_physical_fcram);
+ LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap,
+ physical_fcram);
}
void Shutdown() {
u32 flags = 0;
- MemoryMap_Shutdown(g_views, kNumMemViews, flags, &g_arena);
+ MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena);
- g_arena.ReleaseSpace();
- g_base = NULL;
+ arena.ReleaseSpace();
+ g_base = nullptr;
- NOTICE_LOG(MEMMAP, "shutdown OK");
+ LOG_DEBUG(HW_Memory, "shutdown OK");
}
} // namespace
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index eed44504..7b750f84 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -16,10 +16,9 @@ typedef u32 PAddr; ///< Represents a pointer in the physical address space.
////////////////////////////////////////////////////////////////////////////////////////////////////
-enum {
+enum : u32 {
BOOTROM_SIZE = 0x00010000, ///< Bootrom (super secret code/data @ 0x8000) size
MPCORE_PRIV_SIZE = 0x00002000, ///< MPCore private memory region size
- DSP_SIZE = 0x00080000, ///< DSP memory size
AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size
FCRAM_SIZE = 0x08000000, ///< FCRAM size
@@ -27,47 +26,42 @@ enum {
FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space
FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address
FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space
- FCRAM_MASK = (FCRAM_SIZE - 1), ///< FCRAM mask
SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size
SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory
SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE),
- SHARED_MEMORY_MASK = (SHARED_MEMORY_SIZE - 1),
+
+ DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size
+ DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address
CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size
CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address
CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE),
- CONFIG_MEMORY_MASK = (CONFIG_MEMORY_SIZE - 1),
KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size
KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are
KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE),
- KERNEL_MEMORY_MASK = (KERNEL_MEMORY_SIZE - 1),
EXEFS_CODE_SIZE = 0x03F00000,
EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here
EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE),
- EXEFS_CODE_MASK = 0x03FFFFFF,
// Region of FCRAM used by system
SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB
SYSTEM_MEMORY_VADDR = 0x04000000,
SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE),
- SYSTEM_MEMORY_MASK = 0x03FFFFFF,
HEAP_SIZE = FCRAM_SIZE, ///< Application heap size
//HEAP_PADDR = HEAP_GSP_SIZE,
//HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE),
HEAP_VADDR = 0x08000000,
HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE),
- HEAP_MASK = (HEAP_SIZE - 1),
- HEAP_GSP_SIZE = 0x02000000, ///< GSP heap size... TODO: Define correctly?
- HEAP_GSP_VADDR = 0x14000000,
- HEAP_GSP_VADDR_END = (HEAP_GSP_VADDR + HEAP_GSP_SIZE),
- HEAP_GSP_PADDR = 0x00000000,
- HEAP_GSP_PADDR_END = (HEAP_GSP_PADDR + HEAP_GSP_SIZE),
- HEAP_GSP_MASK = (HEAP_GSP_SIZE - 1),
+ HEAP_LINEAR_SIZE = 0x08000000, ///< Linear heap size... TODO: Define correctly?
+ HEAP_LINEAR_VADDR = 0x14000000,
+ HEAP_LINEAR_VADDR_END = (HEAP_LINEAR_VADDR + HEAP_LINEAR_SIZE),
+ HEAP_LINEAR_PADDR = 0x00000000,
+ HEAP_LINEAR_PADDR_END = (HEAP_LINEAR_PADDR + HEAP_LINEAR_SIZE),
HARDWARE_IO_SIZE = 0x01000000,
HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start
@@ -80,12 +74,10 @@ enum {
VRAM_VADDR = 0x1F000000,
VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE),
VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE),
- VRAM_MASK = 0x007FFFFF,
SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader
SCRATCHPAD_VADDR_END = 0x10000000,
SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space
- SCRATCHPAD_MASK = (SCRATCHPAD_SIZE - 1), ///< Scratchpad memory mask
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -120,7 +112,7 @@ extern u8 *g_base;
// These are guaranteed to point to "low memory" addresses (sub-32-bit).
// 64-bit: Pointers to low-mem (sub-0x10000000) mirror
// 32-bit: Same as the corresponding physical/virtual pointers.
-extern u8* g_heap_gsp; ///< GSP heap (main memory)
+extern u8* g_heap_linear; ///< Linear heap (main memory)
extern u8* g_heap; ///< Application heap (main memory)
extern u8* g_vram; ///< Video memory (VRAM)
extern u8* g_shared_mem; ///< Shared memory
@@ -147,6 +139,7 @@ u32 Read16_ZX(VAddr addr);
void Write8(VAddr addr, u8 data);
void Write16(VAddr addr, u16 data);
void Write32(VAddr addr, u32 data);
+void Write64(VAddr addr, u64 data);
void WriteBlock(VAddr addr, const u8* data, size_t size);
@@ -156,7 +149,7 @@ u8* GetPointer(VAddr virtual_address);
* Maps a block of memory on the heap
* @param size Size of block in bytes
* @param operation Memory map operation type
- * @param flags Memory allocation flags
+ * @param permissions Memory allocation permissions
*/
u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
@@ -166,7 +159,7 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
* @param operation Memory map operation type
* @param permissions Control memory permissions
*/
-u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions);
+u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions);
inline const char* GetCharPointer(const VAddr address) {
return (const char *)GetPointer(address);
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 90951812..7f7e7723 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -12,9 +12,9 @@
namespace Memory {
-std::map<u32, MemoryBlock> g_heap_map;
-std::map<u32, MemoryBlock> g_heap_gsp_map;
-std::map<u32, MemoryBlock> g_shared_map;
+static std::map<u32, MemoryBlock> heap_map;
+static std::map<u32, MemoryBlock> heap_linear_map;
+static std::map<u32, MemoryBlock> shared_map;
/// Convert a physical address to virtual address
VAddr PhysicalToVirtualAddress(const PAddr addr) {
@@ -28,7 +28,7 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) {
return addr - FCRAM_PADDR + FCRAM_VADDR;
}
- ERROR_LOG(MEMMAP, "Unknown physical address @ 0x%08x", addr);
+ LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr);
return addr;
}
@@ -44,7 +44,7 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) {
return addr - FCRAM_VADDR + FCRAM_PADDR;
}
- ERROR_LOG(MEMMAP, "Unknown virtual address @ 0x%08x", addr);
+ LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr);
return addr;
}
@@ -56,7 +56,7 @@ inline void Read(T &var, const VAddr vaddr) {
// Kernel memory command buffer
if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
- var = *((const T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK]);
+ var = *((const T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR]);
// Hardware I/O register reads
// 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
@@ -65,23 +65,23 @@ inline void Read(T &var, const VAddr vaddr) {
// ExeFS:/.code is loaded here
} else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
- var = *((const T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK]);
+ var = *((const T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR]);
- // FCRAM - GSP heap
- } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
- var = *((const T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK]);
+ // FCRAM - linear heap
+ } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
+ var = *((const T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR]);
// FCRAM - application heap
} else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
- var = *((const T*)&g_heap[vaddr & HEAP_MASK]);
+ var = *((const T*)&g_heap[vaddr - HEAP_VADDR]);
// Shared memory
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
- var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]);
+ var = *((const T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR]);
// System memory
} else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
- var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]);
+ var = *((const T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR]);
// Config memory
} else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) {
@@ -89,10 +89,10 @@ inline void Read(T &var, const VAddr vaddr) {
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
- var = *((const T*)&g_vram[vaddr & VRAM_MASK]);
+ var = *((const T*)&g_vram[vaddr - VRAM_VADDR]);
} else {
- ERROR_LOG(MEMMAP, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr);
+ LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, vaddr);
}
}
@@ -101,7 +101,7 @@ inline void Write(const VAddr vaddr, const T data) {
// Kernel memory command buffer
if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
- *(T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK] = data;
+ *(T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR] = data;
// Hardware I/O register writes
// 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
@@ -110,27 +110,27 @@ inline void Write(const VAddr vaddr, const T data) {
// ExeFS:/.code is loaded here
} else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
- *(T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK] = data;
+ *(T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR] = data;
- // FCRAM - GSP heap
- } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
- *(T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK] = data;
+ // FCRAM - linear heap
+ } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
+ *(T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR] = data;
// FCRAM - application heap
} else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
- *(T*)&g_heap[vaddr & HEAP_MASK] = data;
+ *(T*)&g_heap[vaddr - HEAP_VADDR] = data;
// Shared memory
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
- *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data;
+ *(T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR] = data;
// System memory
} else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
- *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data;
+ *(T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR] = data;
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
- *(T*)&g_vram[vaddr & VRAM_MASK] = data;
+ *(T*)&g_vram[vaddr - VRAM_VADDR] = data;
//} else if ((vaddr & 0xFFF00000) == 0x1FF00000) {
// _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory");
@@ -141,41 +141,41 @@ inline void Write(const VAddr vaddr, const T data) {
// Error out...
} else {
- ERROR_LOG(MEMMAP, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, vaddr);
+ LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, vaddr);
}
}
u8 *GetPointer(const VAddr vaddr) {
// Kernel memory command buffer
if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
- return g_kernel_mem + (vaddr & KERNEL_MEMORY_MASK);
+ return g_kernel_mem + (vaddr - KERNEL_MEMORY_VADDR);
// ExeFS:/.code is loaded here
} else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
- return g_exefs_code + (vaddr & EXEFS_CODE_MASK);
+ return g_exefs_code + (vaddr - EXEFS_CODE_VADDR);
- // FCRAM - GSP heap
- } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
- return g_heap_gsp + (vaddr & HEAP_GSP_MASK);
+ // FCRAM - linear heap
+ } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
+ return g_heap_linear + (vaddr - HEAP_LINEAR_VADDR);
// FCRAM - application heap
} else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
- return g_heap + (vaddr & HEAP_MASK);
+ return g_heap + (vaddr - HEAP_VADDR);
// Shared memory
} else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
- return g_shared_mem + (vaddr & SHARED_MEMORY_MASK);
+ return g_shared_mem + (vaddr - SHARED_MEMORY_VADDR);
// System memory
} else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
- return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK);
+ return g_system_mem + (vaddr - SYSTEM_MEMORY_VADDR);
// VRAM
} else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
- return g_vram + (vaddr & VRAM_MASK);
+ return g_vram + (vaddr - VRAM_VADDR);
} else {
- ERROR_LOG(MEMMAP, "unknown GetPointer @ 0x%08x", vaddr);
+ LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
return 0;
}
}
@@ -194,34 +194,34 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) {
block.operation = operation;
block.permissions = permissions;
- if (g_heap_map.size() > 0) {
- const MemoryBlock last_block = g_heap_map.rbegin()->second;
+ if (heap_map.size() > 0) {
+ const MemoryBlock last_block = heap_map.rbegin()->second;
block.address = last_block.address + last_block.size;
}
- g_heap_map[block.GetVirtualAddress()] = block;
+ heap_map[block.GetVirtualAddress()] = block;
return block.GetVirtualAddress();
}
/**
- * Maps a block of memory on the GSP heap
+ * Maps a block of memory on the linear heap
* @param size Size of block in bytes
* @param operation Memory map operation type
* @param flags Memory allocation flags
*/
-u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions) {
+u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) {
MemoryBlock block;
- block.base_address = HEAP_GSP_VADDR;
+ block.base_address = HEAP_LINEAR_VADDR;
block.size = size;
block.operation = operation;
block.permissions = permissions;
- if (g_heap_gsp_map.size() > 0) {
- const MemoryBlock last_block = g_heap_gsp_map.rbegin()->second;
+ if (heap_linear_map.size() > 0) {
+ const MemoryBlock last_block = heap_linear_map.rbegin()->second;
block.address = last_block.address + last_block.size;
}
- g_heap_gsp_map[block.GetVirtualAddress()] = block;
+ heap_linear_map[block.GetVirtualAddress()] = block;
return block.GetVirtualAddress();
}
@@ -239,7 +239,7 @@ u16 Read16(const VAddr addr) {
// Check for 16-bit unaligned memory reads...
if (addr & 1) {
// TODO(bunnei): Implement 16-bit unaligned memory reads
- ERROR_LOG(MEMMAP, "16-bit unaligned memory reads are not implemented!");
+ LOG_ERROR(HW_Memory, "16-bit unaligned memory reads are not implemented!");
}
return (u16)data;
diff --git a/src/core/settings.h b/src/core/settings.h
index 6a6265e1..138ffc61 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -4,6 +4,8 @@
#pragma once
+#include <string>
+
namespace Settings {
struct Values {
@@ -32,6 +34,8 @@ struct Values {
// Data Storage
bool use_virtual_sd;
+
+ std::string log_filter;
} extern values;
}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 43d0eef2..2885ff45 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -23,10 +23,10 @@ void Init(EmuWindow* emu_window) {
Core::Init();
Memory::Init();
HW::Init();
+ Kernel::Init();
HLE::Init();
CoreTiming::Init();
VideoCore::Init(emu_window);
- Kernel::Init();
}
void RunLoopFor(int cycles) {
@@ -37,13 +37,13 @@ void RunLoopUntil(u64 global_cycles) {
}
void Shutdown() {
- Core::Shutdown();
- Memory::Shutdown();
- HW::Shutdown();
- HLE::Shutdown();
- CoreTiming::Shutdown();
VideoCore::Shutdown();
+ CoreTiming::Shutdown();
+ HLE::Shutdown();
Kernel::Shutdown();
+ HW::Shutdown();
+ Memory::Shutdown();
+ Core::Shutdown();
}
} // namespace
diff --git a/src/core/system.h b/src/core/system.h
index 8f8ddf87..2bc2edc7 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -11,16 +11,16 @@
namespace System {
// State of the full emulator
-typedef enum {
- STATE_NULL = 0, ///< System is in null state, nothing initialized
- STATE_IDLE, ///< System is in an initialized state, but not running
- STATE_RUNNING, ///< System is running
- STATE_LOADING, ///< System is loading a ROM
- STATE_HALTED, ///< System is halted (error)
- STATE_STALLED, ///< System is stalled (unused)
- STATE_DEBUG, ///< System is in a special debug mode (unused)
- STATE_DIE ///< System is shutting down
-} State;
+enum State {
+ STATE_NULL = 0, ///< System is in null state, nothing initialized
+ STATE_IDLE, ///< System is in an initialized state, but not running
+ STATE_RUNNING, ///< System is running
+ STATE_LOADING, ///< System is loading a ROM
+ STATE_HALTED, ///< System is halted (error)
+ STATE_STALLED, ///< System is stalled (unused)
+ STATE_DEBUG, ///< System is in a special debug mode (unused)
+ STATE_DIE ///< System is shutting down
+};
extern volatile State g_state;
@@ -30,4 +30,4 @@ void RunLoopFor(int cycles);
void RunLoopUntil(u64 global_cycles);
void Shutdown();
-};
+}
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp
index 96d3dabe..632fb959 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/clipper.cpp
@@ -157,12 +157,12 @@ void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
InitScreenCoordinates(vtx2);
- DEBUG_LOG(GPU,
+ LOG_TRACE(Render_Software,
"Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), "
- "(%.3lu, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and "
+ "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and "
"screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)",
i,output_list.size(), buffer_vertices.size(),
- vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),output_list.size(),
+ vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),
vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(),
vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(),
vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(),
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 1ec72769..b74cd326 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -8,6 +8,7 @@
#include "pica.h"
#include "primitive_assembly.h"
#include "vertex_shader.h"
+#include "core/hle/service/gsp_gpu.h"
#include "debug_utils/debug_utils.h"
@@ -34,15 +35,26 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
u32 old_value = registers[id];
registers[id] = (old_value & ~mask) | (value & mask);
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::CommandLoaded, reinterpret_cast<void*>(&id));
+
DebugUtils::OnPicaRegWrite(id, registers[id]);
switch(id) {
+ // Trigger IRQ
+ case PICA_REG_INDEX(trigger_irq):
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
+ return;
+
// It seems like these trigger vertex rendering
case PICA_REG_INDEX(trigger_draw):
case PICA_REG_INDEX(trigger_draw_indexed):
{
DebugUtils::DumpTevStageConfig(registers.GetTevStages());
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr);
+
const auto& attribute_config = registers.vertex_attributes;
const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress());
@@ -60,7 +72,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
const u8* load_address = base_address + loader_config.data_offset;
// TODO: What happens if a loader overwrites a previous one's data?
- for (int component = 0; component < loader_config.component_count; ++component) {
+ for (unsigned component = 0; component < loader_config.component_count; ++component) {
u32 attribute_index = loader_config.GetComponent(component);
vertex_attribute_sources[attribute_index] = load_address;
vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
@@ -102,7 +114,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
(vertex_attribute_formats[i] == 2) ? *(s16*)srcdata :
*(float*)srcdata;
input.attr[i][comp] = float24::FromFloat32(srcval);
- DEBUG_LOG(GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f",
+ LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f",
comp, i, vertex, index,
attribute_config.GetBaseAddress(),
vertex_attribute_sources[i] - base_address,
@@ -132,6 +144,10 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
}
geometry_dumper.Dump();
+
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
+
break;
}
@@ -160,7 +176,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
auto& uniform = VertexShader::GetFloatUniform(uniform_setup.index);
if (uniform_setup.index > 95) {
- ERROR_LOG(GPU, "Invalid VS uniform index %d", (int)uniform_setup.index);
+ LOG_ERROR(HW_GPU, "Invalid VS uniform index %d", (int)uniform_setup.index);
break;
}
@@ -176,7 +192,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF);
}
- DEBUG_LOG(GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index,
+ LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index,
uniform.x.ToFloat32(), uniform.y.ToFloat32(), uniform.z.ToFloat32(),
uniform.w.ToFloat32());
@@ -229,6 +245,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
default:
break;
}
+
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id));
}
static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) {
@@ -259,8 +278,9 @@ static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) {
void ProcessCommandList(const u32* list, u32 size) {
u32* read_pointer = (u32*)list;
+ u32 list_length = size / sizeof(u32);
- while (read_pointer < list + size) {
+ while (read_pointer < list + list_length) {
read_pointer += ExecuteCommandBlock(read_pointer);
}
}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 22b8e995..1a20f19e 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -3,6 +3,8 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <condition_variable>
+#include <list>
#include <map>
#include <fstream>
#include <mutex>
@@ -12,14 +14,56 @@
#include <png.h>
#endif
+#include "common/log.h"
#include "common/file_util.h"
+#include "video_core/math.h"
#include "video_core/pica.h"
#include "debug_utils.h"
namespace Pica {
+void DebugContext::OnEvent(Event event, void* data) {
+ if (!breakpoints[event].enabled)
+ return;
+
+ {
+ std::unique_lock<std::mutex> lock(breakpoint_mutex);
+
+ // TODO: Should stop the CPU thread here once we multithread emulation.
+
+ active_breakpoint = event;
+ at_breakpoint = true;
+
+ // Tell all observers that we hit a breakpoint
+ for (auto& breakpoint_observer : breakpoint_observers) {
+ breakpoint_observer->OnPicaBreakPointHit(event, data);
+ }
+
+ // Wait until another thread tells us to Resume()
+ resume_from_breakpoint.wait(lock, [&]{ return !at_breakpoint; });
+ }
+}
+
+void DebugContext::Resume() {
+ {
+ std::unique_lock<std::mutex> lock(breakpoint_mutex);
+
+ // Tell all observers that we are about to resume
+ for (auto& breakpoint_observer : breakpoint_observers) {
+ breakpoint_observer->OnPicaResume();
+ }
+
+ // Resume the waiting thread (i.e. OnEvent())
+ at_breakpoint = false;
+ }
+
+ resume_from_breakpoint.notify_one();
+}
+
+std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
+
namespace DebugUtils {
void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) {
@@ -155,7 +199,7 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data
// This is put into a try-catch block to make sure we notice unknown configurations.
std::vector<OutputRegisterInfo> output_info_table;
- for (int i = 0; i < 7; ++i) {
+ for (unsigned i = 0; i < 7; ++i) {
using OutputAttributes = Pica::Regs::VSOutputAttributes;
// TODO: It's still unclear how the attribute components map to the register!
@@ -204,8 +248,8 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data
it->component_mask = it->component_mask | component_mask;
}
} catch (const std::out_of_range& ) {
- _dbg_assert_msg_(GPU, 0, "Unknown output attribute mapping");
- ERROR_LOG(GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x",
+ _dbg_assert_msg_(HW_GPU, 0, "Unknown output attribute mapping");
+ LOG_ERROR(HW_GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x",
(int)output_attributes[i].map_x.Value(),
(int)output_attributes[i].map_y.Value(),
(int)output_attributes[i].map_z.Value(),
@@ -265,7 +309,7 @@ static int is_pica_tracing = false;
void StartPicaTracing()
{
if (is_pica_tracing) {
- ERROR_LOG(GPU, "StartPicaTracing called even though tracing already running!");
+ LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!");
return;
}
@@ -298,7 +342,7 @@ void OnPicaRegWrite(u32 id, u32 value)
std::unique_ptr<PicaTrace> FinishPicaTracing()
{
if (!is_pica_tracing) {
- ERROR_LOG(GPU, "FinishPicaTracing called even though tracing already running!");
+ LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!");
return {};
}
@@ -312,15 +356,51 @@ std::unique_ptr<PicaTrace> FinishPicaTracing()
return std::move(ret);
}
+const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info) {
+ _dbg_assert_(Debug_GPU, info.format == Pica::Regs::TextureFormat::RGB8);
+
+ // Cf. rasterizer code for an explanation of this algorithm.
+ int texel_index_within_tile = 0;
+ for (int block_size_index = 0; block_size_index < 3; ++block_size_index) {
+ int sub_tile_width = 1 << block_size_index;
+ int sub_tile_height = 1 << block_size_index;
+
+ int sub_tile_index = (x & sub_tile_width) << block_size_index;
+ sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index);
+ texel_index_within_tile += sub_tile_index;
+ }
+
+ const int block_width = 8;
+ const int block_height = 8;
+
+ int coarse_x = (x / block_width) * block_width;
+ int coarse_y = (y / block_height) * block_height;
+
+ const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3;
+ return { source_ptr[2], source_ptr[1], source_ptr[0], 255 };
+}
+
+TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
+ const Regs::TextureFormat& format)
+{
+ TextureInfo info;
+ info.address = config.GetPhysicalAddress();
+ info.width = config.width;
+ info.height = config.height;
+ info.format = format;
+ info.stride = Pica::Regs::BytesPerPixel(info.format) * info.width;
+ return info;
+}
+
void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
// NOTE: Permanently enabling this just trashes hard disks for no reason.
// Hence, this is currently disabled.
return;
#ifndef HAVE_PNG
- return;
+ return;
#else
- if (!data)
+ if (!data)
return;
// Write data to file
@@ -341,7 +421,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (png_ptr == nullptr) {
- ERROR_LOG(GPU, "Could not allocate write struct\n");
+ LOG_ERROR(Debug_GPU, "Could not allocate write struct\n");
goto finalise;
}
@@ -349,13 +429,13 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
// Initialize info structure
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) {
- ERROR_LOG(GPU, "Could not allocate info struct\n");
+ LOG_ERROR(Debug_GPU, "Could not allocate info struct\n");
goto finalise;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
- ERROR_LOG(GPU, "Error during png creation\n");
+ LOG_ERROR(Debug_GPU, "Error during png creation\n");
goto finalise;
}
@@ -375,34 +455,22 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
png_write_info(png_ptr, info_ptr);
buf = new u8[row_stride * texture_config.height];
- for (int y = 0; y < texture_config.height; ++y) {
- for (int x = 0; x < texture_config.width; ++x) {
- // Cf. rasterizer code for an explanation of this algorithm.
- int texel_index_within_tile = 0;
- for (int block_size_index = 0; block_size_index < 3; ++block_size_index) {
- int sub_tile_width = 1 << block_size_index;
- int sub_tile_height = 1 << block_size_index;
-
- int sub_tile_index = (x & sub_tile_width) << block_size_index;
- sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index);
- texel_index_within_tile += sub_tile_index;
- }
-
- const int block_width = 8;
- const int block_height = 8;
-
- int coarse_x = (x / block_width) * block_width;
- int coarse_y = (y / block_height) * block_height;
-
- u8* source_ptr = (u8*)data + coarse_x * block_height * 3 + coarse_y * row_stride + texel_index_within_tile * 3;
- buf[3 * x + y * row_stride ] = source_ptr[2];
- buf[3 * x + y * row_stride + 1] = source_ptr[1];
- buf[3 * x + y * row_stride + 2] = source_ptr[0];
+ for (unsigned y = 0; y < texture_config.height; ++y) {
+ for (unsigned x = 0; x < texture_config.width; ++x) {
+ TextureInfo info;
+ info.width = texture_config.width;
+ info.height = texture_config.height;
+ info.stride = row_stride;
+ info.format = registers.texture0_format;
+ Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info);
+ buf[3 * x + y * row_stride ] = texture_color.r();
+ buf[3 * x + y * row_stride + 1] = texture_color.g();
+ buf[3 * x + y * row_stride + 2] = texture_color.b();
}
}
// Write image data
- for (auto y = 0; y < texture_config.height; ++y)
+ for (unsigned y = 0; y < texture_config.height; ++y)
{
u8* row_ptr = (u8*)buf + y * row_stride;
u8* ptr = row_ptr;
@@ -514,7 +582,7 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages)
stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n";
}
- DEBUG_LOG(GPU, "%s", stage_info.c_str());
+ LOG_TRACE(HW_GPU, "%s", stage_info.c_str());
}
} // namespace
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 8b1499bf..51f14f12 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -5,13 +5,147 @@
#pragma once
#include <array>
+#include <condition_variable>
+#include <list>
+#include <map>
#include <memory>
+#include <mutex>
#include <vector>
+#include "video_core/math.h"
#include "video_core/pica.h"
namespace Pica {
+class DebugContext {
+public:
+ enum class Event {
+ FirstEvent = 0,
+
+ CommandLoaded = FirstEvent,
+ CommandProcessed,
+ IncomingPrimitiveBatch,
+ FinishedPrimitiveBatch,
+
+ NumEvents
+ };
+
+ /**
+ * Inherit from this class to be notified of events registered to some debug context.
+ * Most importantly this is used for our debugger GUI.
+ *
+ * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
+ * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access
+ * @todo Evaluate an alternative interface, in which there is only one managing observer and multiple child observers running (by design) on the same thread.
+ */
+ class BreakPointObserver {
+ public:
+ /// Constructs the object such that it observes events of the given DebugContext.
+ BreakPointObserver(std::shared_ptr<DebugContext> debug_context) : context_weak(debug_context) {
+ std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
+ debug_context->breakpoint_observers.push_back(this);
+ }
+
+ virtual ~BreakPointObserver() {
+ auto context = context_weak.lock();
+ if (context) {
+ std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
+ context->breakpoint_observers.remove(this);
+
+ // If we are the last observer to be destroyed, tell the debugger context that
+ // it is free to continue. In particular, this is required for a proper Citra
+ // shutdown, when the emulation thread is waiting at a breakpoint.
+ if (context->breakpoint_observers.empty())
+ context->Resume();
+ }
+ }
+
+ /**
+ * Action to perform when a breakpoint was reached.
+ * @param event Type of event which triggered the breakpoint
+ * @param data Optional data pointer (if unused, this is a nullptr)
+ * @note This function will perform nothing unless it is overridden in the child class.
+ */
+ virtual void OnPicaBreakPointHit(Event, void*) {
+ }
+
+ /**
+ * Action to perform when emulation is resumed from a breakpoint.
+ * @note This function will perform nothing unless it is overridden in the child class.
+ */
+ virtual void OnPicaResume() {
+ }
+
+ protected:
+ /**
+ * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
+ * context_weak.lock(), always compare the result against nullptr.
+ */
+ std::weak_ptr<DebugContext> context_weak;
+ };
+
+ /**
+ * Simple structure defining a breakpoint state
+ */
+ struct BreakPoint {
+ bool enabled = false;
+ };
+
+ /**
+ * Static constructor used to create a shared_ptr of a DebugContext.
+ */
+ static std::shared_ptr<DebugContext> Construct() {
+ return std::shared_ptr<DebugContext>(new DebugContext);
+ }
+
+ /**
+ * Used by the emulation core when a given event has happened. If a breakpoint has been set
+ * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
+ * The current thread then is halted until Resume() is called from another thread (or until
+ * emulation is stopped).
+ * @param event Event which has happened
+ * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called.
+ */
+ void OnEvent(Event event, void* data);
+
+ /**
+ * Resume from the current breakpoint.
+ * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe.
+ */
+ void Resume();
+
+ /**
+ * Delete all set breakpoints and resume emulation.
+ */
+ void ClearBreakpoints() {
+ breakpoints.clear();
+ Resume();
+ }
+
+ // TODO: Evaluate if access to these members should be hidden behind a public interface.
+ std::map<Event, BreakPoint> breakpoints;
+ Event active_breakpoint;
+ bool at_breakpoint = false;
+
+private:
+ /**
+ * Private default constructor to make sure people always construct this through Construct()
+ * instead.
+ */
+ DebugContext() = default;
+
+ /// Mutex protecting current breakpoint state and the observer list.
+ std::mutex breakpoint_mutex;
+
+ /// Used by OnEvent to wait for resumption.
+ std::condition_variable resume_from_breakpoint;
+
+ /// List of registered observers
+ std::list<BreakPointObserver*> breakpoint_observers;
+};
+
+extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
+
namespace DebugUtils {
// Simple utility class for dumping geometry data to an OBJ file
@@ -41,7 +175,7 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data
// Utility class to log Pica commands.
struct PicaTrace {
struct Write : public std::pair<u32,u32> {
- Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {}
+ Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {}
u32& Id() { return first; }
const u32& Id() const { return first; }
@@ -57,6 +191,18 @@ bool IsPicaTracing();
void OnPicaRegWrite(u32 id, u32 value);
std::unique_ptr<PicaTrace> FinishPicaTracing();
+struct TextureInfo {
+ unsigned int address;
+ int width;
+ int height;
+ int stride;
+ Pica::Regs::TextureFormat format;
+
+ static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config,
+ const Pica::Regs::TextureFormat& format);
+};
+
+const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info);
void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages);
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h
index 1242eb58..16b1656b 100644
--- a/src/video_core/gpu_debugger.h
+++ b/src/video_core/gpu_debugger.h
@@ -39,7 +39,7 @@ public:
virtual void GXCommandProcessed(int total_command_count)
{
const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1);
- ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value());
+ LOG_TRACE(Debug_GPU, "Received command: id=%x", (int)cmd.id.Value());
}
protected:
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 5fe15a21..4c3791ad 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -45,10 +45,16 @@ struct Regs {
#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(0x41);
+ INSERT_PADDING_WORDS(0x10);
+
+ u32 trigger_irq;
+
+ INSERT_PADDING_WORDS(0x30);
BitField<0, 24, u32> viewport_size_x;
+
INSERT_PADDING_WORDS(0x1);
+
BitField<0, 24, u32> viewport_size_y;
INSERT_PADDING_WORDS(0x9);
@@ -109,8 +115,8 @@ struct Regs {
u32 address;
- u32 GetPhysicalAddress() {
- return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR;
+ u32 GetPhysicalAddress() const {
+ return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR;
}
// texture1 and texture2 store the texture format directly after the address
@@ -130,7 +136,26 @@ struct Regs {
// Seems like they are luminance formats and compressed textures.
};
- BitField<0, 1, u32> texturing_enable;
+ static unsigned BytesPerPixel(TextureFormat format) {
+ switch (format) {
+ case TextureFormat::RGBA8:
+ return 4;
+
+ case TextureFormat::RGB8:
+ return 3;
+
+ case TextureFormat::RGBA5551:
+ case TextureFormat::RGB565:
+ case TextureFormat::RGBA4:
+ return 2;
+
+ default:
+ // placeholder for yet unknown formats
+ return 1;
+ }
+ }
+
+ BitField< 0, 1, u32> texturing_enable;
TextureConfig texture0;
INSERT_PADDING_WORDS(0x8);
BitField<0, 4, TextureFormat> texture0_format;
@@ -287,7 +312,7 @@ struct Regs {
inline u32 GetBaseAddress() const {
// TODO: Ugly, should fix PhysicalToVirtualAddress instead
- return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR;
+ return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR;
}
// Descriptor for internal vertex attributes
@@ -517,10 +542,6 @@ struct Regs {
static std::string GetCommandName(int index) {
std::map<u32, std::string> map;
- // TODO: MSVC does not support using offsetof() on non-static data members even though this
- // is technically allowed since C++11. Hence, this functionality is disabled until
- // MSVC properly supports it.
- #ifndef _MSC_VER
Regs regs;
#define ADD_FIELD(name) \
do { \
@@ -529,6 +550,7 @@ struct Regs {
map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \
} while(false)
+ ADD_FIELD(trigger_irq);
ADD_FIELD(viewport_size_x);
ADD_FIELD(viewport_size_y);
ADD_FIELD(viewport_depth_range);
@@ -557,7 +579,6 @@ struct Regs {
ADD_FIELD(vs_swizzle_patterns);
#undef ADD_FIELD
- #endif // _MSC_VER
// Return empty string if no match is found
return map[index];
@@ -593,6 +614,7 @@ private:
#ifndef _MSC_VER
#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position")
+ASSERT_REG_POSITION(trigger_irq, 0x10);
ASSERT_REG_POSITION(viewport_size_x, 0x41);
ASSERT_REG_POSITION(viewport_size_y, 0x43);
ASSERT_REG_POSITION(viewport_depth_range, 0x4d);
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index dabf2d1a..102693ed 100644
--- a/src/video_core/primitive_assembly.cpp
+++ b/src/video_core/primitive_assembly.cpp
@@ -43,7 +43,7 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl
break;
default:
- ERROR_LOG(GPU, "Unknown triangle topology %x:", (int)topology);
+ LOG_ERROR(Render_Software, "Unknown triangle topology %x:", (int)topology);
break;
}
}
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index a35f0c0d..b7e04a56 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -252,7 +252,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
return combiner_output.rgb();
default:
- ERROR_LOG(GPU, "Unknown color combiner source %d\n", (int)source);
+ LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source);
return {};
}
};
@@ -272,7 +272,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
return combiner_output.a();
default:
- ERROR_LOG(GPU, "Unknown alpha combiner source %d\n", (int)source);
+ LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source);
return 0;
}
};
@@ -283,7 +283,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
case ColorModifier::SourceColor:
return values;
default:
- ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor);
+ LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor);
return {};
}
};
@@ -293,7 +293,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
case AlphaModifier::SourceAlpha:
return value;
default:
- ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor);
+ LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor);
return 0;
}
};
@@ -307,7 +307,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
return ((input[0] * input[1]) / 255).Cast<u8>();
default:
- ERROR_LOG(GPU, "Unknown color combiner operation %d\n", (int)op);
+ LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op);
return {};
}
};
@@ -321,7 +321,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
return input[0] * input[1] / 255;
default:
- ERROR_LOG(GPU, "Unknown alpha combiner operation %d\n", (int)op);
+ LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op);
return 0;
}
};
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index f1dbc9d1..bce402b8 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -25,7 +25,7 @@ public:
/// Swap buffers (render frame)
virtual void SwapBuffers() = 0;
- /**
+ /**
* Set the emulator window to use for renderer
* @param window EmuWindow handle to emulator window to use for rendering
*/
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index a0eb0418..d0f82e6c 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -20,9 +20,9 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
int info_log_length;
// Compile Vertex Shader
- DEBUG_LOG(GPU, "Compiling vertex shader.");
+ LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
- glShaderSource(vertex_shader_id, 1, &vertex_shader, NULL);
+ glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
glCompileShader(vertex_shader_id);
// Check Vertex Shader
@@ -31,14 +31,18 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
if (info_log_length > 1) {
std::vector<char> vertex_shader_error(info_log_length);
- glGetShaderInfoLog(vertex_shader_id, info_log_length, NULL, &vertex_shader_error[0]);
- DEBUG_LOG(GPU, "%s", &vertex_shader_error[0]);
+ glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
+ if (result) {
+ LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
+ } else {
+ LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]);
+ }
}
// Compile Fragment Shader
- DEBUG_LOG(GPU, "Compiling fragment shader.");
+ LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
- glShaderSource(fragment_shader_id, 1, &fragment_shader, NULL);
+ glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
glCompileShader(fragment_shader_id);
// Check Fragment Shader
@@ -47,12 +51,16 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
if (info_log_length > 1) {
std::vector<char> fragment_shader_error(info_log_length);
- glGetShaderInfoLog(fragment_shader_id, info_log_length, NULL, &fragment_shader_error[0]);
- DEBUG_LOG(GPU, "%s", &fragment_shader_error[0]);
+ glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
+ if (result) {
+ LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
+ } else {
+ LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s", &fragment_shader_error[0]);
+ }
}
// Link the program
- DEBUG_LOG(GPU, "Linking program.");
+ LOG_DEBUG(Render_OpenGL, "Linking program...");
GLuint program_id = glCreateProgram();
glAttachShader(program_id, vertex_shader_id);
@@ -65,8 +73,12 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
if (info_log_length > 1) {
std::vector<char> program_error(info_log_length);
- glGetProgramInfoLog(program_id, info_log_length, NULL, &program_error[0]);
- DEBUG_LOG(GPU, "%s", &program_error[0]);
+ glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]);
+ if (result) {
+ LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]);
+ } else {
+ LOG_ERROR(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]);
+ }
}
glDeleteShader(vertex_shader_id);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 8483f79b..e2caeeb8 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -61,7 +61,7 @@ void RendererOpenGL::SwapBuffers() {
for(int i : {0, 1}) {
const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
- if (textures[i].width != framebuffer.width || textures[i].height != framebuffer.height) {
+ if (textures[i].width != (GLsizei)framebuffer.width || textures[i].height != (GLsizei)framebuffer.height) {
// Reallocate texture if the framebuffer size has changed.
// This is expected to not happen very often and hence should not be a
// performance problem.
@@ -90,7 +90,7 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress(
framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1);
- DEBUG_LOG(GPU, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
+ LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
framebuffer.stride * framebuffer.height,
framebuffer_vaddr, (int)framebuffer.width,
(int)framebuffer.height, (int)framebuffer.format);
@@ -98,15 +98,15 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr);
// TODO: Handle other pixel formats
- _dbg_assert_msg_(RENDER, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8,
+ _dbg_assert_msg_(Render_OpenGL, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8,
"Unsupported 3DS pixel format.");
size_t pixel_stride = framebuffer.stride / 3;
// OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately
- _dbg_assert_(RENDER, pixel_stride * 3 == framebuffer.stride);
+ _dbg_assert_(Render_OpenGL, pixel_stride * 3 == framebuffer.stride);
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
// only allows rows to have a memory alignement of 4.
- _dbg_assert_(RENDER, pixel_stride % 4 == 0);
+ _dbg_assert_(Render_OpenGL, pixel_stride % 4 == 0);
glBindTexture(GL_TEXTURE_2D, texture.handle);
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride);
@@ -191,7 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x
* Draws the emulated screens to the emulator window.
*/
void RendererOpenGL::DrawScreens() {
- glViewport(0, 0, resolution_width, resolution_height);
+ auto viewport_extent = GetViewportExtent();
+ glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom?
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program_id);
@@ -228,17 +229,45 @@ 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 = 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 = 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();
int err = ogl_LoadFunctions();
if (ogl_LOAD_SUCCEEDED != err) {
- ERROR_LOG(RENDER, "Failed to initialize GL functions! Exiting...");
+ LOG_CRITICAL(Render_OpenGL, "Failed to initialize GL functions! Exiting...");
exit(-1);
}
- NOTICE_LOG(RENDER, "GL_VERSION: %s\n", glGetString(GL_VERSION));
+ LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION));
InitOpenGLObjects();
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index eed201a9..7fdcec73 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -4,13 +4,15 @@
#pragma once
+#include <array>
+
#include "generated/gl_3_2_core.h"
-#include "common/common.h"
+#include "common/math_util.h"
+
#include "core/hw/gpu.h"
-#include "video_core/renderer_base.h"
-#include <array>
+#include "video_core/renderer_base.h"
class EmuWindow;
@@ -52,6 +54,9 @@ private:
static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
const TextureInfo& texture);
+ /// Computes the viewport rectangle
+ MathUtil::Rectangle<unsigned> GetViewportExtent();
+
EmuWindow* render_window; ///< Handle to render window
u32 last_mode; ///< Last render mode
diff --git a/src/video_core/utils.cpp b/src/video_core/utils.cpp
index c1848f92..f1156a49 100644
--- a/src/video_core/utils.cpp
+++ b/src/video_core/utils.cpp
@@ -20,7 +20,7 @@ namespace VideoCore {
void DumpTGA(std::string filename, short width, short height, u8* raw_data) {
TGAHeader hdr = {0, 0, 2, 0, 0, 0, 0, width, height, 24, 0};
FILE* fout = fopen(filename.c_str(), "wb");
-
+
fwrite(&hdr, sizeof(TGAHeader), 1, fout);
for (int y = 0; y < height; y++) {
@@ -30,7 +30,7 @@ void DumpTGA(std::string filename, short width, short height, u8* raw_data) {
putc(raw_data[(3 * (y * width)) + (3 * x) + 2], fout); // r
}
}
-
+
fclose(fout);
}
} // namespace
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 9cb3d4d4..21380a90 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -12,24 +12,24 @@ namespace FormatPrecision {
/// Adjust RGBA8 color with RGBA6 precision
static inline u32 rgba8_with_rgba6(u32 src) {
- u32 color = src;
- color &= 0xFCFCFCFC;
- color |= (color >> 6) & 0x03030303;
- return color;
+ u32 color = src;
+ color &= 0xFCFCFCFC;
+ color |= (color >> 6) & 0x03030303;
+ return color;
}
/// Adjust RGBA8 color with RGB565 precision
static inline u32 rgba8_with_rgb565(u32 src) {
- u32 color = (src & 0xF8FCF8);
- color |= (color >> 5) & 0x070007;
- color |= (color >> 6) & 0x000300;
- color |= 0xFF000000;
- return color;
+ u32 color = (src & 0xF8FCF8);
+ color |= (color >> 5) & 0x070007;
+ color |= (color >> 6) & 0x000300;
+ color |= 0xFF000000;
+ return color;
}
/// Adjust Z24 depth value with Z16 precision
static inline u32 z24_with_z16(u32 src) {
- return (src & 0xFFFF00) | (src >> 16);
+ return (src & 0xFFFF00) | (src >> 16);
}
} // namespace
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp
index 96625791..477e78cf 100644
--- a/src/video_core/vertex_shader.cpp
+++ b/src/video_core/vertex_shader.cpp
@@ -2,11 +2,16 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
+#include <boost/range/algorithm.hpp>
+
+#include <common/file_util.h>
+
+#include <core/mem_map.h>
+
+#include "debug_utils/debug_utils.h"
+
#include "pica.h"
#include "vertex_shader.h"
-#include "debug_utils/debug_utils.h"
-#include <core/mem_map.h>
-#include <common/file_util.h>
namespace Pica {
@@ -201,7 +206,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
case Instruction::OpCode::CALL:
increment_pc = false;
- _dbg_assert_(GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack));
+ _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack));
*++state.call_stack_pointer = state.program_counter - shader_memory;
// TODO: Does this offset refer to the beginning of shader memory?
@@ -213,7 +218,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
default:
- ERROR_LOG(GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
+ LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
(int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex);
break;
}
@@ -238,7 +243,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes)
// Setup input register table
const auto& attribute_register_map = registers.vs_input_register_map;
float24 dummy_register;
- std::fill(&state.input_register_table[0], &state.input_register_table[16], &dummy_register);
+ boost::fill(state.input_register_table, &dummy_register);
if(num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x;
if(num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x;
if(num_attributes > 2) state.input_register_table[attribute_register_map.attribute2_register] = &input.attr[2].x;
@@ -272,8 +277,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes)
state.status_registers[0] = false;
state.status_registers[1] = false;
- std::fill(state.call_stack, state.call_stack + sizeof(state.call_stack) / sizeof(state.call_stack[0]),
- VertexShaderState::INVALID_ADDRESS);
+ boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS);
state.call_stack_pointer = &state.call_stack[0];
ProcessShaderCode(state);
@@ -281,7 +285,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes)
state.debug.max_opdesc_id, registers.vs_main_offset,
registers.vs_output_attributes);
- DEBUG_LOG(GPU, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
+ LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),
ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32());
diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h
index 607a8e80..bfb6fb6e 100644
--- a/src/video_core/vertex_shader.h
+++ b/src/video_core/vertex_shader.h
@@ -141,7 +141,7 @@ union Instruction {
return BitFieldType::Value();
else if (GetRegisterType() == Temporary)
return BitFieldType::Value() - 0x10;
- else if (GetRegisterType() == FloatUniform)
+ else // if (GetRegisterType() == FloatUniform)
return BitFieldType::Value() - 0x20;
}
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index c779771c..6791e400 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -17,8 +17,8 @@
namespace VideoCore {
-EmuWindow* g_emu_window = NULL; ///< Frontend emulator window
-RendererBase* g_renderer = NULL; ///< Renderer plugin
+EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
+RendererBase* g_renderer = nullptr; ///< Renderer plugin
int g_current_frame = 0;
/// Initialize the video core
@@ -30,13 +30,13 @@ void Init(EmuWindow* emu_window) {
g_current_frame = 0;
- NOTICE_LOG(VIDEO, "initialized OK");
+ LOG_DEBUG(Render, "initialized OK");
}
/// Shutdown the video core
void Shutdown() {
delete g_renderer;
- NOTICE_LOG(VIDEO, "shutdown OK");
+ LOG_DEBUG(Render, "shutdown OK");
}
} // namespace