summaryrefslogtreecommitdiff
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.h (renamed from src/core/hle/service/fs_user.h)10
-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.h33
-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, 9506 insertions, 5177 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 6ac5c5dc5..d6e8a4ec7 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -2,9 +2,15 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <thread>
6
5#include "common/common.h" 7#include "common/common.h"
6#include "common/log_manager.h" 8#include "common/logging/text_formatter.h"
9#include "common/logging/backend.h"
10#include "common/logging/filter.h"
11#include "common/scope_exit.h"
7 12
13#include "core/settings.h"
8#include "core/system.h" 14#include "core/system.h"
9#include "core/core.h" 15#include "core/core.h"
10#include "core/loader/loader.h" 16#include "core/loader/loader.h"
@@ -14,14 +20,21 @@
14 20
15/// Application entry point 21/// Application entry point
16int __cdecl main(int argc, char **argv) { 22int __cdecl main(int argc, char **argv) {
17 LogManager::Init(); 23 std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
24 Log::Filter log_filter(Log::Level::Debug);
25 std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter);
26 SCOPE_EXIT({
27 logger->Close();
28 logging_thread.join();
29 });
18 30
19 if (argc < 2) { 31 if (argc < 2) {
20 ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); 32 LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
21 return -1; 33 return -1;
22 } 34 }
23 35
24 Config config; 36 Config config;
37 log_filter.ParseFilterString(Settings::values.log_filter);
25 38
26 std::string boot_filename = argv[1]; 39 std::string boot_filename = argv[1];
27 EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; 40 EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
@@ -30,7 +43,7 @@ int __cdecl main(int argc, char **argv) {
30 43
31 Loader::ResultStatus load_result = Loader::LoadFile(boot_filename); 44 Loader::ResultStatus load_result = Loader::LoadFile(boot_filename);
32 if (Loader::ResultStatus::Success != load_result) { 45 if (Loader::ResultStatus::Success != load_result) {
33 ERROR_LOG(BOOT, "Failed to load ROM (Error %i)!", load_result); 46 LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result);
34 return -1; 47 return -1;
35 } 48 }
36 49
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index c5ce8a164..92764809e 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -22,21 +22,22 @@ Config::Config() {
22bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) { 22bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) {
23 if (config->ParseError() < 0) { 23 if (config->ParseError() < 0) {
24 if (retry) { 24 if (retry) {
25 ERROR_LOG(CONFIG, "Failed to load %s. Creating file from defaults...", location); 25 LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
26 FileUtil::CreateFullPath(location); 26 FileUtil::CreateFullPath(location);
27 FileUtil::WriteStringToFile(true, default_contents, location); 27 FileUtil::WriteStringToFile(true, default_contents, location);
28 *config = INIReader(location); // Reopen file 28 *config = INIReader(location); // Reopen file
29 29
30 return LoadINI(config, location, default_contents, false); 30 return LoadINI(config, location, default_contents, false);
31 } 31 }
32 ERROR_LOG(CONFIG, "Failed."); 32 LOG_ERROR(Config, "Failed.");
33 return false; 33 return false;
34 } 34 }
35 INFO_LOG(CONFIG, "Successfully loaded %s", location); 35 LOG_INFO(Config, "Successfully loaded %s", location);
36 return true; 36 return true;
37} 37}
38 38
39void Config::ReadControls() { 39void Config::ReadValues() {
40 // Controls
40 Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A); 41 Settings::values.pad_a_key = glfw_config->GetInteger("Controls", "pad_a", GLFW_KEY_A);
41 Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S); 42 Settings::values.pad_b_key = glfw_config->GetInteger("Controls", "pad_b", GLFW_KEY_S);
42 Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z); 43 Settings::values.pad_x_key = glfw_config->GetInteger("Controls", "pad_x", GLFW_KEY_Z);
@@ -54,22 +55,21 @@ void Config::ReadControls() {
54 Settings::values.pad_sdown_key = glfw_config->GetInteger("Controls", "pad_sdown", GLFW_KEY_DOWN); 55 Settings::values.pad_sdown_key = glfw_config->GetInteger("Controls", "pad_sdown", GLFW_KEY_DOWN);
55 Settings::values.pad_sleft_key = glfw_config->GetInteger("Controls", "pad_sleft", GLFW_KEY_LEFT); 56 Settings::values.pad_sleft_key = glfw_config->GetInteger("Controls", "pad_sleft", GLFW_KEY_LEFT);
56 Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT); 57 Settings::values.pad_sright_key = glfw_config->GetInteger("Controls", "pad_sright", GLFW_KEY_RIGHT);
57}
58 58
59void Config::ReadCore() { 59 // Core
60 Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter); 60 Settings::values.cpu_core = glfw_config->GetInteger("Core", "cpu_core", Core::CPU_Interpreter);
61 Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 60); 61 Settings::values.gpu_refresh_rate = glfw_config->GetInteger("Core", "gpu_refresh_rate", 60);
62}
63 62
64void Config::ReadData() { 63 // Data Storage
65 Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true); 64 Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true);
65
66 // Miscellaneous
67 Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info");
66} 68}
67 69
68void Config::Reload() { 70void Config::Reload() {
69 LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file); 71 LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file);
70 ReadControls(); 72 ReadValues();
71 ReadCore();
72 ReadData();
73} 73}
74 74
75Config::~Config() { 75Config::~Config() {
diff --git a/src/citra/config.h b/src/citra/config.h
index 4f6551876..2b46fa8aa 100644
--- a/src/citra/config.h
+++ b/src/citra/config.h
@@ -15,9 +15,7 @@ class Config {
15 std::string glfw_config_loc; 15 std::string glfw_config_loc;
16 16
17 bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true); 17 bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true);
18 void ReadControls(); 18 void ReadValues();
19 void ReadCore();
20 void ReadData();
21public: 19public:
22 Config(); 20 Config();
23 ~Config(); 21 ~Config();
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 7352c70c2..7cf543e07 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -28,10 +28,13 @@ pad_sright =
28 28
29[Core] 29[Core]
30cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental) 30cpu_core = ## 0: Interpreter (default), 1: FastInterpreter (experimental)
31gpu_refresh_rate = ## 60 (default), 1024 or 2048 may work better on the FastInterpreter 31gpu_refresh_rate = ## 60 (default)
32 32
33[Data Storage] 33[Data Storage]
34use_virtual_sd = 34use_virtual_sd =
35
36[Miscellaneous]
37log_filter = *:Info ## Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
35)"; 38)";
36 39
37} 40}
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
index 0c774bbc5..929e09f43 100644
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ b/src/citra/emu_window/emu_window_glfw.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <GLFW/glfw3.h>
6
5#include "common/common.h" 7#include "common/common.h"
6 8
7#include "video_core/video_core.h" 9#include "video_core/video_core.h"
@@ -10,22 +12,21 @@
10 12
11#include "citra/emu_window/emu_window_glfw.h" 13#include "citra/emu_window/emu_window_glfw.h"
12 14
15EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
16 return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
17}
18
13/// Called by GLFW when a key event occurs 19/// Called by GLFW when a key event occurs
14void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { 20void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
15 21
16 if (!VideoCore::g_emu_window) { 22 int keyboard_id = GetEmuWindow(win)->keyboard_id;
17 return;
18 }
19
20 int keyboard_id = ((EmuWindow_GLFW*)VideoCore::g_emu_window)->keyboard_id;
21 23
22 if (action == GLFW_PRESS) { 24 if (action == GLFW_PRESS) {
23 EmuWindow::KeyPressed({key, keyboard_id}); 25 EmuWindow::KeyPressed({key, keyboard_id});
24 } 26 } else if (action == GLFW_RELEASE) {
25
26 if (action == GLFW_RELEASE) {
27 EmuWindow::KeyReleased({key, keyboard_id}); 27 EmuWindow::KeyReleased({key, keyboard_id});
28 } 28 }
29
29 HID_User::PadUpdateComplete(); 30 HID_User::PadUpdateComplete();
30} 31}
31 32
@@ -34,15 +35,36 @@ const bool EmuWindow_GLFW::IsOpen() {
34 return glfwWindowShouldClose(m_render_window) == 0; 35 return glfwWindowShouldClose(m_render_window) == 0;
35} 36}
36 37
38void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
39 _dbg_assert_(Frontend, width > 0);
40 _dbg_assert_(Frontend, height > 0);
41
42 GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height));
43}
44
45void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
46 _dbg_assert_(Frontend, width > 0);
47 _dbg_assert_(Frontend, height > 0);
48
49 // NOTE: GLFW provides no proper way to set a minimal window size.
50 // Hence, we just ignore the corresponding EmuWindow hint.
51
52 GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(width, height));
53}
54
37/// EmuWindow_GLFW constructor 55/// EmuWindow_GLFW constructor
38EmuWindow_GLFW::EmuWindow_GLFW() { 56EmuWindow_GLFW::EmuWindow_GLFW() {
39 keyboard_id = KeyMap::NewDeviceId(); 57 keyboard_id = KeyMap::NewDeviceId();
40 58
41 ReloadSetKeymaps(); 59 ReloadSetKeymaps();
42 60
61 glfwSetErrorCallback([](int error, const char *desc){
62 LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc);
63 });
64
43 // Initialize the window 65 // Initialize the window
44 if(glfwInit() != GL_TRUE) { 66 if(glfwInit() != GL_TRUE) {
45 printf("Failed to initialize GLFW! Exiting..."); 67 LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting...");
46 exit(1); 68 exit(1);
47 } 69 }
48 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 70 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
@@ -50,19 +72,31 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
50 // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context. 72 // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
51 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 73 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
52 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 74 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
53
54 m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
55 (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
56 m_window_title.c_str(), NULL, NULL);
57 75
58 if (m_render_window == NULL) { 76 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
59 printf("Failed to create GLFW window! Exiting..."); 77 m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
78 (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
79 window_title.c_str(), nullptr, nullptr);
80
81 if (m_render_window == nullptr) {
82 LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
60 exit(1); 83 exit(1);
61 } 84 }
62 85
63 // Setup callbacks
64 glfwSetWindowUserPointer(m_render_window, this); 86 glfwSetWindowUserPointer(m_render_window, this);
87
88 // Notify base interface about window state
89 int width, height;
90 glfwGetFramebufferSize(m_render_window, &width, &height);
91 OnFramebufferResizeEvent(m_render_window, width, height);
92
93 glfwGetWindowSize(m_render_window, &width, &height);
94 OnClientAreaResizeEvent(m_render_window, width, height);
95
96 // Setup callbacks
65 glfwSetKeyCallback(m_render_window, OnKeyEvent); 97 glfwSetKeyCallback(m_render_window, OnKeyEvent);
98 glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
99 glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);
66 100
67 DoneCurrent(); 101 DoneCurrent();
68} 102}
@@ -89,7 +123,7 @@ void EmuWindow_GLFW::MakeCurrent() {
89 123
90/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread 124/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
91void EmuWindow_GLFW::DoneCurrent() { 125void EmuWindow_GLFW::DoneCurrent() {
92 glfwMakeContextCurrent(NULL); 126 glfwMakeContextCurrent(nullptr);
93} 127}
94 128
95void EmuWindow_GLFW::ReloadSetKeymaps() { 129void EmuWindow_GLFW::ReloadSetKeymaps() {
@@ -110,3 +144,15 @@ void EmuWindow_GLFW::ReloadSetKeymaps() {
110 KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP); 144 KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP);
111 KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); 145 KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN);
112} 146}
147
148void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
149 std::pair<int,int> current_size;
150 glfwGetWindowSize(m_render_window, &current_size.first, &current_size.second);
151
152 _dbg_assert_(Frontend, (int)minimal_size.first > 0 && (int)minimal_size.second > 0);
153 int new_width = std::max(current_size.first, (int)minimal_size.first);
154 int new_height = std::max(current_size.second, (int)minimal_size.second);
155
156 if (current_size != std::make_pair(new_width, new_height))
157 glfwSetWindowSize(m_render_window, new_width, new_height);
158}
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h
index 7c3072145..5b04e87bb 100644
--- a/src/citra/emu_window/emu_window_glfw.h
+++ b/src/citra/emu_window/emu_window_glfw.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <GLFW/glfw3.h>
8
9#include "common/emu_window.h" 7#include "common/emu_window.h"
10 8
9struct GLFWwindow;
10
11class EmuWindow_GLFW : public EmuWindow { 11class EmuWindow_GLFW : public EmuWindow {
12public: 12public:
13 EmuWindow_GLFW(); 13 EmuWindow_GLFW();
@@ -16,12 +16,12 @@ public:
16 /// Swap buffers to display the next frame 16 /// Swap buffers to display the next frame
17 void SwapBuffers() override; 17 void SwapBuffers() override;
18 18
19 /// Polls window events 19 /// Polls window events
20 void PollEvents() override; 20 void PollEvents() override;
21 21
22 /// Makes the graphics context current for the caller thread 22 /// Makes the graphics context current for the caller thread
23 void MakeCurrent() override; 23 void MakeCurrent() override;
24 24
25 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread 25 /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
26 void DoneCurrent() override; 26 void DoneCurrent() override;
27 27
@@ -30,9 +30,17 @@ public:
30 /// Whether the window is still open, and a close request hasn't yet been sent 30 /// Whether the window is still open, and a close request hasn't yet been sent
31 const bool IsOpen(); 31 const bool IsOpen();
32 32
33 static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height);
34
35 static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height);
36
33 void ReloadSetKeymaps() override; 37 void ReloadSetKeymaps() override;
34 38
35private: 39private:
40 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
41
42 static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win);
43
36 GLFWwindow* m_render_window; ///< Internal GLFW render window 44 GLFWwindow* m_render_window; ///< Internal GLFW render window
37 45
38 /// Device id of keyboard for use with KeyMap 46 /// Device id of keyboard for use with KeyMap
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 98a48a69a..90e5c6aa6 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -8,9 +8,12 @@ set(SRCS
8 debugger/callstack.cpp 8 debugger/callstack.cpp
9 debugger/disassembler.cpp 9 debugger/disassembler.cpp
10 debugger/graphics.cpp 10 debugger/graphics.cpp
11 debugger/graphics_breakpoints.cpp
11 debugger/graphics_cmdlists.cpp 12 debugger/graphics_cmdlists.cpp
13 debugger/graphics_framebuffer.cpp
12 debugger/ramview.cpp 14 debugger/ramview.cpp
13 debugger/registers.cpp 15 debugger/registers.cpp
16 util/spinbox.cpp
14 bootmanager.cpp 17 bootmanager.cpp
15 hotkeys.cpp 18 hotkeys.cpp
16 main.cpp 19 main.cpp
@@ -23,9 +26,13 @@ set(HEADERS
23 debugger/callstack.hxx 26 debugger/callstack.hxx
24 debugger/disassembler.hxx 27 debugger/disassembler.hxx
25 debugger/graphics.hxx 28 debugger/graphics.hxx
29 debugger/graphics_breakpoints.hxx
30 debugger/graphics_breakpoints_p.hxx
26 debugger/graphics_cmdlists.hxx 31 debugger/graphics_cmdlists.hxx
32 debugger/graphics_framebuffer.hxx
27 debugger/ramview.hxx 33 debugger/ramview.hxx
28 debugger/registers.hxx 34 debugger/registers.hxx
35 util/spinbox.hxx
29 bootmanager.hxx 36 bootmanager.hxx
30 hotkeys.hxx 37 hotkeys.hxx
31 main.hxx 38 main.hxx
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 8f3799351..6d08d6afc 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -2,12 +2,20 @@
2#include <QKeyEvent> 2#include <QKeyEvent>
3#include <QApplication> 3#include <QApplication>
4 4
5#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
6// Required for screen DPI information
7#include <QScreen>
8#include <QWindow>
9#endif
10
5#include "common/common.h" 11#include "common/common.h"
6#include "bootmanager.hxx" 12#include "bootmanager.hxx"
7 13
8#include "core/core.h" 14#include "core/core.h"
9#include "core/settings.h" 15#include "core/settings.h"
10 16
17#include "video_core/debug_utils/debug_utils.h"
18
11#include "video_core/video_core.h" 19#include "video_core/video_core.h"
12 20
13#include "citra_qt/version.h" 21#include "citra_qt/version.h"
@@ -17,7 +25,7 @@
17#define APP_TITLE APP_NAME " " APP_VERSION 25#define APP_TITLE APP_NAME " " APP_VERSION
18#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" 26#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
19 27
20EmuThread::EmuThread(GRenderWindow* render_window) : 28EmuThread::EmuThread(GRenderWindow* render_window) :
21 filename(""), exec_cpu_step(false), cpu_running(false), 29 filename(""), exec_cpu_step(false), cpu_running(false),
22 stop_run(false), render_window(render_window) 30 stop_run(false), render_window(render_window)
23{ 31{
@@ -33,19 +41,16 @@ void EmuThread::run()
33 stop_run = false; 41 stop_run = false;
34 while (!stop_run) 42 while (!stop_run)
35 { 43 {
36 for (int tight_loop = 0; tight_loop < 10000; ++tight_loop) 44 if (cpu_running)
37 { 45 {
38 if (cpu_running || exec_cpu_step) 46 Core::RunLoop();
39 { 47 }
40 if (exec_cpu_step) 48 else if (exec_cpu_step)
41 exec_cpu_step = false; 49 {
42 50 exec_cpu_step = false;
43 Core::SingleStep(); 51 Core::SingleStep();
44 if (!cpu_running) { 52 emit CPUStepped();
45 emit CPUStepped(); 53 yieldCurrentThread();
46 yieldCurrentThread();
47 }
48 }
49 } 54 }
50 } 55 }
51 render_window->moveContext(); 56 render_window->moveContext();
@@ -57,26 +62,33 @@ void EmuThread::Stop()
57{ 62{
58 if (!isRunning()) 63 if (!isRunning())
59 { 64 {
60 INFO_LOG(MASTER_LOG, "EmuThread::Stop called while emu thread wasn't running, returning..."); 65 LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
61 return; 66 return;
62 } 67 }
63 stop_run = true; 68 stop_run = true;
64 69
70 // Release emu threads from any breakpoints, so that this doesn't hang forever.
71 Pica::g_debug_context->ClearBreakpoints();
72
65 //core::g_state = core::SYS_DIE; 73 //core::g_state = core::SYS_DIE;
66 74
67 wait(500); 75 // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
76 wait(1000);
68 if (isRunning()) 77 if (isRunning())
69 { 78 {
70 WARN_LOG(MASTER_LOG, "EmuThread still running, terminating..."); 79 LOG_WARNING(Frontend, "EmuThread still running, terminating...");
71 quit(); 80 quit();
72 wait(1000); 81
82 // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
83 // queued... This should be fixed.
84 wait(50000);
73 if (isRunning()) 85 if (isRunning())
74 { 86 {
75 WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here..."); 87 LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
76 terminate(); 88 terminate();
77 } 89 }
78 } 90 }
79 INFO_LOG(MASTER_LOG, "EmuThread stopped"); 91 LOG_INFO(Frontend, "EmuThread stopped");
80} 92}
81 93
82 94
@@ -85,20 +97,20 @@ void EmuThread::Stop()
85class GGLWidgetInternal : public QGLWidget 97class GGLWidgetInternal : public QGLWidget
86{ 98{
87public: 99public:
88 GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) : QGLWidget(fmt, parent) 100 GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
89 { 101 : QGLWidget(fmt, parent), parent(parent) {
90 parent_ = parent;
91 } 102 }
92 103
93 void paintEvent(QPaintEvent* ev) override 104 void paintEvent(QPaintEvent* ev) override {
94 {
95 } 105 }
106
96 void resizeEvent(QResizeEvent* ev) override { 107 void resizeEvent(QResizeEvent* ev) override {
97 parent_->SetClientAreaWidth(size().width()); 108 parent->OnClientAreaResized(ev->size().width(), ev->size().height());
98 parent_->SetClientAreaHeight(size().height()); 109 parent->OnFramebufferSizeChanged();
99 } 110 }
111
100private: 112private:
101 GRenderWindow* parent_; 113 GRenderWindow* parent;
102}; 114};
103 115
104EmuThread& GRenderWindow::GetEmuThread() 116EmuThread& GRenderWindow::GetEmuThread()
@@ -108,6 +120,9 @@ EmuThread& GRenderWindow::GetEmuThread()
108 120
109GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) 121GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0)
110{ 122{
123 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
124 setWindowTitle(QString::fromStdString(window_title));
125
111 keyboard_id = KeyMap::NewDeviceId(); 126 keyboard_id = KeyMap::NewDeviceId();
112 ReloadSetKeymaps(); 127 ReloadSetKeymaps();
113 128
@@ -117,16 +132,25 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this
117 fmt.setProfile(QGLFormat::CoreProfile); 132 fmt.setProfile(QGLFormat::CoreProfile);
118 // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X 133 // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
119 fmt.setOption(QGL::NoDeprecatedFunctions); 134 fmt.setOption(QGL::NoDeprecatedFunctions);
120 135
121 child = new GGLWidgetInternal(fmt, this); 136 child = new GGLWidgetInternal(fmt, this);
122 QBoxLayout* layout = new QHBoxLayout(this); 137 QBoxLayout* layout = new QHBoxLayout(this);
123 resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); 138 resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
124 layout->addWidget(child); 139 layout->addWidget(child);
125 layout->setMargin(0); 140 layout->setMargin(0);
126 setLayout(layout); 141 setLayout(layout);
127 QObject::connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); 142 connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext()));
143
144 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
145
146 OnFramebufferSizeChanged();
147 NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(child->width(), child->height()));
128 148
129 BackupGeometry(); 149 BackupGeometry();
150
151#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
152 connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()));
153#endif
130} 154}
131 155
132void GRenderWindow::moveContext() 156void GRenderWindow::moveContext()
@@ -169,14 +193,28 @@ void GRenderWindow::DoneCurrent()
169} 193}
170 194
171void GRenderWindow::PollEvents() { 195void GRenderWindow::PollEvents() {
172 // TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title 196}
173 // from the main thread, but this should probably be in an event handler... 197
174 /* 198// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
175 static char title[128]; 199//
176 sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(), 200// Older versions get the window size (density independent pixels),
177 video_core::g_renderer->current_fps()); 201// and hence, do not support DPI scaling ("retina" displays).
178 setWindowTitle(title); 202// The result will be a viewport that is smaller than the extent of the window.
179 */ 203void GRenderWindow::OnFramebufferSizeChanged()
204{
205 // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
206#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
207 // windowHandle() might not be accessible until the window is displayed to screen.
208 auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0;
209
210 unsigned width = child->QPaintDevice::width() * pixel_ratio;
211 unsigned height = child->QPaintDevice::height() * pixel_ratio;
212#else
213 unsigned width = child->QPaintDevice::width();
214 unsigned height = child->QPaintDevice::height();
215#endif
216
217 NotifyFramebufferSizeChanged(std::make_pair(width, height));
180} 218}
181 219
182void GRenderWindow::BackupGeometry() 220void GRenderWindow::BackupGeometry()
@@ -201,7 +239,7 @@ QByteArray GRenderWindow::saveGeometry()
201{ 239{
202 // If we are a top-level widget, store the current geometry 240 // If we are a top-level widget, store the current geometry
203 // otherwise, store the last backup 241 // otherwise, store the last backup
204 if (parent() == NULL) 242 if (parent() == nullptr)
205 return ((QGLWidget*)this)->saveGeometry(); 243 return ((QGLWidget*)this)->saveGeometry();
206 else 244 else
207 return geometry; 245 return geometry;
@@ -239,3 +277,11 @@ void GRenderWindow::ReloadSetKeymaps()
239 KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); 277 KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN);
240} 278}
241 279
280void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height)
281{
282 NotifyClientAreaSizeChanged(std::make_pair(width, height));
283}
284
285void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
286 setMinimumSize(minimal_size.first, minimal_size.second);
287}
diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx
index f8afc403e..5f69f15ea 100644
--- a/src/citra_qt/bootmanager.hxx
+++ b/src/citra_qt/bootmanager.hxx
@@ -1,12 +1,16 @@
1#include <atomic>
2
1#include <QThread> 3#include <QThread>
2#include <QGLWidget> 4#include <QGLWidget>
3#include <atomic> 5
4#include "common/common.h" 6#include "common/common.h"
5#include "common/emu_window.h" 7#include "common/emu_window.h"
6 8
7class GRenderWindow; 9class QScreen;
8class QKeyEvent; 10class QKeyEvent;
9 11
12class GRenderWindow;
13
10class EmuThread : public QThread 14class EmuThread : public QThread
11{ 15{
12 Q_OBJECT 16 Q_OBJECT
@@ -14,7 +18,7 @@ class EmuThread : public QThread
14public: 18public:
15 /** 19 /**
16 * Set image filename 20 * Set image filename
17 * 21 *
18 * @param filename 22 * @param filename
19 * @warning Only call when not running! 23 * @warning Only call when not running!
20 */ 24 */
@@ -74,7 +78,7 @@ private:
74signals: 78signals:
75 /** 79 /**
76 * Emitted when CPU when we've finished processing a single Gekko instruction 80 * Emitted when CPU when we've finished processing a single Gekko instruction
77 * 81 *
78 * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false)) 82 * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false))
79 * @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) 83 * @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)
80 */ 84 */
@@ -100,7 +104,7 @@ public:
100 void BackupGeometry(); 104 void BackupGeometry();
101 void RestoreGeometry(); 105 void RestoreGeometry();
102 void restoreGeometry(const QByteArray& geometry); // overridden 106 void restoreGeometry(const QByteArray& geometry); // overridden
103 QByteArray saveGeometry(); // overridden 107 QByteArray saveGeometry(); // overridden
104 108
105 EmuThread& GetEmuThread(); 109 EmuThread& GetEmuThread();
106 110
@@ -109,10 +113,16 @@ public:
109 113
110 void ReloadSetKeymaps() override; 114 void ReloadSetKeymaps() override;
111 115
116 void OnClientAreaResized(unsigned width, unsigned height);
117
118 void OnFramebufferSizeChanged();
119
112public slots: 120public slots:
113 void moveContext(); 121 void moveContext(); // overridden
114 122
115private: 123private:
124 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
125
116 QGLWidget* child; 126 QGLWidget* child;
117 127
118 EmuThread emu_thread; 128 EmuThread emu_thread;
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 63d396439..0ae6b8b2d 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -21,7 +21,7 @@ Config::Config() {
21 Reload(); 21 Reload();
22} 22}
23 23
24void Config::ReadControls() { 24void Config::ReadValues() {
25 qt_config->beginGroup("Controls"); 25 qt_config->beginGroup("Controls");
26 Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt(); 26 Settings::values.pad_a_key = qt_config->value("pad_a", Qt::Key_A).toInt();
27 Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt(); 27 Settings::values.pad_b_key = qt_config->value("pad_b", Qt::Key_S).toInt();
@@ -41,9 +41,22 @@ void Config::ReadControls() {
41 Settings::values.pad_sleft_key = qt_config->value("pad_sleft", Qt::Key_Left).toInt(); 41 Settings::values.pad_sleft_key = qt_config->value("pad_sleft", Qt::Key_Left).toInt();
42 Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt(); 42 Settings::values.pad_sright_key = qt_config->value("pad_sright", Qt::Key_Right).toInt();
43 qt_config->endGroup(); 43 qt_config->endGroup();
44
45 qt_config->beginGroup("Core");
46 Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt();
47 Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt();
48 qt_config->endGroup();
49
50 qt_config->beginGroup("Data Storage");
51 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
52 qt_config->endGroup();
53
54 qt_config->beginGroup("Miscellaneous");
55 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
56 qt_config->endGroup();
44} 57}
45 58
46void Config::SaveControls() { 59void Config::SaveValues() {
47 qt_config->beginGroup("Controls"); 60 qt_config->beginGroup("Controls");
48 qt_config->setValue("pad_a", Settings::values.pad_a_key); 61 qt_config->setValue("pad_a", Settings::values.pad_a_key);
49 qt_config->setValue("pad_b", Settings::values.pad_b_key); 62 qt_config->setValue("pad_b", Settings::values.pad_b_key);
@@ -63,44 +76,27 @@ void Config::SaveControls() {
63 qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key); 76 qt_config->setValue("pad_sleft", Settings::values.pad_sleft_key);
64 qt_config->setValue("pad_sright", Settings::values.pad_sright_key); 77 qt_config->setValue("pad_sright", Settings::values.pad_sright_key);
65 qt_config->endGroup(); 78 qt_config->endGroup();
66}
67
68void Config::ReadCore() {
69 qt_config->beginGroup("Core");
70 Settings::values.cpu_core = qt_config->value("cpu_core", Core::CPU_Interpreter).toInt();
71 Settings::values.gpu_refresh_rate = qt_config->value("gpu_refresh_rate", 60).toInt();
72 qt_config->endGroup();
73}
74 79
75void Config::SaveCore() {
76 qt_config->beginGroup("Core"); 80 qt_config->beginGroup("Core");
77 qt_config->setValue("cpu_core", Settings::values.cpu_core); 81 qt_config->setValue("cpu_core", Settings::values.cpu_core);
78 qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate); 82 qt_config->setValue("gpu_refresh_rate", Settings::values.gpu_refresh_rate);
79 qt_config->endGroup(); 83 qt_config->endGroup();
80}
81 84
82void Config::ReadData() {
83 qt_config->beginGroup("Data Storage"); 85 qt_config->beginGroup("Data Storage");
84 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); 86 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
85 qt_config->endGroup(); 87 qt_config->endGroup();
86}
87 88
88void Config::SaveData() { 89 qt_config->beginGroup("Miscellaneous");
89 qt_config->beginGroup("Data Storage"); 90 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
90 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
91 qt_config->endGroup(); 91 qt_config->endGroup();
92} 92}
93 93
94void Config::Reload() { 94void Config::Reload() {
95 ReadControls(); 95 ReadValues();
96 ReadCore();
97 ReadData();
98} 96}
99 97
100void Config::Save() { 98void Config::Save() {
101 SaveControls(); 99 SaveValues();
102 SaveCore();
103 SaveData();
104} 100}
105 101
106Config::~Config() { 102Config::~Config() {
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h
index 782c26287..4c95d0cb9 100644
--- a/src/citra_qt/config.h
+++ b/src/citra_qt/config.h
@@ -12,12 +12,8 @@ class Config {
12 QSettings* qt_config; 12 QSettings* qt_config;
13 std::string qt_config_loc; 13 std::string qt_config_loc;
14 14
15 void ReadControls(); 15 void ReadValues();
16 void SaveControls(); 16 void SaveValues();
17 void ReadCore();
18 void SaveCore();
19 void ReadData();
20 void SaveData();
21public: 17public:
22 Config(); 18 Config();
23 ~Config(); 19 ~Config();
diff --git a/src/citra_qt/config/controller_config_util.cpp b/src/citra_qt/config/controller_config_util.cpp
index c5426570b..aee3f8616 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&
48 else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control"); 48 else if (config.keys.key_code[id] == Qt::Key_Control) text = tr("Control");
49 else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt"); 49 else if (config.keys.key_code[id] == Qt::Key_Alt) text = tr("Alt");
50 else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta"); 50 else if (config.keys.key_code[id] == Qt::Key_Meta) text = tr("Meta");
51 setText(text); 51 setText(text);
52} 52}
53 53
54void GKeyConfigButton::OnClicked() 54void GKeyConfigButton::OnClicked()
@@ -118,4 +118,4 @@ GButtonConfigGroup::GButtonConfigGroup(const QString& name, common::Config::Cont
118 setLayout(layout); 118 setLayout(layout);
119} 119}
120 120
121*/ \ No newline at end of file 121*/
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index 77fb0c9ed..895851be3 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -28,7 +28,7 @@ void CallstackWidget::OnCPUStepped()
28 28
29 u32 sp = app_core->GetReg(13); //stack pointer 29 u32 sp = app_core->GetReg(13); //stack pointer
30 u32 addr, ret_addr, call_addr, func_addr; 30 u32 addr, ret_addr, call_addr, func_addr;
31 31
32 int counter = 0; 32 int counter = 0;
33 for (int addr = 0x10000000; addr >= sp; addr -= 4) 33 for (int addr = 0x10000000; addr >= sp; addr -= 4)
34 { 34 {
@@ -55,7 +55,7 @@ void CallstackWidget::OnCPUStepped()
55 callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0')))); 55 callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0'))));
56 callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0')))); 56 callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0'))));
57 callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0')))); 57 callstack_model->setItem(counter, 2, new QStandardItem(QString("0x%1").arg(call_addr, 8, 16, QLatin1Char('0'))));
58 58
59 name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown"; 59 name = Symbols::HasSymbol(func_addr) ? Symbols::GetSymbol(func_addr).name : "unknown";
60 callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name)) 60 callstack_model->setItem(counter, 3, new QStandardItem(QString("%1_%2").arg(QString::fromStdString(name))
61 .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0'))))); 61 .arg(QString("0x%1").arg(func_addr, 8, 16, QLatin1Char('0')))));
@@ -63,4 +63,4 @@ void CallstackWidget::OnCPUStepped()
63 counter++; 63 counter++;
64 } 64 }
65 } 65 }
66} \ No newline at end of file 66}
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp
new file mode 100644
index 000000000..53394b6e6
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints.cpp
@@ -0,0 +1,261 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <QMetaType>
6#include <QPushButton>
7#include <QTreeWidget>
8#include <QVBoxLayout>
9#include <QLabel>
10
11#include "graphics_breakpoints.hxx"
12#include "graphics_breakpoints_p.hxx"
13
14BreakPointModel::BreakPointModel(std::shared_ptr<Pica::DebugContext> debug_context, QObject* parent)
15 : QAbstractListModel(parent), context_weak(debug_context),
16 at_breakpoint(debug_context->at_breakpoint),
17 active_breakpoint(debug_context->active_breakpoint)
18{
19
20}
21
22int BreakPointModel::columnCount(const QModelIndex& parent) const
23{
24 return 2;
25}
26
27int BreakPointModel::rowCount(const QModelIndex& parent) const
28{
29 return static_cast<int>(Pica::DebugContext::Event::NumEvents);
30}
31
32QVariant BreakPointModel::data(const QModelIndex& index, int role) const
33{
34 const auto event = static_cast<Pica::DebugContext::Event>(index.row());
35
36 switch (role) {
37 case Qt::DisplayRole:
38 {
39 switch (index.column()) {
40 case 0:
41 {
42 std::map<Pica::DebugContext::Event, QString> map;
43 map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")});
44 map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")});
45 map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")});
46 map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")});
47
48 _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents));
49
50 return map[event];
51 }
52
53 case 1:
54 return data(index, Role_IsEnabled).toBool() ? tr("Enabled") : tr("Disabled");
55
56 default:
57 break;
58 }
59
60 break;
61 }
62
63 case Qt::BackgroundRole:
64 {
65 if (at_breakpoint && index.row() == static_cast<int>(active_breakpoint)) {
66 return QBrush(QColor(0xE0, 0xE0, 0x10));
67 }
68 break;
69 }
70
71 case Role_IsEnabled:
72 {
73 auto context = context_weak.lock();
74 return context && context->breakpoints[event].enabled;
75 }
76
77 default:
78 break;
79 }
80 return QVariant();
81}
82
83QVariant BreakPointModel::headerData(int section, Qt::Orientation orientation, int role) const
84{
85 switch(role) {
86 case Qt::DisplayRole:
87 {
88 if (section == 0) {
89 return tr("Event");
90 } else if (section == 1) {
91 return tr("Status");
92 }
93
94 break;
95 }
96 }
97
98 return QVariant();
99}
100
101bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, int role)
102{
103 const auto event = static_cast<Pica::DebugContext::Event>(index.row());
104
105 switch (role) {
106 case Role_IsEnabled:
107 {
108 auto context = context_weak.lock();
109 if (!context)
110 return false;
111
112 context->breakpoints[event].enabled = value.toBool();
113 QModelIndex changed_index = createIndex(index.row(), 1);
114 emit dataChanged(changed_index, changed_index);
115 return true;
116 }
117 }
118
119 return false;
120}
121
122
123void BreakPointModel::OnBreakPointHit(Pica::DebugContext::Event event)
124{
125 auto context = context_weak.lock();
126 if (!context)
127 return;
128
129 active_breakpoint = context->active_breakpoint;
130 at_breakpoint = context->at_breakpoint;
131 emit dataChanged(createIndex(static_cast<int>(event), 0),
132 createIndex(static_cast<int>(event), 1));
133}
134
135void BreakPointModel::OnResumed()
136{
137 auto context = context_weak.lock();
138 if (!context)
139 return;
140
141 at_breakpoint = context->at_breakpoint;
142 emit dataChanged(createIndex(static_cast<int>(active_breakpoint), 0),
143 createIndex(static_cast<int>(active_breakpoint), 1));
144 active_breakpoint = context->active_breakpoint;
145}
146
147
148GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
149 QWidget* parent)
150 : QDockWidget(tr("Pica Breakpoints"), parent),
151 Pica::DebugContext::BreakPointObserver(debug_context)
152{
153 setObjectName("PicaBreakPointsWidget");
154
155 status_text = new QLabel(tr("Emulation running"));
156 resume_button = new QPushButton(tr("Resume"));
157 resume_button->setEnabled(false);
158
159 breakpoint_model = new BreakPointModel(debug_context, this);
160 breakpoint_list = new QTreeView;
161 breakpoint_list->setModel(breakpoint_model);
162
163 toggle_breakpoint_button = new QPushButton(tr("Enable"));
164 toggle_breakpoint_button->setEnabled(false);
165
166 qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
167
168 connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested()));
169
170 connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
171 this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
172 Qt::BlockingQueuedConnection);
173 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
174
175 connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
176 breakpoint_model, SLOT(OnBreakPointHit(Pica::DebugContext::Event)),
177 Qt::BlockingQueuedConnection);
178 connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed()));
179
180 connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&,const QModelIndex&)),
181 breakpoint_model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)));
182
183 connect(breakpoint_list->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
184 this, SLOT(OnBreakpointSelectionChanged(QModelIndex)));
185
186 connect(toggle_breakpoint_button, SIGNAL(clicked()), this, SLOT(OnToggleBreakpointEnabled()));
187
188 QWidget* main_widget = new QWidget;
189 auto main_layout = new QVBoxLayout;
190 {
191 auto sub_layout = new QHBoxLayout;
192 sub_layout->addWidget(status_text);
193 sub_layout->addWidget(resume_button);
194 main_layout->addLayout(sub_layout);
195 }
196 main_layout->addWidget(breakpoint_list);
197 main_layout->addWidget(toggle_breakpoint_button);
198 main_widget->setLayout(main_layout);
199
200 setWidget(main_widget);
201}
202
203void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data)
204{
205 // Process in GUI thread
206 emit BreakPointHit(event, data);
207}
208
209void GraphicsBreakPointsWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
210{
211 status_text->setText(tr("Emulation halted at breakpoint"));
212 resume_button->setEnabled(true);
213}
214
215void GraphicsBreakPointsWidget::OnPicaResume()
216{
217 // Process in GUI thread
218 emit Resumed();
219}
220
221void GraphicsBreakPointsWidget::OnResumed()
222{
223 status_text->setText(tr("Emulation running"));
224 resume_button->setEnabled(false);
225}
226
227void GraphicsBreakPointsWidget::OnResumeRequested()
228{
229 if (auto context = context_weak.lock())
230 context->Resume();
231}
232
233void GraphicsBreakPointsWidget::OnBreakpointSelectionChanged(const QModelIndex& index)
234{
235 if (!index.isValid()) {
236 toggle_breakpoint_button->setEnabled(false);
237 return;
238 }
239
240 toggle_breakpoint_button->setEnabled(true);
241 UpdateToggleBreakpointButton(index);
242}
243
244void GraphicsBreakPointsWidget::OnToggleBreakpointEnabled()
245{
246 QModelIndex index = breakpoint_list->selectionModel()->currentIndex();
247 bool new_state = !(breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool());
248
249 breakpoint_model->setData(index, new_state,
250 BreakPointModel::Role_IsEnabled);
251 UpdateToggleBreakpointButton(index);
252}
253
254void GraphicsBreakPointsWidget::UpdateToggleBreakpointButton(const QModelIndex& index)
255{
256 if (true == breakpoint_model->data(index, BreakPointModel::Role_IsEnabled).toBool()) {
257 toggle_breakpoint_button->setText(tr("Disable"));
258 } else {
259 toggle_breakpoint_button->setText(tr("Enable"));
260 }
261}
diff --git a/src/citra_qt/debugger/graphics_breakpoints.hxx b/src/citra_qt/debugger/graphics_breakpoints.hxx
new file mode 100644
index 000000000..2142c6fa1
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints.hxx
@@ -0,0 +1,53 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include <QAbstractListModel>
10#include <QDockWidget>
11
12#include "video_core/debug_utils/debug_utils.h"
13
14class QLabel;
15class QPushButton;
16class QTreeView;
17
18class BreakPointModel;
19
20class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver {
21 Q_OBJECT
22
23 using Event = Pica::DebugContext::Event;
24
25public:
26 GraphicsBreakPointsWidget(std::shared_ptr<Pica::DebugContext> debug_context,
27 QWidget* parent = nullptr);
28
29 void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
30 void OnPicaResume() override;
31
32public slots:
33 void OnBreakPointHit(Pica::DebugContext::Event event, void* data);
34 void OnResumeRequested();
35 void OnResumed();
36 void OnBreakpointSelectionChanged(const QModelIndex&);
37 void OnToggleBreakpointEnabled();
38
39signals:
40 void Resumed();
41 void BreakPointHit(Pica::DebugContext::Event event, void* data);
42 void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
43
44private:
45 void UpdateToggleBreakpointButton(const QModelIndex& index);
46
47 QLabel* status_text;
48 QPushButton* resume_button;
49 QPushButton* toggle_breakpoint_button;
50
51 BreakPointModel* breakpoint_model;
52 QTreeView* breakpoint_list;
53};
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 000000000..bf5daf73d
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_breakpoints_p.hxx
@@ -0,0 +1,38 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include <QAbstractListModel>
10
11#include "video_core/debug_utils/debug_utils.h"
12
13class BreakPointModel : public QAbstractListModel {
14 Q_OBJECT
15
16public:
17 enum {
18 Role_IsEnabled = Qt::UserRole,
19 };
20
21 BreakPointModel(std::shared_ptr<Pica::DebugContext> context, QObject* parent);
22
23 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
24 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
25 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
26 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
27
28 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
29
30public slots:
31 void OnBreakPointHit(Pica::DebugContext::Event event);
32 void OnResumed();
33
34private:
35 std::weak_ptr<Pica::DebugContext> context_weak;
36 bool at_breakpoint;
37 Pica::DebugContext::Event active_breakpoint;
38};
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index 71dd166cd..7f97cf143 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -2,30 +2,171 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QLabel>
5#include <QListView> 6#include <QListView>
7#include <QMainWindow>
6#include <QPushButton> 8#include <QPushButton>
7#include <QVBoxLayout> 9#include <QVBoxLayout>
8#include <QTreeView> 10#include <QTreeView>
11#include <QSpinBox>
12#include <QComboBox>
13
14#include "video_core/pica.h"
15#include "video_core/math.h"
16
17#include "video_core/debug_utils/debug_utils.h"
9 18
10#include "graphics_cmdlists.hxx" 19#include "graphics_cmdlists.hxx"
11 20
12GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) 21#include "util/spinbox.hxx"
13{ 22
23QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
24 QImage decoded_image(info.width, info.height, QImage::Format_ARGB32);
25 for (int y = 0; y < info.height; ++y) {
26 for (int x = 0; x < info.width; ++x) {
27 Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info);
28 decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
29 }
30 }
31
32 return decoded_image;
33}
34
35class TextureInfoWidget : public QWidget {
36public:
37 TextureInfoWidget(u8* src, const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr) : QWidget(parent) {
38 QLabel* image_widget = new QLabel;
39 QPixmap image_pixmap = QPixmap::fromImage(LoadTexture(src, info));
40 image_pixmap = image_pixmap.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
41 image_widget->setPixmap(image_pixmap);
42
43 QVBoxLayout* layout = new QVBoxLayout;
44 layout->addWidget(image_widget);
45 setLayout(layout);
46 }
47};
48
49TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent)
50 : QDockWidget(tr("Texture 0x%1").arg(info.address, 8, 16, QLatin1Char('0'))),
51 info(info) {
52
53 QWidget* main_widget = new QWidget;
54
55 QLabel* image_widget = new QLabel;
56
57 connect(this, SIGNAL(UpdatePixmap(const QPixmap&)), image_widget, SLOT(setPixmap(const QPixmap&)));
58
59 CSpinBox* phys_address_spinbox = new CSpinBox;
60 phys_address_spinbox->SetBase(16);
61 phys_address_spinbox->SetRange(0, 0xFFFFFFFF);
62 phys_address_spinbox->SetPrefix("0x");
63 phys_address_spinbox->SetValue(info.address);
64 connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64)));
65
66 QComboBox* format_choice = new QComboBox;
67 format_choice->addItem(tr("RGBA8"));
68 format_choice->addItem(tr("RGB8"));
69 format_choice->addItem(tr("RGBA5551"));
70 format_choice->addItem(tr("RGB565"));
71 format_choice->addItem(tr("RGBA4"));
72 format_choice->setCurrentIndex(static_cast<int>(info.format));
73 connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int)));
74
75 QSpinBox* width_spinbox = new QSpinBox;
76 width_spinbox->setMaximum(65535);
77 width_spinbox->setValue(info.width);
78 connect(width_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnWidthChanged(int)));
79
80 QSpinBox* height_spinbox = new QSpinBox;
81 height_spinbox->setMaximum(65535);
82 height_spinbox->setValue(info.height);
83 connect(height_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnHeightChanged(int)));
84
85 QSpinBox* stride_spinbox = new QSpinBox;
86 stride_spinbox->setMaximum(65535 * 4);
87 stride_spinbox->setValue(info.stride);
88 connect(stride_spinbox, SIGNAL(valueChanged(int)), this, SLOT(OnStrideChanged(int)));
89
90 QVBoxLayout* main_layout = new QVBoxLayout;
91 main_layout->addWidget(image_widget);
92
93 {
94 QHBoxLayout* sub_layout = new QHBoxLayout;
95 sub_layout->addWidget(new QLabel(tr("Source Address:")));
96 sub_layout->addWidget(phys_address_spinbox);
97 main_layout->addLayout(sub_layout);
98 }
99
100 {
101 QHBoxLayout* sub_layout = new QHBoxLayout;
102 sub_layout->addWidget(new QLabel(tr("Format")));
103 sub_layout->addWidget(format_choice);
104 main_layout->addLayout(sub_layout);
105 }
106
107 {
108 QHBoxLayout* sub_layout = new QHBoxLayout;
109 sub_layout->addWidget(new QLabel(tr("Width:")));
110 sub_layout->addWidget(width_spinbox);
111 sub_layout->addStretch();
112 sub_layout->addWidget(new QLabel(tr("Height:")));
113 sub_layout->addWidget(height_spinbox);
114 sub_layout->addStretch();
115 sub_layout->addWidget(new QLabel(tr("Stride:")));
116 sub_layout->addWidget(stride_spinbox);
117 main_layout->addLayout(sub_layout);
118 }
119
120 main_widget->setLayout(main_layout);
121
122 emit UpdatePixmap(ReloadPixmap());
123
124 setWidget(main_widget);
125}
126
127void TextureInfoDockWidget::OnAddressChanged(qint64 value) {
128 info.address = value;
129 emit UpdatePixmap(ReloadPixmap());
130}
131
132void TextureInfoDockWidget::OnFormatChanged(int value) {
133 info.format = static_cast<Pica::Regs::TextureFormat>(value);
134 emit UpdatePixmap(ReloadPixmap());
135}
136
137void TextureInfoDockWidget::OnWidthChanged(int value) {
138 info.width = value;
139 emit UpdatePixmap(ReloadPixmap());
140}
141
142void TextureInfoDockWidget::OnHeightChanged(int value) {
143 info.height = value;
144 emit UpdatePixmap(ReloadPixmap());
145}
14 146
147void TextureInfoDockWidget::OnStrideChanged(int value) {
148 info.stride = value;
149 emit UpdatePixmap(ReloadPixmap());
15} 150}
16 151
17int GPUCommandListModel::rowCount(const QModelIndex& parent) const 152QPixmap TextureInfoDockWidget::ReloadPixmap() const {
18{ 153 u8* src = Memory::GetPointer(info.address);
154 return QPixmap::fromImage(LoadTexture(src, info));
155}
156
157GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) {
158
159}
160
161int GPUCommandListModel::rowCount(const QModelIndex& parent) const {
19 return pica_trace.writes.size(); 162 return pica_trace.writes.size();
20} 163}
21 164
22int GPUCommandListModel::columnCount(const QModelIndex& parent) const 165int GPUCommandListModel::columnCount(const QModelIndex& parent) const {
23{
24 return 2; 166 return 2;
25} 167}
26 168
27QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const 169QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const {
28{
29 if (!index.isValid()) 170 if (!index.isValid())
30 return QVariant(); 171 return QVariant();
31 172
@@ -36,21 +177,39 @@ QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const
36 if (role == Qt::DisplayRole) { 177 if (role == Qt::DisplayRole) {
37 QString content; 178 QString content;
38 if (index.column() == 0) { 179 if (index.column() == 0) {
39 content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str()); 180 QString content = QString::fromLatin1(Pica::Regs::GetCommandName(cmd.cmd_id).c_str());
40 content.append(" "); 181 content.append(" ");
182 return content;
41 } else if (index.column() == 1) { 183 } else if (index.column() == 1) {
42 content.append(QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0'))); 184 QString content = QString("%1 ").arg(cmd.hex, 8, 16, QLatin1Char('0'));
43 content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0'))); 185 content.append(QString("%1 ").arg(val, 8, 16, QLatin1Char('0')));
186 return content;
44 } 187 }
188 } else if (role == CommandIdRole) {
189 return QVariant::fromValue<int>(cmd.cmd_id.Value());
190 }
45 191
46 return QVariant(content); 192 return QVariant();
193}
194
195QVariant GPUCommandListModel::headerData(int section, Qt::Orientation orientation, int role) const {
196 switch(role) {
197 case Qt::DisplayRole:
198 {
199 if (section == 0) {
200 return tr("Command Name");
201 } else if (section == 1) {
202 return tr("Data");
203 }
204
205 break;
206 }
47 } 207 }
48 208
49 return QVariant(); 209 return QVariant();
50} 210}
51 211
52void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) 212void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) {
53{
54 beginResetModel(); 213 beginResetModel();
55 214
56 pica_trace = trace; 215 pica_trace = trace;
@@ -58,38 +217,82 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&
58 endResetModel(); 217 endResetModel();
59} 218}
60 219
220#define COMMAND_IN_RANGE(cmd_id, reg_name) \
221 (cmd_id >= PICA_REG_INDEX(reg_name) && \
222 cmd_id < PICA_REG_INDEX(reg_name) + sizeof(decltype(Pica::registers.reg_name)) / 4)
223
224void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) {
225 const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt();
226 if (COMMAND_IN_RANGE(command_id, texture0)) {
227 auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0,
228 Pica::registers.texture0_format);
229
230 // TODO: Instead, emit a signal here to be caught by the main window widget.
231 auto main_window = static_cast<QMainWindow*>(parent());
232 main_window->tabifyDockWidget(this, new TextureInfoDockWidget(info, main_window));
233 }
234}
235
236void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) {
237 QWidget* new_info_widget;
238
239 const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt();
240 if (COMMAND_IN_RANGE(command_id, texture0)) {
241 u8* src = Memory::GetPointer(Pica::registers.texture0.GetPhysicalAddress());
242 auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0,
243 Pica::registers.texture0_format);
244 new_info_widget = new TextureInfoWidget(src, info);
245 } else {
246 new_info_widget = new QWidget;
247 }
248
249 widget()->layout()->removeWidget(command_info_widget);
250 delete command_info_widget;
251 widget()->layout()->addWidget(new_info_widget);
252 command_info_widget = new_info_widget;
253}
254#undef COMMAND_IN_RANGE
61 255
62GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) 256GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) {
63{ 257 setObjectName("Pica Command List");
64 GPUCommandListModel* model = new GPUCommandListModel(this); 258 GPUCommandListModel* model = new GPUCommandListModel(this);
65 259
66 QWidget* main_widget = new QWidget; 260 QWidget* main_widget = new QWidget;
67 261
68 QTreeView* list_widget = new QTreeView; 262 list_widget = new QTreeView;
69 list_widget->setModel(model); 263 list_widget->setModel(model);
70 list_widget->setFont(QFont("monospace")); 264 list_widget->setFont(QFont("monospace"));
71 list_widget->setRootIsDecorated(false); 265 list_widget->setRootIsDecorated(false);
72 266
73 QPushButton* toggle_tracing = new QPushButton(tr("Start Tracing")); 267 connect(list_widget->selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),
268 this, SLOT(SetCommandInfo(const QModelIndex&)));
269 connect(list_widget, SIGNAL(doubleClicked(const QModelIndex&)),
270 this, SLOT(OnCommandDoubleClicked(const QModelIndex&)));
271
272 toggle_tracing = new QPushButton(tr("Start Tracing"));
74 273
75 connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing())); 274 connect(toggle_tracing, SIGNAL(clicked()), this, SLOT(OnToggleTracing()));
76 connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)), 275 connect(this, SIGNAL(TracingFinished(const Pica::DebugUtils::PicaTrace&)),
77 model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&))); 276 model, SLOT(OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace&)));
78 277
278 command_info_widget = new QWidget;
279
79 QVBoxLayout* main_layout = new QVBoxLayout; 280 QVBoxLayout* main_layout = new QVBoxLayout;
80 main_layout->addWidget(list_widget); 281 main_layout->addWidget(list_widget);
81 main_layout->addWidget(toggle_tracing); 282 main_layout->addWidget(toggle_tracing);
283 main_layout->addWidget(command_info_widget);
82 main_widget->setLayout(main_layout); 284 main_widget->setLayout(main_layout);
83 285
84 setWidget(main_widget); 286 setWidget(main_widget);
85} 287}
86 288
87void GPUCommandListWidget::OnToggleTracing() 289void GPUCommandListWidget::OnToggleTracing() {
88{
89 if (!Pica::DebugUtils::IsPicaTracing()) { 290 if (!Pica::DebugUtils::IsPicaTracing()) {
90 Pica::DebugUtils::StartPicaTracing(); 291 Pica::DebugUtils::StartPicaTracing();
292 toggle_tracing->setText(tr("Finish Tracing"));
91 } else { 293 } else {
92 pica_trace = Pica::DebugUtils::FinishPicaTracing(); 294 pica_trace = Pica::DebugUtils::FinishPicaTracing();
93 emit TracingFinished(*pica_trace); 295 emit TracingFinished(*pica_trace);
296 toggle_tracing->setText(tr("Start Tracing"));
94 } 297 }
95} 298}
diff --git a/src/citra_qt/debugger/graphics_cmdlists.hxx b/src/citra_qt/debugger/graphics_cmdlists.hxx
index 1523e724f..a459bba64 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.hxx
+++ b/src/citra_qt/debugger/graphics_cmdlists.hxx
@@ -10,16 +10,24 @@
10#include "video_core/gpu_debugger.h" 10#include "video_core/gpu_debugger.h"
11#include "video_core/debug_utils/debug_utils.h" 11#include "video_core/debug_utils/debug_utils.h"
12 12
13class QPushButton;
14class QTreeView;
15
13class GPUCommandListModel : public QAbstractListModel 16class GPUCommandListModel : public QAbstractListModel
14{ 17{
15 Q_OBJECT 18 Q_OBJECT
16 19
17public: 20public:
21 enum {
22 CommandIdRole = Qt::UserRole,
23 };
24
18 GPUCommandListModel(QObject* parent); 25 GPUCommandListModel(QObject* parent);
19 26
20 int columnCount(const QModelIndex& parent = QModelIndex()) const override; 27 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
21 int rowCount(const QModelIndex& parent = QModelIndex()) const override; 28 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
22 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 29 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
30 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
23 31
24public slots: 32public slots:
25 void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); 33 void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace);
@@ -37,10 +45,39 @@ public:
37 45
38public slots: 46public slots:
39 void OnToggleTracing(); 47 void OnToggleTracing();
48 void OnCommandDoubleClicked(const QModelIndex&);
49
50 void SetCommandInfo(const QModelIndex&);
40 51
41signals: 52signals:
42 void TracingFinished(const Pica::DebugUtils::PicaTrace&); 53 void TracingFinished(const Pica::DebugUtils::PicaTrace&);
43 54
44private: 55private:
45 std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace; 56 std::unique_ptr<Pica::DebugUtils::PicaTrace> pica_trace;
57
58 QTreeView* list_widget;
59 QWidget* command_info_widget;
60 QPushButton* toggle_tracing;
61};
62
63class TextureInfoDockWidget : public QDockWidget {
64 Q_OBJECT
65
66public:
67 TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr);
68
69signals:
70 void UpdatePixmap(const QPixmap& pixmap);
71
72private slots:
73 void OnAddressChanged(qint64 value);
74 void OnFormatChanged(int value);
75 void OnWidthChanged(int value);
76 void OnHeightChanged(int value);
77 void OnStrideChanged(int value);
78
79private:
80 QPixmap ReloadPixmap() const;
81
82 Pica::DebugUtils::TextureInfo info;
46}; 83};
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
new file mode 100644
index 000000000..ac47f298d
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_framebuffer.cpp
@@ -0,0 +1,282 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <QBoxLayout>
6#include <QComboBox>
7#include <QDebug>
8#include <QLabel>
9#include <QMetaType>
10#include <QPushButton>
11#include <QSpinBox>
12
13#include "video_core/pica.h"
14
15#include "graphics_framebuffer.hxx"
16
17#include "util/spinbox.hxx"
18
19BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context,
20 const QString& title, QWidget* parent)
21 : QDockWidget(title, parent), BreakPointObserver(debug_context)
22{
23 qRegisterMetaType<Pica::DebugContext::Event>("Pica::DebugContext::Event");
24
25 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed()));
26
27 // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
28 // care of delaying its handling to the GUI thread.
29 connect(this, SIGNAL(BreakPointHit(Pica::DebugContext::Event,void*)),
30 this, SLOT(OnBreakPointHit(Pica::DebugContext::Event,void*)),
31 Qt::BlockingQueuedConnection);
32}
33
34void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data)
35{
36 emit BreakPointHit(event, data);
37}
38
39void BreakPointObserverDock::OnPicaResume()
40{
41 emit Resumed();
42}
43
44
45GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
46 QWidget* parent)
47 : BreakPointObserverDock(debug_context, tr("Pica Framebuffer"), parent),
48 framebuffer_source(Source::PicaTarget)
49{
50 setObjectName("PicaFramebuffer");
51
52 framebuffer_source_list = new QComboBox;
53 framebuffer_source_list->addItem(tr("Active Render Target"));
54 framebuffer_source_list->addItem(tr("Custom"));
55 framebuffer_source_list->setCurrentIndex(static_cast<int>(framebuffer_source));
56
57 framebuffer_address_control = new CSpinBox;
58 framebuffer_address_control->SetBase(16);
59 framebuffer_address_control->SetRange(0, 0xFFFFFFFF);
60 framebuffer_address_control->SetPrefix("0x");
61
62 framebuffer_width_control = new QSpinBox;
63 framebuffer_width_control->setMinimum(1);
64 framebuffer_width_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
65
66 framebuffer_height_control = new QSpinBox;
67 framebuffer_height_control->setMinimum(1);
68 framebuffer_height_control->setMaximum(std::numeric_limits<int>::max()); // TODO: Find actual maximum
69
70 framebuffer_format_control = new QComboBox;
71 framebuffer_format_control->addItem(tr("RGBA8"));
72 framebuffer_format_control->addItem(tr("RGB8"));
73 framebuffer_format_control->addItem(tr("RGBA5551"));
74 framebuffer_format_control->addItem(tr("RGB565"));
75 framebuffer_format_control->addItem(tr("RGBA4"));
76
77 // TODO: This QLabel should shrink the image to the available space rather than just expanding...
78 framebuffer_picture_label = new QLabel;
79
80 auto enlarge_button = new QPushButton(tr("Enlarge"));
81
82 // Connections
83 connect(this, SIGNAL(Update()), this, SLOT(OnUpdate()));
84 connect(framebuffer_source_list, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferSourceChanged(int)));
85 connect(framebuffer_address_control, SIGNAL(ValueChanged(qint64)), this, SLOT(OnFramebufferAddressChanged(qint64)));
86 connect(framebuffer_width_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferWidthChanged(int)));
87 connect(framebuffer_height_control, SIGNAL(valueChanged(int)), this, SLOT(OnFramebufferHeightChanged(int)));
88 connect(framebuffer_format_control, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFramebufferFormatChanged(int)));
89
90 auto main_widget = new QWidget;
91 auto main_layout = new QVBoxLayout;
92 {
93 auto sub_layout = new QHBoxLayout;
94 sub_layout->addWidget(new QLabel(tr("Source:")));
95 sub_layout->addWidget(framebuffer_source_list);
96 main_layout->addLayout(sub_layout);
97 }
98 {
99 auto sub_layout = new QHBoxLayout;
100 sub_layout->addWidget(new QLabel(tr("Virtual Address:")));
101 sub_layout->addWidget(framebuffer_address_control);
102 main_layout->addLayout(sub_layout);
103 }
104 {
105 auto sub_layout = new QHBoxLayout;
106 sub_layout->addWidget(new QLabel(tr("Width:")));
107 sub_layout->addWidget(framebuffer_width_control);
108 main_layout->addLayout(sub_layout);
109 }
110 {
111 auto sub_layout = new QHBoxLayout;
112 sub_layout->addWidget(new QLabel(tr("Height:")));
113 sub_layout->addWidget(framebuffer_height_control);
114 main_layout->addLayout(sub_layout);
115 }
116 {
117 auto sub_layout = new QHBoxLayout;
118 sub_layout->addWidget(new QLabel(tr("Format:")));
119 sub_layout->addWidget(framebuffer_format_control);
120 main_layout->addLayout(sub_layout);
121 }
122 main_layout->addWidget(framebuffer_picture_label);
123 main_layout->addWidget(enlarge_button);
124 main_widget->setLayout(main_layout);
125 setWidget(main_widget);
126
127 // Load current data - TODO: Make sure this works when emulation is not running
128 emit Update();
129 widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint
130}
131
132void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data)
133{
134 emit Update();
135 widget()->setEnabled(true);
136}
137
138void GraphicsFramebufferWidget::OnResumed()
139{
140 widget()->setEnabled(false);
141}
142
143void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value)
144{
145 framebuffer_source = static_cast<Source>(new_value);
146 emit Update();
147}
148
149void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
150{
151 if (framebuffer_address != new_value) {
152 framebuffer_address = static_cast<unsigned>(new_value);
153
154 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
155 emit Update();
156 }
157}
158
159void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
160{
161 if (framebuffer_width != new_value) {
162 framebuffer_width = new_value;
163
164 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
165 emit Update();
166 }
167}
168
169void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
170{
171 if (framebuffer_height != new_value) {
172 framebuffer_height = new_value;
173
174 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
175 emit Update();
176 }
177}
178
179void GraphicsFramebufferWidget::OnFramebufferFormatChanged(int new_value)
180{
181 if (framebuffer_format != static_cast<Format>(new_value)) {
182 framebuffer_format = static_cast<Format>(new_value);
183
184 framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
185 emit Update();
186 }
187}
188
189void GraphicsFramebufferWidget::OnUpdate()
190{
191 QPixmap pixmap;
192
193 switch (framebuffer_source) {
194 case Source::PicaTarget:
195 {
196 // TODO: Store a reference to the registers in the debug context instead of accessing them directly...
197
198 auto framebuffer = Pica::registers.framebuffer;
199 using Framebuffer = decltype(framebuffer);
200
201 framebuffer_address = framebuffer.GetColorBufferAddress();
202 framebuffer_width = framebuffer.GetWidth();
203 framebuffer_height = framebuffer.GetHeight();
204 framebuffer_format = static_cast<Format>(framebuffer.color_format);
205
206 break;
207 }
208
209 case Source::Custom:
210 {
211 // Keep user-specified values
212 break;
213 }
214
215 default:
216 qDebug() << "Unknown framebuffer source " << static_cast<int>(framebuffer_source);
217 break;
218 }
219
220 // TODO: Implement a good way to visualize alpha components!
221 // TODO: Unify this decoding code with the texture decoder
222 switch (framebuffer_format) {
223 case Format::RGBA8:
224 {
225 QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
226 u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address);
227 for (int y = 0; y < framebuffer_height; ++y) {
228 for (int x = 0; x < framebuffer_width; ++x) {
229 u32 value = *(color_buffer + x + y * framebuffer_width);
230
231 decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/));
232 }
233 }
234 pixmap = QPixmap::fromImage(decoded_image);
235 break;
236 }
237
238 case Format::RGB8:
239 {
240 QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
241 u8* color_buffer = Memory::GetPointer(framebuffer_address);
242 for (int y = 0; y < framebuffer_height; ++y) {
243 for (int x = 0; x < framebuffer_width; ++x) {
244 u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width;
245
246 decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/));
247 }
248 }
249 pixmap = QPixmap::fromImage(decoded_image);
250 break;
251 }
252
253 case Format::RGBA5551:
254 {
255 QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32);
256 u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address);
257 for (int y = 0; y < framebuffer_height; ++y) {
258 for (int x = 0; x < framebuffer_width; ++x) {
259 u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2);
260 u8 r = (value >> 11) & 0x1F;
261 u8 g = (value >> 6) & 0x1F;
262 u8 b = (value >> 1) & 0x1F;
263 u8 a = value & 1;
264
265 decoded_image.setPixel(x, y, qRgba(r, g, b, 255/*a*/));
266 }
267 }
268 pixmap = QPixmap::fromImage(decoded_image);
269 break;
270 }
271
272 default:
273 qDebug() << "Unknown fb color format " << static_cast<int>(framebuffer_format);
274 break;
275 }
276
277 framebuffer_address_control->SetValue(framebuffer_address);
278 framebuffer_width_control->setValue(framebuffer_width);
279 framebuffer_height_control->setValue(framebuffer_height);
280 framebuffer_format_control->setCurrentIndex(static_cast<int>(framebuffer_format));
281 framebuffer_picture_label->setPixmap(pixmap);
282}
diff --git a/src/citra_qt/debugger/graphics_framebuffer.hxx b/src/citra_qt/debugger/graphics_framebuffer.hxx
new file mode 100644
index 000000000..1151ee7a1
--- /dev/null
+++ b/src/citra_qt/debugger/graphics_framebuffer.hxx
@@ -0,0 +1,92 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDockWidget>
8
9#include "video_core/debug_utils/debug_utils.h"
10
11class QComboBox;
12class QLabel;
13class QSpinBox;
14
15class CSpinBox;
16
17// Utility class which forwards calls to OnPicaBreakPointHit and OnPicaResume to public slots.
18// This is because the Pica breakpoint callbacks are called from a non-GUI thread, while
19// the widget usually wants to perform reactions in the GUI thread.
20class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver {
21 Q_OBJECT
22
23public:
24 BreakPointObserverDock(std::shared_ptr<Pica::DebugContext> debug_context, const QString& title,
25 QWidget* parent = nullptr);
26
27 void OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) override;
28 void OnPicaResume() override;
29
30private slots:
31 virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0;
32 virtual void OnResumed() = 0;
33
34signals:
35 void Resumed();
36 void BreakPointHit(Pica::DebugContext::Event event, void* data);
37};
38
39class GraphicsFramebufferWidget : public BreakPointObserverDock {
40 Q_OBJECT
41
42 using Event = Pica::DebugContext::Event;
43
44 enum class Source {
45 PicaTarget = 0,
46 Custom = 1,
47
48 // TODO: Add GPU framebuffer sources!
49 };
50
51 enum class Format {
52 RGBA8 = 0,
53 RGB8 = 1,
54 RGBA5551 = 2,
55 RGB565 = 3,
56 RGBA4 = 4,
57 };
58
59public:
60 GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr);
61
62public slots:
63 void OnFramebufferSourceChanged(int new_value);
64 void OnFramebufferAddressChanged(qint64 new_value);
65 void OnFramebufferWidthChanged(int new_value);
66 void OnFramebufferHeightChanged(int new_value);
67 void OnFramebufferFormatChanged(int new_value);
68 void OnUpdate();
69
70private slots:
71 void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override;
72 void OnResumed() override;
73
74signals:
75 void Update();
76
77private:
78
79 QComboBox* framebuffer_source_list;
80 CSpinBox* framebuffer_address_control;
81 QSpinBox* framebuffer_width_control;
82 QSpinBox* framebuffer_height_control;
83 QComboBox* framebuffer_format_control;
84
85 QLabel* framebuffer_picture_label;
86
87 Source framebuffer_source;
88 unsigned framebuffer_address;
89 unsigned framebuffer_width;
90 unsigned framebuffer_height;
91 Format framebuffer_format;
92};
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp
index 96ceed480..ed17ee4b4 100644
--- a/src/citra_qt/debugger/registers.cpp
+++ b/src/citra_qt/debugger/registers.cpp
@@ -46,18 +46,18 @@ void RegistersWidget::OnCPUStepped()
46 46
47 CSPR->setText(1, QString("0x%1").arg(app_core->GetCPSR(), 8, 16, QLatin1Char('0'))); 47 CSPR->setText(1, QString("0x%1").arg(app_core->GetCPSR(), 8, 16, QLatin1Char('0')));
48 CSPR->child(0)->setText(1, QString("b%1").arg(app_core->GetCPSR() & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode 48 CSPR->child(0)->setText(1, QString("b%1").arg(app_core->GetCPSR() & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
49 CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State 49 CSPR->child(1)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 5) & 0x1)); // T - State
50 CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable 50 CSPR->child(2)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 6) & 0x1)); // F - FIQ disable
51 CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable 51 CSPR->child(3)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 7) & 0x1)); // I - IRQ disable
52 CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort 52 CSPR->child(4)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 8) & 0x1)); // A - Imprecise abort
53 CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess 53 CSPR->child(5)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 9) & 0x1)); // E - Data endianess
54 CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM) 54 CSPR->child(6)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 10) & 0x3F)); // IT - If-Then state (DNM)
55 CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal 55 CSPR->child(7)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 16) & 0xF)); // GE - Greater-than-or-Equal
56 CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify 56 CSPR->child(8)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 20) & 0xF)); // DNM - Do not modify
57 CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state 57 CSPR->child(9)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 24) & 0x1)); // J - Java state
58 CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow 58 CSPR->child(10)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 27) & 0x1)); // Q - Sticky overflow
59 CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow 59 CSPR->child(11)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 28) & 0x1)); // V - Overflow
60 CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend 60 CSPR->child(12)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 29) & 0x1)); // C - Carry/Borrow/Extend
61 CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero 61 CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero
62 CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than 62 CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
63} 63}
diff --git a/src/citra_qt/debugger/registers.hxx b/src/citra_qt/debugger/registers.hxx
index 9645feb2a..4cca957ce 100644
--- a/src/citra_qt/debugger/registers.hxx
+++ b/src/citra_qt/debugger/registers.hxx
@@ -16,10 +16,10 @@ public slots:
16 void OnCPUStepped(); 16 void OnCPUStepped();
17 17
18private: 18private:
19 Ui::ARMRegisters cpu_regs_ui; 19 Ui::ARMRegisters cpu_regs_ui;
20 20
21 QTreeWidget* tree; 21 QTreeWidget* tree;
22 22
23 QTreeWidgetItem* registers; 23 QTreeWidgetItem* registers;
24 QTreeWidgetItem* CSPR; 24 QTreeWidgetItem* CSPR;
25}; 25};
diff --git a/src/citra_qt/hotkeys.cpp b/src/citra_qt/hotkeys.cpp
index bbaa4a8dc..5d0b52e4f 100644
--- a/src/citra_qt/hotkeys.cpp
+++ b/src/citra_qt/hotkeys.cpp
@@ -5,7 +5,7 @@
5 5
6struct Hotkey 6struct Hotkey
7{ 7{
8 Hotkey() : shortcut(NULL), context(Qt::WindowShortcut) {} 8 Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
9 9
10 QKeySequence keyseq; 10 QKeySequence keyseq;
11 QShortcut* shortcut; 11 QShortcut* shortcut;
@@ -81,7 +81,7 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
81 Hotkey& hk = hotkey_groups[group][action]; 81 Hotkey& hk = hotkey_groups[group][action];
82 82
83 if (!hk.shortcut) 83 if (!hk.shortcut)
84 hk.shortcut = new QShortcut(hk.keyseq, widget, NULL, NULL, hk.context); 84 hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
85 85
86 return hk.shortcut; 86 return hk.shortcut;
87} 87}
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 304c169b9..23d4925b8 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -1,3 +1,5 @@
1#include <thread>
2
1#include <QtGui> 3#include <QtGui>
2#include <QDesktopWidget> 4#include <QDesktopWidget>
3#include <QFileDialog> 5#include <QFileDialog>
@@ -5,8 +7,13 @@
5#include "main.hxx" 7#include "main.hxx"
6 8
7#include "common/common.h" 9#include "common/common.h"
10#include "common/logging/text_formatter.h"
11#include "common/logging/log.h"
12#include "common/logging/backend.h"
13#include "common/logging/filter.h"
8#include "common/platform.h" 14#include "common/platform.h"
9#include "common/log_manager.h" 15#include "common/scope_exit.h"
16
10#if EMU_PLATFORM == PLATFORM_LINUX 17#if EMU_PLATFORM == PLATFORM_LINUX
11#include <unistd.h> 18#include <unistd.h>
12#endif 19#endif
@@ -20,8 +27,11 @@
20#include "debugger/callstack.hxx" 27#include "debugger/callstack.hxx"
21#include "debugger/ramview.hxx" 28#include "debugger/ramview.hxx"
22#include "debugger/graphics.hxx" 29#include "debugger/graphics.hxx"
30#include "debugger/graphics_breakpoints.hxx"
23#include "debugger/graphics_cmdlists.hxx" 31#include "debugger/graphics_cmdlists.hxx"
32#include "debugger/graphics_framebuffer.hxx"
24 33
34#include "core/settings.h"
25#include "core/system.h" 35#include "core/system.h"
26#include "core/core.h" 36#include "core/core.h"
27#include "core/loader/loader.h" 37#include "core/loader/loader.h"
@@ -30,10 +40,10 @@
30 40
31#include "version.h" 41#include "version.h"
32 42
33
34GMainWindow::GMainWindow() 43GMainWindow::GMainWindow()
35{ 44{
36 LogManager::Init(); 45 Pica::g_debug_context = Pica::DebugContext::Construct();
46
37 Config config; 47 Config config;
38 48
39 ui.setupUi(this); 49 ui.setupUi(this);
@@ -62,12 +72,22 @@ GMainWindow::GMainWindow()
62 addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); 72 addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
63 graphicsCommandsWidget->hide(); 73 graphicsCommandsWidget->hide();
64 74
75 auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
76 addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
77 graphicsBreakpointsWidget->hide();
78
79 auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this);
80 addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget);
81 graphicsFramebufferWidget->hide();
82
65 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); 83 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
66 debug_menu->addAction(disasmWidget->toggleViewAction()); 84 debug_menu->addAction(disasmWidget->toggleViewAction());
67 debug_menu->addAction(registersWidget->toggleViewAction()); 85 debug_menu->addAction(registersWidget->toggleViewAction());
68 debug_menu->addAction(callstackWidget->toggleViewAction()); 86 debug_menu->addAction(callstackWidget->toggleViewAction());
69 debug_menu->addAction(graphicsWidget->toggleViewAction()); 87 debug_menu->addAction(graphicsWidget->toggleViewAction());
70 debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); 88 debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
89 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
90 debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
71 91
72 // Set default UI state 92 // Set default UI state
73 // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half 93 // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
@@ -112,7 +132,8 @@ GMainWindow::GMainWindow()
112 connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); 132 connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile()));
113 connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); 133 connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame()));
114 134
115 setWindowTitle(render_window->GetWindowTitle().c_str()); 135 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
136 setWindowTitle(window_title.c_str());
116 137
117 show(); 138 show();
118 139
@@ -125,24 +146,20 @@ GMainWindow::GMainWindow()
125GMainWindow::~GMainWindow() 146GMainWindow::~GMainWindow()
126{ 147{
127 // will get automatically deleted otherwise 148 // will get automatically deleted otherwise
128 if (render_window->parent() == NULL) 149 if (render_window->parent() == nullptr)
129 delete render_window; 150 delete render_window;
151
152 Pica::g_debug_context.reset();
130} 153}
131 154
132void GMainWindow::BootGame(std::string filename) 155void GMainWindow::BootGame(std::string filename)
133{ 156{
134 NOTICE_LOG(MASTER_LOG, "Citra starting...\n"); 157 LOG_INFO(Frontend, "Citra starting...\n");
135 System::Init(render_window); 158 System::Init(render_window);
136 159
137 if (Core::Init()) {
138 ERROR_LOG(MASTER_LOG, "Core initialization failed, exiting...");
139 Core::Stop();
140 exit(1);
141 }
142
143 // Load a game or die... 160 // Load a game or die...
144 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { 161 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
145 ERROR_LOG(BOOT, "Failed to load ROM!"); 162 LOG_CRITICAL(Frontend, "Failed to load ROM!");
146 } 163 }
147 164
148 disasmWidget->Init(); 165 disasmWidget->Init();
@@ -158,7 +175,7 @@ void GMainWindow::BootGame(std::string filename)
158 175
159void GMainWindow::OnMenuLoadFile() 176void GMainWindow::OnMenuLoadFile()
160{ 177{
161 QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.elf *.axf *.bin *.cci *.cxi)")); 178 QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
162 if (filename.size()) 179 if (filename.size())
163 BootGame(filename.toLatin1().data()); 180 BootGame(filename.toLatin1().data());
164} 181}
@@ -207,14 +224,14 @@ void GMainWindow::OnOpenHotkeysDialog()
207void GMainWindow::ToggleWindowMode() 224void GMainWindow::ToggleWindowMode()
208{ 225{
209 bool enable = ui.action_Popout_Window_Mode->isChecked(); 226 bool enable = ui.action_Popout_Window_Mode->isChecked();
210 if (enable && render_window->parent() != NULL) 227 if (enable && render_window->parent() != nullptr)
211 { 228 {
212 ui.horizontalLayout->removeWidget(render_window); 229 ui.horizontalLayout->removeWidget(render_window);
213 render_window->setParent(NULL); 230 render_window->setParent(nullptr);
214 render_window->setVisible(true); 231 render_window->setVisible(true);
215 render_window->RestoreGeometry(); 232 render_window->RestoreGeometry();
216 } 233 }
217 else if (!enable && render_window->parent() == NULL) 234 else if (!enable && render_window->parent() == nullptr)
218 { 235 {
219 render_window->BackupGeometry(); 236 render_window->BackupGeometry();
220 ui.horizontalLayout->addWidget(render_window); 237 ui.horizontalLayout->addWidget(render_window);
@@ -249,9 +266,21 @@ void GMainWindow::closeEvent(QCloseEvent* event)
249 266
250int __cdecl main(int argc, char* argv[]) 267int __cdecl main(int argc, char* argv[])
251{ 268{
269 std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
270 Log::Filter log_filter(Log::Level::Info);
271 std::thread logging_thread(Log::TextLoggingLoop, logger, &log_filter);
272 SCOPE_EXIT({
273 logger->Close();
274 logging_thread.join();
275 });
276
252 QApplication::setAttribute(Qt::AA_X11InitThreads); 277 QApplication::setAttribute(Qt::AA_X11InitThreads);
253 QApplication app(argc, argv); 278 QApplication app(argc, argv);
279
254 GMainWindow main_window; 280 GMainWindow main_window;
281 // After settings have been loaded by GMainWindow, apply the filter
282 log_filter.ParseFilterString(Settings::values.log_filter);
283
255 main_window.show(); 284 main_window.show();
256 return app.exec(); 285 return app.exec();
257} 286}
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp
new file mode 100644
index 000000000..9672168f5
--- /dev/null
+++ b/src/citra_qt/util/spinbox.cpp
@@ -0,0 +1,303 @@
1// Licensed under GPLv2+
2// Refer to the license.txt file included.
3
4
5// Copyright 2014 Tony Wasserka
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are met:
10//
11// * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// * Redistributions in binary form must reproduce the above copyright
14// notice, this list of conditions and the following disclaimer in the
15// documentation and/or other materials provided with the distribution.
16// * Neither the name of the owner nor the names of its contributors may
17// be used to endorse or promote products derived from this software
18// without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32#include <QLineEdit>
33#include <QRegExpValidator>
34
35#include "common/log.h"
36
37#include "spinbox.hxx"
38
39CSpinBox::CSpinBox(QWidget* parent) : QAbstractSpinBox(parent), base(10), min_value(-100), max_value(100), value(0), num_digits(0)
40{
41 // TODO: Might be nice to not immediately call the slot.
42 // Think of an address that is being replaced by a different one, in which case a lot
43 // invalid intermediate addresses would be read from during editing.
44 connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(OnEditingFinished()));
45
46 UpdateText();
47}
48
49void CSpinBox::SetValue(qint64 val)
50{
51 auto old_value = value;
52 value = std::max(std::min(val, max_value), min_value);
53
54 if (old_value != value) {
55 UpdateText();
56 emit ValueChanged(value);
57 }
58}
59
60void CSpinBox::SetRange(qint64 min, qint64 max)
61{
62 min_value = min;
63 max_value = max;
64
65 SetValue(value);
66 UpdateText();
67}
68
69void CSpinBox::stepBy(int steps)
70{
71 auto new_value = value;
72 // Scale number of steps by the currently selected digit
73 // TODO: Move this code elsewhere and enable it.
74 // TODO: Support for num_digits==0, too
75 // TODO: Support base!=16, too
76 // TODO: Make the cursor not jump back to the end of the line...
77 /*if (base == 16 && num_digits > 0) {
78 int digit = num_digits - (lineEdit()->cursorPosition() - prefix.length()) - 1;
79 digit = std::max(0, std::min(digit, num_digits - 1));
80 steps <<= digit * 4;
81 }*/
82
83 // Increment "new_value" by "steps", and perform annoying overflow checks, too.
84 if (steps < 0 && new_value + steps > new_value) {
85 new_value = std::numeric_limits<qint64>::min();
86 } else if (steps > 0 && new_value + steps < new_value) {
87 new_value = std::numeric_limits<qint64>::max();
88 } else {
89 new_value += steps;
90 }
91
92 SetValue(new_value);
93 UpdateText();
94}
95
96QAbstractSpinBox::StepEnabled CSpinBox::stepEnabled() const
97{
98 StepEnabled ret = StepNone;
99
100 if (value > min_value)
101 ret |= StepDownEnabled;
102
103 if (value < max_value)
104 ret |= StepUpEnabled;
105
106 return ret;
107}
108
109void CSpinBox::SetBase(int base)
110{
111 this->base = base;
112
113 UpdateText();
114}
115
116void CSpinBox::SetNumDigits(int num_digits)
117{
118 this->num_digits = num_digits;
119
120 UpdateText();
121}
122
123void CSpinBox::SetPrefix(const QString& prefix)
124{
125 this->prefix = prefix;
126
127 UpdateText();
128}
129
130void CSpinBox::SetSuffix(const QString& suffix)
131{
132 this->suffix = suffix;
133
134 UpdateText();
135}
136
137static QString StringToInputMask(const QString& input) {
138 QString mask = input;
139
140 // ... replace any special characters by their escaped counterparts ...
141 mask.replace("\\", "\\\\");
142 mask.replace("A", "\\A");
143 mask.replace("a", "\\a");
144 mask.replace("N", "\\N");
145 mask.replace("n", "\\n");
146 mask.replace("X", "\\X");
147 mask.replace("x", "\\x");
148 mask.replace("9", "\\9");
149 mask.replace("0", "\\0");
150 mask.replace("D", "\\D");
151 mask.replace("d", "\\d");
152 mask.replace("#", "\\#");
153 mask.replace("H", "\\H");
154 mask.replace("h", "\\h");
155 mask.replace("B", "\\B");
156 mask.replace("b", "\\b");
157 mask.replace(">", "\\>");
158 mask.replace("<", "\\<");
159 mask.replace("!", "\\!");
160
161 return mask;
162}
163
164void CSpinBox::UpdateText()
165{
166 // If a fixed number of digits is used, we put the line edit in insertion mode by setting an
167 // input mask.
168 QString mask;
169 if (num_digits != 0) {
170 mask += StringToInputMask(prefix);
171
172 // For base 10 and negative range, demand a single sign character
173 if (HasSign())
174 mask += "X"; // identified as "-" or "+" in the validator
175
176 // Uppercase digits greater than 9.
177 mask += ">";
178
179 // The greatest signed 64-bit number has 19 decimal digits.
180 // TODO: Could probably make this more generic with some logarithms.
181 // For reference, unsigned 64-bit can have up to 20 decimal digits.
182 int digits = (num_digits != 0) ? num_digits
183 : (base == 16) ? 16
184 : (base == 10) ? 19
185 : 0xFF; // fallback case...
186
187 // Match num_digits digits
188 // Digits irrelevant to the chosen number base are filtered in the validator
189 mask += QString("H").repeated(std::max(num_digits, 1));
190
191 // Switch off case conversion
192 mask += "!";
193
194 mask += StringToInputMask(suffix);
195 }
196 lineEdit()->setInputMask(mask);
197
198 // Set new text without changing the cursor position. This will cause the cursor to briefly
199 // appear at the end of the line and then to jump back to its original position. That's
200 // a bit ugly, but better than having setText() move the cursor permanently all the time.
201 int cursor_position = lineEdit()->cursorPosition();
202 lineEdit()->setText(TextFromValue());
203 lineEdit()->setCursorPosition(cursor_position);
204}
205
206QString CSpinBox::TextFromValue()
207{
208 return prefix
209 + QString(HasSign() ? ((value < 0) ? "-" : "+") : "")
210 + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper()
211 + suffix;
212}
213
214qint64 CSpinBox::ValueFromText()
215{
216 unsigned strpos = prefix.length();
217
218 QString num_string = text().mid(strpos, text().length() - strpos - suffix.length());
219 return num_string.toLongLong(nullptr, base);
220}
221
222bool CSpinBox::HasSign() const
223{
224 return base == 10 && min_value < 0;
225}
226
227void CSpinBox::OnEditingFinished()
228{
229 // Only update for valid input
230 QString input = lineEdit()->text();
231 int pos = 0;
232 if (QValidator::Acceptable == validate(input, pos))
233 SetValue(ValueFromText());
234}
235
236QValidator::State CSpinBox::validate(QString& input, int& pos) const
237{
238 if (!prefix.isEmpty() && input.left(prefix.length()) != prefix)
239 return QValidator::Invalid;
240
241 unsigned strpos = prefix.length();
242
243 // Empty "numbers" allowed as intermediate values
244 if (strpos >= input.length() - HasSign() - suffix.length())
245 return QValidator::Intermediate;
246
247 _dbg_assert_(Frontend, base <= 10 || base == 16);
248 QString regexp;
249
250 // Demand sign character for negative ranges
251 if (HasSign())
252 regexp += "[+\\-]";
253
254 // Match digits corresponding to the chosen number base.
255 regexp += QString("[0-%1").arg(std::min(base, 9));
256 if (base == 16) {
257 regexp += "a-fA-F";
258 }
259 regexp += "]";
260
261 // Specify number of digits
262 if (num_digits > 0) {
263 regexp += QString("{%1}").arg(num_digits);
264 } else {
265 regexp += "+";
266 }
267
268 // Match string
269 QRegExp num_regexp(regexp);
270 int num_pos = strpos;
271 QString sub_input = input.mid(strpos, input.length() - strpos - suffix.length());
272
273 if (!num_regexp.exactMatch(sub_input) && num_regexp.matchedLength() == 0)
274 return QValidator::Invalid;
275
276 sub_input = sub_input.left(num_regexp.matchedLength());
277 bool ok;
278 qint64 val = sub_input.toLongLong(&ok, base);
279
280 if (!ok)
281 return QValidator::Invalid;
282
283 // Outside boundaries => don't accept
284 if (val < min_value || val > max_value)
285 return QValidator::Invalid;
286
287 // Make sure we are actually at the end of this string...
288 strpos += num_regexp.matchedLength();
289
290 if (!suffix.isEmpty() && input.mid(strpos) != suffix) {
291 return QValidator::Invalid;
292 } else {
293 strpos += suffix.length();
294 }
295
296 if (strpos != input.length())
297 return QValidator::Invalid;
298
299 // At this point we can say for sure that the input is fine. Let's fix it up a bit though
300 input.replace(num_pos, sub_input.length(), sub_input.toUpper());
301
302 return QValidator::Acceptable;
303}
diff --git a/src/citra_qt/util/spinbox.hxx b/src/citra_qt/util/spinbox.hxx
new file mode 100644
index 000000000..68f5b9894
--- /dev/null
+++ b/src/citra_qt/util/spinbox.hxx
@@ -0,0 +1,88 @@
1// Licensed under GPLv2+
2// Refer to the license.txt file included.
3
4
5// Copyright 2014 Tony Wasserka
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are met:
10//
11// * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// * Redistributions in binary form must reproduce the above copyright
14// notice, this list of conditions and the following disclaimer in the
15// documentation and/or other materials provided with the distribution.
16// * Neither the name of the owner nor the names of its contributors may
17// be used to endorse or promote products derived from this software
18// without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32
33#pragma once
34
35#include <QAbstractSpinBox>
36#include <QtGlobal>
37
38class QVariant;
39
40/**
41 * A custom spin box widget with enhanced functionality over Qt's QSpinBox
42 */
43class CSpinBox : public QAbstractSpinBox {
44 Q_OBJECT
45
46public:
47 CSpinBox(QWidget* parent = nullptr);
48
49 void stepBy(int steps) override;
50 StepEnabled stepEnabled() const override;
51
52 void SetValue(qint64 val);
53
54 void SetRange(qint64 min, qint64 max);
55
56 void SetBase(int base);
57
58 void SetPrefix(const QString& prefix);
59 void SetSuffix(const QString& suffix);
60
61 void SetNumDigits(int num_digits);
62
63 QValidator::State validate(QString& input, int& pos) const override;
64
65signals:
66 void ValueChanged(qint64 val);
67
68private slots:
69 void OnEditingFinished();
70
71private:
72 void UpdateText();
73
74 bool HasSign() const;
75
76 QString TextFromValue();
77 qint64 ValueFromText();
78
79 qint64 min_value, max_value;
80
81 qint64 value;
82
83 QString prefix, suffix;
84
85 int base;
86
87 int num_digits;
88};
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 9d5a90762..15989708d 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
3 3
4set(SRCS 4set(SRCS
5 break_points.cpp 5 break_points.cpp
6 console_listener.cpp
7 emu_window.cpp 6 emu_window.cpp
8 extended_trace.cpp 7 extended_trace.cpp
9 file_search.cpp 8 file_search.cpp
10 file_util.cpp 9 file_util.cpp
11 hash.cpp 10 hash.cpp
12 key_map.cpp 11 key_map.cpp
13 log_manager.cpp 12 logging/filter.cpp
13 logging/text_formatter.cpp
14 logging/backend.cpp
14 math_util.cpp 15 math_util.cpp
15 mem_arena.cpp 16 mem_arena.cpp
16 memory_util.cpp 17 memory_util.cpp
@@ -32,7 +33,7 @@ set(HEADERS
32 common_funcs.h 33 common_funcs.h
33 common_paths.h 34 common_paths.h
34 common_types.h 35 common_types.h
35 console_listener.h 36 concurrent_ring_buffer.h
36 cpu_detect.h 37 cpu_detect.h
37 debug_interface.h 38 debug_interface.h
38 emu_window.h 39 emu_window.h
@@ -44,13 +45,17 @@ set(HEADERS
44 key_map.h 45 key_map.h
45 linear_disk_cache.h 46 linear_disk_cache.h
46 log.h 47 log.h
47 log_manager.h 48 logging/text_formatter.h
49 logging/filter.h
50 logging/log.h
51 logging/backend.h
48 math_util.h 52 math_util.h
49 mem_arena.h 53 mem_arena.h
50 memory_util.h 54 memory_util.h
51 msg_handler.h 55 msg_handler.h
52 platform.h 56 platform.h
53 scm_rev.h 57 scm_rev.h
58 scope_exit.h
54 string_util.h 59 string_util.h
55 swap.h 60 swap.h
56 symbols.h 61 symbols.h
diff --git a/src/common/break_points.cpp b/src/common/break_points.cpp
index 25528b864..587dbf40a 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,
180 { 180 {
181 if (Log) 181 if (Log)
182 { 182 {
183 INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", 183 LOG_DEBUG(Debug_Breakpoint, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
184 pc, debug_interface->getDescription(pc).c_str(), 184 pc, debug_interface->getDescription(pc).c_str(),
185 write ? "Write" : "Read", size*8, size*2, iValue, addr, 185 write ? "Write" : "Read", size*8, size*2, iValue, addr,
186 debug_interface->getDescription(addr).c_str() 186 debug_interface->getDescription(addr).c_str()
diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h
index dc8ac1fd9..39a14dc81 100644
--- a/src/common/chunk_file.h
+++ b/src/common/chunk_file.h
@@ -51,7 +51,7 @@ public:
51 PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { 51 PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) {
52 } 52 }
53 ~PointerWrapSection(); 53 ~PointerWrapSection();
54 54
55 bool operator == (const int &v) const { return ver_ == v; } 55 bool operator == (const int &v) const { return ver_ == v; }
56 bool operator != (const int &v) const { return ver_ != v; } 56 bool operator != (const int &v) const { return ver_ != v; }
57 bool operator <= (const int &v) const { return ver_ <= v; } 57 bool operator <= (const int &v) const { return ver_ <= v; }
@@ -154,7 +154,7 @@ public:
154 Do(foundVersion); 154 Do(foundVersion);
155 155
156 if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { 156 if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) {
157 WARN_LOG(COMMON, "Savestate failure: wrong version %d found for %s", foundVersion, title); 157 LOG_ERROR(Common, "Savestate failure: wrong version %d found for %s", foundVersion, title);
158 SetError(ERROR_FAILURE); 158 SetError(ERROR_FAILURE);
159 return PointerWrapSection(*this, -1, title); 159 return PointerWrapSection(*this, -1, title);
160 } 160 }
@@ -178,7 +178,14 @@ public:
178 case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break; 178 case MODE_READ: if (memcmp(data, *ptr, size) != 0) return false; break;
179 case MODE_WRITE: memcpy(*ptr, data, size); break; 179 case MODE_WRITE: memcpy(*ptr, data, size); break;
180 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything 180 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
181 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; 181 case MODE_VERIFY:
182 for (int i = 0; i < size; i++) {
183 _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
184 "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
185 ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
186 (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
187 }
188 break;
182 default: break; // throw an error? 189 default: break; // throw an error?
183 } 190 }
184 (*ptr) += size; 191 (*ptr) += size;
@@ -191,12 +198,19 @@ public:
191 case MODE_READ: memcpy(data, *ptr, size); break; 198 case MODE_READ: memcpy(data, *ptr, size); break;
192 case MODE_WRITE: memcpy(*ptr, data, size); break; 199 case MODE_WRITE: memcpy(*ptr, data, size); break;
193 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything 200 case MODE_MEASURE: break; // MODE_MEASURE - don't need to do anything
194 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; 201 case MODE_VERIFY:
202 for (int i = 0; i < size; i++) {
203 _dbg_assert_msg_(Common, ((u8*)data)[i] == (*ptr)[i],
204 "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
205 ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i],
206 (*ptr)[i], (*ptr)[i], &(*ptr)[i]);
207 }
208 break;
195 default: break; // throw an error? 209 default: break; // throw an error?
196 } 210 }
197 (*ptr) += size; 211 (*ptr) += size;
198 } 212 }
199 213
200 template<class K, class T> 214 template<class K, class T>
201 void Do(std::map<K, T *> &x) 215 void Do(std::map<K, T *> &x)
202 { 216 {
@@ -204,11 +218,11 @@ public:
204 { 218 {
205 for (auto it = x.begin(), end = x.end(); it != end; ++it) 219 for (auto it = x.begin(), end = x.end(); it != end; ++it)
206 { 220 {
207 if (it->second != NULL) 221 if (it->second != nullptr)
208 delete it->second; 222 delete it->second;
209 } 223 }
210 } 224 }
211 T *dv = NULL; 225 T *dv = nullptr;
212 DoMap(x, dv); 226 DoMap(x, dv);
213 } 227 }
214 228
@@ -264,11 +278,11 @@ public:
264 { 278 {
265 for (auto it = x.begin(), end = x.end(); it != end; ++it) 279 for (auto it = x.begin(), end = x.end(); it != end; ++it)
266 { 280 {
267 if (it->second != NULL) 281 if (it->second != nullptr)
268 delete it->second; 282 delete it->second;
269 } 283 }
270 } 284 }
271 T *dv = NULL; 285 T *dv = nullptr;
272 DoMultimap(x, dv); 286 DoMultimap(x, dv);
273 } 287 }
274 288
@@ -320,7 +334,7 @@ public:
320 template<class T> 334 template<class T>
321 void Do(std::vector<T *> &x) 335 void Do(std::vector<T *> &x)
322 { 336 {
323 T *dv = NULL; 337 T *dv = nullptr;
324 DoVector(x, dv); 338 DoVector(x, dv);
325 } 339 }
326 340
@@ -364,12 +378,12 @@ public:
364 if (vec_size > 0) 378 if (vec_size > 0)
365 DoArray(&x[0], vec_size); 379 DoArray(&x[0], vec_size);
366 } 380 }
367 381
368 // Store deques. 382 // Store deques.
369 template<class T> 383 template<class T>
370 void Do(std::deque<T *> &x) 384 void Do(std::deque<T *> &x)
371 { 385 {
372 T *dv = NULL; 386 T *dv = nullptr;
373 DoDeque(x, dv); 387 DoDeque(x, dv);
374 } 388 }
375 389
@@ -395,7 +409,7 @@ public:
395 template<class T> 409 template<class T>
396 void Do(std::list<T *> &x) 410 void Do(std::list<T *> &x)
397 { 411 {
398 T *dv = NULL; 412 T *dv = nullptr;
399 Do(x, dv); 413 Do(x, dv);
400 } 414 }
401 415
@@ -433,7 +447,7 @@ public:
433 { 447 {
434 for (auto it = x.begin(), end = x.end(); it != end; ++it) 448 for (auto it = x.begin(), end = x.end(); it != end; ++it)
435 { 449 {
436 if (*it != NULL) 450 if (*it != nullptr)
437 delete *it; 451 delete *it;
438 } 452 }
439 } 453 }
@@ -476,26 +490,31 @@ public:
476 break; 490 break;
477 491
478 default: 492 default:
479 ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode); 493 LOG_ERROR(Common, "Savestate error: invalid mode %d.", mode);
480 } 494 }
481 } 495 }
482 496
483 // Store strings. 497 // Store strings.
484 void Do(std::string &x) 498 void Do(std::string &x)
485 { 499 {
486 int stringLen = (int)x.length() + 1; 500 int stringLen = (int)x.length() + 1;
487 Do(stringLen); 501 Do(stringLen);
488 502
489 switch (mode) { 503 switch (mode) {
490 case MODE_READ: x = (char*)*ptr; break; 504 case MODE_READ: x = (char*)*ptr; break;
491 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; 505 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
492 case MODE_MEASURE: break; 506 case MODE_MEASURE: break;
493 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; 507 case MODE_VERIFY:
508 _dbg_assert_msg_(Common,
509 !strcmp(x.c_str(), (char*)*ptr),
510 "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n",
511 x.c_str(), (char*)*ptr, ptr);
512 break;
494 } 513 }
495 (*ptr) += stringLen; 514 (*ptr) += stringLen;
496 } 515 }
497 516
498 void Do(std::wstring &x) 517 void Do(std::wstring &x)
499 { 518 {
500 int stringLen = sizeof(wchar_t)*((int)x.length() + 1); 519 int stringLen = sizeof(wchar_t)*((int)x.length() + 1);
501 Do(stringLen); 520 Do(stringLen);
@@ -504,7 +523,11 @@ public:
504 case MODE_READ: x = (wchar_t*)*ptr; break; 523 case MODE_READ: x = (wchar_t*)*ptr; break;
505 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break; 524 case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
506 case MODE_MEASURE: break; 525 case MODE_MEASURE: break;
507 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; 526 case MODE_VERIFY:
527 _dbg_assert_msg_(Common, x == (wchar_t*)*ptr,
528 "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n",
529 x.c_str(), (wchar_t*)*ptr, ptr);
530 break;
508 } 531 }
509 (*ptr) += stringLen; 532 (*ptr) += stringLen;
510 } 533 }
@@ -518,7 +541,7 @@ public:
518 void DoClass(T *&x) { 541 void DoClass(T *&x) {
519 if (mode == MODE_READ) 542 if (mode == MODE_READ)
520 { 543 {
521 if (x != NULL) 544 if (x != nullptr)
522 delete x; 545 delete x;
523 x = new T(); 546 x = new T();
524 } 547 }
@@ -534,7 +557,7 @@ public:
534 void Do(T &x) { 557 void Do(T &x) {
535 DoHelper<T>::Do(this, x); 558 DoHelper<T>::Do(this, x);
536 } 559 }
537 560
538 template<class T> 561 template<class T>
539 void DoPOD(T &x) { 562 void DoPOD(T &x) {
540 DoHelper<T>::Do(this, x); 563 DoHelper<T>::Do(this, x);
@@ -567,7 +590,7 @@ public:
567 { 590 {
568 if (mode == MODE_READ) 591 if (mode == MODE_READ)
569 { 592 {
570 cur->next = 0; 593 cur->next = nullptr;
571 list_cur = cur; 594 list_cur = cur;
572 if (prev) 595 if (prev)
573 prev->next = cur; 596 prev->next = cur;
@@ -586,13 +609,13 @@ public:
586 if (mode == MODE_READ) 609 if (mode == MODE_READ)
587 { 610 {
588 if (prev) 611 if (prev)
589 prev->next = 0; 612 prev->next = nullptr;
590 if (list_end) 613 if (list_end)
591 *list_end = prev; 614 *list_end = prev;
592 if (list_cur) 615 if (list_cur)
593 { 616 {
594 if (list_start == list_cur) 617 if (list_start == list_cur)
595 list_start = 0; 618 list_start = nullptr;
596 do 619 do
597 { 620 {
598 LinkedListItem<T>* next = list_cur->next; 621 LinkedListItem<T>* next = list_cur->next;
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index c18afe119..67b3679b0 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common_types.h"
8#include <cstdlib>
9
7#ifdef _WIN32 10#ifdef _WIN32
8#define SLEEP(x) Sleep(x) 11#define SLEEP(x) Sleep(x)
9#else 12#else
@@ -17,7 +20,7 @@ template<> struct CompileTimeAssert<true> {};
17#define b2(x) ( (x) | ( (x) >> 1) ) 20#define b2(x) ( (x) | ( (x) >> 1) )
18#define b4(x) ( b2(x) | ( b2(x) >> 2) ) 21#define b4(x) ( b2(x) | ( b2(x) >> 2) )
19#define b8(x) ( b4(x) | ( b4(x) >> 4) ) 22#define b8(x) ( b4(x) | ( b4(x) >> 4) )
20#define b16(x) ( b8(x) | ( b8(x) >> 8) ) 23#define b16(x) ( b8(x) | ( b8(x) >> 8) )
21#define b32(x) (b16(x) | (b16(x) >>16) ) 24#define b32(x) (b16(x) | (b16(x) >>16) )
22#define ROUND_UP_POW2(x) (b32(x - 1) + 1) 25#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
23 26
@@ -73,18 +76,20 @@ inline u64 _rotr64(u64 x, unsigned int shift){
73} 76}
74 77
75#else // WIN32 78#else // WIN32
79#include <locale.h>
80
76// Function Cross-Compatibility 81// Function Cross-Compatibility
77 #define strcasecmp _stricmp 82 #define strcasecmp _stricmp
78 #define strncasecmp _strnicmp 83 #define strncasecmp _strnicmp
79 #define unlink _unlink 84 #define unlink _unlink
80 #define snprintf _snprintf 85 #define snprintf _snprintf
81 #define vscprintf _vscprintf 86 #define vscprintf _vscprintf
82 87
83// Locale Cross-Compatibility 88// Locale Cross-Compatibility
84 #define locale_t _locale_t 89 #define locale_t _locale_t
85 #define freelocale _free_locale 90 #define freelocale _free_locale
86 #define newlocale(mask, locale, base) _create_locale(mask, locale) 91 #define newlocale(mask, locale, base) _create_locale(mask, locale)
87 92
88 #define LC_GLOBAL_LOCALE ((locale_t)-1) 93 #define LC_GLOBAL_LOCALE ((locale_t)-1)
89 #define LC_ALL_MASK LC_ALL 94 #define LC_ALL_MASK LC_ALL
90 #define LC_COLLATE_MASK LC_COLLATE 95 #define LC_COLLATE_MASK LC_COLLATE
@@ -92,7 +97,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){
92 #define LC_MONETARY_MASK LC_MONETARY 97 #define LC_MONETARY_MASK LC_MONETARY
93 #define LC_NUMERIC_MASK LC_NUMERIC 98 #define LC_NUMERIC_MASK LC_NUMERIC
94 #define LC_TIME_MASK LC_TIME 99 #define LC_TIME_MASK LC_TIME
95 100
96 inline locale_t uselocale(locale_t new_locale) 101 inline locale_t uselocale(locale_t new_locale)
97 { 102 {
98 // Retrieve the current per thread locale setting 103 // Retrieve the current per thread locale setting
@@ -106,7 +111,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){
106 // Restore the global locale 111 // Restore the global locale
107 _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); 112 _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
108 } 113 }
109 else if(new_locale != NULL) 114 else if(new_locale != nullptr)
110 { 115 {
111 // Configure the thread to set the locale only for this thread 116 // Configure the thread to set the locale only for this thread
112 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); 117 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
@@ -168,8 +173,8 @@ inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
168inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} 173inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
169inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);} 174inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
170#elif _M_ARM 175#elif _M_ARM
171inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;} 176inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;}
172inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;} 177inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
173inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);} 178inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);}
174#elif __linux__ 179#elif __linux__
175inline u16 swap16(u16 _data) {return bswap_16(_data);} 180inline u16 swap16(u16 _data) {return bswap_16(_data);}
@@ -226,7 +231,7 @@ template <typename T>
226inline T FromBigEndian(T data) 231inline T FromBigEndian(T data)
227{ 232{
228 //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types"); 233 //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types");
229 234
230 swap<sizeof(data)>(reinterpret_cast<u8*>(&data)); 235 swap<sizeof(data)>(reinterpret_cast<u8*>(&data));
231 return data; 236 return data;
232} 237}
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index ae08d082a..a86889756 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -29,19 +29,6 @@
29 #endif 29 #endif
30#endif 30#endif
31 31
32// Shared data dirs (Sys and shared User for linux)
33#ifdef _WIN32
34 #define SYSDATA_DIR "sys"
35#else
36 #ifdef DATA_DIR
37 #define SYSDATA_DIR DATA_DIR "sys"
38 #define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP
39 #else
40 #define SYSDATA_DIR "sys"
41 #define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP
42 #endif
43#endif
44
45// Dirs in both User and Sys 32// Dirs in both User and Sys
46#define EUR_DIR "EUR" 33#define EUR_DIR "EUR"
47#define USA_DIR "USA" 34#define USA_DIR "USA"
@@ -53,6 +40,8 @@
53#define MAPS_DIR "maps" 40#define MAPS_DIR "maps"
54#define CACHE_DIR "cache" 41#define CACHE_DIR "cache"
55#define SDMC_DIR "sdmc" 42#define SDMC_DIR "sdmc"
43#define SAVEDATA_DIR "savedata"
44#define SYSDATA_DIR "sysdata"
56#define SHADERCACHE_DIR "shader_cache" 45#define SHADERCACHE_DIR "shader_cache"
57#define STATESAVES_DIR "state_saves" 46#define STATESAVES_DIR "state_saves"
58#define SCREENSHOTS_DIR "screenShots" 47#define SCREENSHOTS_DIR "screenShots"
@@ -70,6 +59,9 @@
70#define DEBUGGER_CONFIG "debugger.ini" 59#define DEBUGGER_CONFIG "debugger.ini"
71#define LOGGER_CONFIG "logger.ini" 60#define LOGGER_CONFIG "logger.ini"
72 61
62// Sys files
63#define SHARED_FONT "shared_font.bin"
64
73// Files in the directory returned by GetUserPath(D_LOGS_IDX) 65// Files in the directory returned by GetUserPath(D_LOGS_IDX)
74#define MAIN_LOG "emu.log" 66#define MAIN_LOG "emu.log"
75 67
diff --git a/src/common/common_types.h b/src/common/common_types.h
index 7ce6b2240..c74c74f0f 100644
--- a/src/common/common_types.h
+++ b/src/common/common_types.h
@@ -22,7 +22,7 @@
22 * http://code.google.com/p/gekko-gc-emu/ 22 * http://code.google.com/p/gekko-gc-emu/
23 */ 23 */
24 24
25#pragma once 25#pragma once
26 26
27#include <cmath> 27#include <cmath>
28#include <cstdint> 28#include <cstdint>
@@ -41,12 +41,10 @@ typedef std::int64_t s64; ///< 64-bit signed int
41typedef float f32; ///< 32-bit floating point 41typedef float f32; ///< 32-bit floating point
42typedef double f64; ///< 64-bit floating point 42typedef double f64; ///< 64-bit floating point
43 43
44#include "common/common.h"
45
46/// Union for fast 16-bit type casting 44/// Union for fast 16-bit type casting
47union t16 { 45union t16 {
48 u8 _u8[2]; ///< 8-bit unsigned char(s) 46 u8 _u8[2]; ///< 8-bit unsigned char(s)
49 u16 _u16; ///< 16-bit unsigned shorts(s) 47 u16 _u16; ///< 16-bit unsigned shorts(s)
50}; 48};
51 49
52/// Union for fast 32-bit type casting 50/// 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 000000000..2951d93db
--- /dev/null
+++ b/src/common/concurrent_ring_buffer.h
@@ -0,0 +1,164 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <condition_variable>
9#include <cstdint>
10#include <mutex>
11#include <thread>
12
13#include "common/common.h" // for NonCopyable
14#include "common/log.h" // for _dbg_assert_
15
16namespace Common {
17
18/**
19 * A MPMC (Multiple-Producer Multiple-Consumer) concurrent ring buffer. This data structure permits
20 * multiple threads to push and pop from a queue of bounded size.
21 */
22template <typename T, size_t ArraySize>
23class ConcurrentRingBuffer : private NonCopyable {
24public:
25 /// Value returned by the popping functions when the queue has been closed.
26 static const size_t QUEUE_CLOSED = -1;
27
28 ConcurrentRingBuffer() {}
29
30 ~ConcurrentRingBuffer() {
31 // If for whatever reason the queue wasn't completely drained, destroy the left over items.
32 for (size_t i = reader_index, end = writer_index; i != end; i = (i + 1) % ArraySize) {
33 Data()[i].~T();
34 }
35 }
36
37 /**
38 * Pushes a value to the queue. If the queue is full, this method will block. Does nothing if
39 * the queue is closed.
40 */
41 void Push(T val) {
42 std::unique_lock<std::mutex> lock(mutex);
43 if (closed) {
44 return;
45 }
46
47 // If the buffer is full, wait
48 writer.wait(lock, [&]{
49 return (writer_index + 1) % ArraySize != reader_index;
50 });
51
52 T* item = &Data()[writer_index];
53 new (item) T(std::move(val));
54
55 writer_index = (writer_index + 1) % ArraySize;
56
57 // Wake up waiting readers
58 lock.unlock();
59 reader.notify_one();
60 }
61
62 /**
63 * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will not
64 * block, and might return 0 values if there are no elements in the queue when it is called.
65 *
66 * @return The number of elements stored in `dest`. If the queue has been closed, returns
67 * `QUEUE_CLOSED`.
68 */
69 size_t Pop(T* dest, size_t dest_len) {
70 std::unique_lock<std::mutex> lock(mutex);
71 if (closed && !CanRead()) {
72 return QUEUE_CLOSED;
73 }
74 return PopInternal(dest, dest_len);
75 }
76
77 /**
78 * Pops up to `dest_len` items from the queue, storing them in `dest`. This function will block
79 * if there are no elements in the queue when it is called.
80 *
81 * @return The number of elements stored in `dest`. If the queue has been closed, returns
82 * `QUEUE_CLOSED`.
83 */
84 size_t BlockingPop(T* dest, size_t dest_len) {
85 std::unique_lock<std::mutex> lock(mutex);
86 if (closed && !CanRead()) {
87 return QUEUE_CLOSED;
88 }
89
90 while (!CanRead()) {
91 reader.wait(lock);
92 if (closed && !CanRead()) {
93 return QUEUE_CLOSED;
94 }
95 }
96 _dbg_assert_(Common, CanRead());
97 return PopInternal(dest, dest_len);
98 }
99
100 /**
101 * Closes the queue. After calling this method, `Push` operations won't have any effect, and
102 * `PopMany` and `PopManyBlock` will start returning `QUEUE_CLOSED`. This is intended to allow
103 * a graceful shutdown of all consumers.
104 */
105 void Close() {
106 std::unique_lock<std::mutex> lock(mutex);
107 closed = true;
108 // We need to wake up any reader that are waiting for an item that will never come.
109 lock.unlock();
110 reader.notify_all();
111 }
112
113 /// Returns true if `Close()` has been called.
114 bool IsClosed() const {
115 return closed;
116 }
117
118private:
119 size_t PopInternal(T* dest, size_t dest_len) {
120 size_t output_count = 0;
121 while (output_count < dest_len && CanRead()) {
122 _dbg_assert_(Common, CanRead());
123
124 T* item = &Data()[reader_index];
125 T out_val = std::move(*item);
126 item->~T();
127
128 size_t prev_index = (reader_index + ArraySize - 1) % ArraySize;
129 reader_index = (reader_index + 1) % ArraySize;
130 if (writer_index == prev_index) {
131 writer.notify_one();
132 }
133 dest[output_count++] = std::move(out_val);
134 }
135 return output_count;
136 }
137
138 bool CanRead() const {
139 return reader_index != writer_index;
140 }
141
142 T* Data() {
143 return static_cast<T*>(static_cast<void*>(&storage));
144 }
145
146 /// Storage for entries
147 typename std::aligned_storage<ArraySize * sizeof(T),
148 std::alignment_of<T>::value>::type storage;
149
150 /// Data is valid in the half-open interval [reader, writer). If they are `QUEUE_CLOSED` then the
151 /// queue has been closed.
152 size_t writer_index = 0, reader_index = 0;
153 // True if the queue has been closed.
154 bool closed = false;
155
156 /// Mutex that protects the entire data structure.
157 std::mutex mutex;
158 /// Signaling wakes up reader which is waiting for storage to be non-empty.
159 std::condition_variable reader;
160 /// Signaling wakes up writer which is waiting for storage to be non-full.
161 std::condition_variable writer;
162};
163
164} // namespace
diff --git a/src/common/console_listener.cpp b/src/common/console_listener.cpp
deleted file mode 100644
index 53f20d754..000000000
--- a/src/common/console_listener.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#ifdef _WIN32
8#include <windows.h>
9#include <array>
10#endif
11
12#include "common/common.h"
13#include "common/log_manager.h" // Common
14#include "common/console_listener.h" // Common
15
16ConsoleListener::ConsoleListener()
17{
18#ifdef _WIN32
19 hConsole = NULL;
20 bUseColor = true;
21#else
22 bUseColor = isatty(fileno(stdout));
23#endif
24}
25
26ConsoleListener::~ConsoleListener()
27{
28 Close();
29}
30
31// 100, 100, "Dolphin Log Console"
32// Open console window - width and height is the size of console window
33// Name is the window title
34void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title)
35{
36#ifdef _WIN32
37 if (!GetConsoleWindow())
38 {
39 // Open the console window and create the window handle for GetStdHandle()
40 AllocConsole();
41 // Hide
42 if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE);
43 // Save the window handle that AllocConsole() created
44 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
45 // Set the console window title
46 SetConsoleTitle(Common::UTF8ToTStr(Title).c_str());
47 // Set letter space
48 LetterSpace(80, 4000);
49 //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true);
50 }
51 else
52 {
53 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
54 }
55#endif
56}
57
58void ConsoleListener::UpdateHandle()
59{
60#ifdef _WIN32
61 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
62#endif
63}
64
65// Close the console window and close the eventual file handle
66void ConsoleListener::Close()
67{
68#ifdef _WIN32
69 if (hConsole == NULL)
70 return;
71 FreeConsole();
72 hConsole = NULL;
73#else
74 fflush(NULL);
75#endif
76}
77
78bool ConsoleListener::IsOpen()
79{
80#ifdef _WIN32
81 return (hConsole != NULL);
82#else
83 return true;
84#endif
85}
86
87/*
88 LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are
89 dependent on each other, that's the reason for the additional checks.
90*/
91void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst)
92{
93#ifdef _WIN32
94 BOOL SB, SW;
95 if (BufferFirst)
96 {
97 // Change screen buffer size
98 COORD Co = {BufferWidth, BufferHeight};
99 SB = SetConsoleScreenBufferSize(hConsole, Co);
100 // Change the screen buffer window size
101 SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom
102 SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
103 }
104 else
105 {
106 // Change the screen buffer window size
107 SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom
108 SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
109 // Change screen buffer size
110 COORD Co = {BufferWidth, BufferHeight};
111 SB = SetConsoleScreenBufferSize(hConsole, Co);
112 }
113#endif
114}
115void ConsoleListener::LetterSpace(int Width, int Height)
116{
117#ifdef _WIN32
118 // Get console info
119 CONSOLE_SCREEN_BUFFER_INFO ConInfo;
120 GetConsoleScreenBufferInfo(hConsole, &ConInfo);
121
122 //
123 int OldBufferWidth = ConInfo.dwSize.X;
124 int OldBufferHeight = ConInfo.dwSize.Y;
125 int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left);
126 int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top);
127 //
128 int NewBufferWidth = Width;
129 int NewBufferHeight = Height;
130 int NewScreenWidth = NewBufferWidth - 1;
131 int NewScreenHeight = OldScreenHeight;
132
133 // Width
134 BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1));
135 // Height
136 BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1));
137
138 // Resize the window too
139 //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true);
140#endif
141}
142#ifdef _WIN32
143COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth)
144{
145 COORD Ret = {0, 0};
146 // Full rows
147 int Step = (int)floor((float)BytesRead / (float)BufferWidth);
148 Ret.Y += Step;
149 // Partial row
150 Ret.X = BytesRead - (BufferWidth * Step);
151 return Ret;
152}
153#endif
154void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize)
155{
156#ifdef _WIN32
157 // Check size
158 if (Width < 8 || Height < 12) return;
159
160 bool DBef = true;
161 bool DAft = true;
162 std::string SLog = "";
163
164 const HWND hWnd = GetConsoleWindow();
165 const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
166
167 // Get console info
168 CONSOLE_SCREEN_BUFFER_INFO ConInfo;
169 GetConsoleScreenBufferInfo(hConsole, &ConInfo);
170 DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y;
171
172 // ---------------------------------------------------------------------
173 // Save the current text
174 // ------------------------
175 DWORD cCharsRead = 0;
176 COORD coordScreen = { 0, 0 };
177
178 static const int MAX_BYTES = 1024 * 16;
179
180 std::vector<std::array<TCHAR, MAX_BYTES>> Str;
181 std::vector<std::array<WORD, MAX_BYTES>> Attr;
182
183 // ReadConsoleOutputAttribute seems to have a limit at this level
184 static const int ReadBufferSize = MAX_BYTES - 32;
185
186 DWORD cAttrRead = ReadBufferSize;
187 DWORD BytesRead = 0;
188 while (BytesRead < BufferSize)
189 {
190 Str.resize(Str.size() + 1);
191 if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead))
192 SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
193
194 Attr.resize(Attr.size() + 1);
195 if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead))
196 SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
197
198 // Break on error
199 if (cAttrRead == 0) break;
200 BytesRead += cAttrRead;
201 coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X);
202 }
203 // Letter space
204 int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f);
205 int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f);
206 int LBufWidth = LWidth + 1;
207 int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth);
208 // Change screen buffer size
209 LetterSpace(LBufWidth, LBufHeight);
210
211
212 ClearScreen(true);
213 coordScreen.Y = 0;
214 coordScreen.X = 0;
215 DWORD cCharsWritten = 0;
216
217 int BytesWritten = 0;
218 DWORD cAttrWritten = 0;
219 for (size_t i = 0; i < Attr.size(); i++)
220 {
221 if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten))
222 SLog += Common::StringFromFormat("WriteConsoleOutputCharacter error");
223 if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten))
224 SLog += Common::StringFromFormat("WriteConsoleOutputAttribute error");
225
226 BytesWritten += cAttrWritten;
227 coordScreen = GetCoordinates(BytesWritten, LBufWidth);
228 }
229
230 const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X;
231 COORD Coo = GetCoordinates(OldCursor, LBufWidth);
232 SetConsoleCursorPosition(hConsole, Coo);
233
234 if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str());
235
236 // Resize the window too
237 if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true);
238#endif
239}
240
241void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
242{
243#if defined(_WIN32)
244 WORD Color;
245
246 switch (Level)
247 {
248 case OS_LEVEL: // light yellow
249 Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
250 break;
251 case NOTICE_LEVEL: // light green
252 Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
253 break;
254 case ERROR_LEVEL: // light red
255 Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
256 break;
257 case WARNING_LEVEL: // light purple
258 Color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
259 break;
260 case INFO_LEVEL: // cyan
261 Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
262 break;
263 case DEBUG_LEVEL: // gray
264 Color = FOREGROUND_INTENSITY;
265 break;
266 default: // off-white
267 Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
268 break;
269 }
270 SetConsoleTextAttribute(hConsole, Color);
271 printf(Text);
272#else
273 char ColorAttr[16] = "";
274 char ResetAttr[16] = "";
275
276 if (bUseColor)
277 {
278 strcpy(ResetAttr, "\033[0m");
279 switch (Level)
280 {
281 case NOTICE_LEVEL: // light green
282 strcpy(ColorAttr, "\033[92m");
283 break;
284 case ERROR_LEVEL: // light red
285 strcpy(ColorAttr, "\033[91m");
286 break;
287 case WARNING_LEVEL: // light yellow
288 strcpy(ColorAttr, "\033[93m");
289 break;
290 default:
291 break;
292 }
293 }
294 fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr);
295#endif
296}
297// Clear console screen
298void ConsoleListener::ClearScreen(bool Cursor)
299{
300#if defined(_WIN32)
301 COORD coordScreen = { 0, 0 };
302 DWORD cCharsWritten;
303 CONSOLE_SCREEN_BUFFER_INFO csbi;
304 DWORD dwConSize;
305
306 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
307
308 GetConsoleScreenBufferInfo(hConsole, &csbi);
309 dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
310 // Write space to the entire console
311 FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
312 GetConsoleScreenBufferInfo(hConsole, &csbi);
313 FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
314 // Reset cursor
315 if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen);
316#endif
317}
318
319
diff --git a/src/common/console_listener.h b/src/common/console_listener.h
deleted file mode 100644
index ebd90a105..000000000
--- a/src/common/console_listener.h
+++ /dev/null
@@ -1,38 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/log_manager.h"
8
9#ifdef _WIN32
10#include <windows.h>
11#endif
12
13class ConsoleListener : public LogListener
14{
15public:
16 ConsoleListener();
17 ~ConsoleListener();
18
19 void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console");
20 void UpdateHandle();
21 void Close();
22 bool IsOpen();
23 void LetterSpace(int Width, int Height);
24 void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst);
25 void PixelSpace(int Left, int Top, int Width, int Height, bool);
26#ifdef _WIN32
27 COORD GetCoordinates(int BytesRead, int BufferWidth);
28#endif
29 void Log(LogTypes::LOG_LEVELS, const char *Text) override;
30 void ClearScreen(bool Cursor = true);
31
32private:
33#ifdef _WIN32
34 HWND GetHwnd(void);
35 HANDLE hConsole;
36#endif
37 bool bUseColor;
38};
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index 4d09acb8b..4cb94fed1 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -9,17 +9,33 @@
9#include "common/string_util.h" 9#include "common/string_util.h"
10#include "common/key_map.h" 10#include "common/key_map.h"
11 11
12// Abstraction class used to provide an interface between emulation code and the frontend (e.g. SDL, 12/**
13// QGLWidget, GLFW, etc...) 13 * Abstraction class used to provide an interface between emulation code and the frontend
14 * (e.g. SDL, QGLWidget, GLFW, etc...).
15 *
16 * Design notes on the interaction between EmuWindow and the emulation core:
17 * - Generally, decisions on anything visible to the user should be left up to the GUI.
18 * For example, the emulation core should not try to dictate some window title or size.
19 * This stuff is not the core's business and only causes problems with regards to thread-safety
20 * anyway.
21 * - Under certain circumstances, it may be desirable for the core to politely request the GUI
22 * to set e.g. a minimum window size. However, the GUI should always be free to ignore any
23 * such hints.
24 * - EmuWindow may expose some of its state as read-only to the emulation core, however care
25 * should be taken to make sure the provided information is self-consistent. This requires
26 * some sort of synchronization (most of this is still a TODO).
27 * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
28 * re-read the upper points again and think about it if you don't see this.
29 */
14class EmuWindow 30class EmuWindow
15{ 31{
16
17public: 32public:
18 /// Data structure to store an emuwindow configuration 33 /// Data structure to store emuwindow configuration
19 struct WindowConfig { 34 struct WindowConfig {
20 bool fullscreen; 35 bool fullscreen;
21 int res_width; 36 int res_width;
22 int res_height; 37 int res_height;
38 std::pair<unsigned,unsigned> min_client_area_size;
23 }; 39 };
24 40
25 /// Swap buffers to display the next frame 41 /// Swap buffers to display the next frame
@@ -42,52 +58,96 @@ public:
42 /// Signals a key release action to the HID module 58 /// Signals a key release action to the HID module
43 static void KeyReleased(KeyMap::HostDeviceKey key); 59 static void KeyReleased(KeyMap::HostDeviceKey key);
44 60
45 WindowConfig GetConfig() const { 61 /**
46 return m_config; 62 * Returns currently active configuration.
63 * @note Accesses to the returned object need not be consistent because it may be modified in another thread
64 */
65 const WindowConfig& GetActiveConfig() const {
66 return active_config;
47 } 67 }
48 68
69 /**
70 * Requests the internal configuration to be replaced by the specified argument at some point in the future.
71 * @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.
72 */
49 void SetConfig(const WindowConfig& val) { 73 void SetConfig(const WindowConfig& val) {
50 m_config = val; 74 config = val;
51 }
52
53 int GetClientAreaWidth() const {
54 return m_client_area_width;
55 } 75 }
56 76
57 void SetClientAreaWidth(const int val) { 77 /**
58 m_client_area_width = val; 78 * Gets the framebuffer size in pixels.
79 * @note This method is thread-safe
80 */
81 const std::pair<unsigned,unsigned> GetFramebufferSize() const {
82 return framebuffer_size;
59 } 83 }
60 84
61 int GetClientAreaHeight() const { 85 /**
62 return m_client_area_height; 86 * Gets window client area width in logical coordinates.
87 * @note For high-DPI systems, this is smaller than the framebuffer size.
88 * @note This method is thread-safe
89 */
90 std::pair<unsigned,unsigned> GetClientAreaSize() const {
91 return std::make_pair(client_area_width, client_area_height);
63 } 92 }
64 93
65 void SetClientAreaHeight(const int val) { 94protected:
66 m_client_area_height = val; 95 EmuWindow()
96 {
97 // TODO: Find a better place to set this.
98 config.min_client_area_size = std::make_pair(400u, 480u);
99 active_config = config;
67 } 100 }
101 virtual ~EmuWindow() {}
68 102
69 std::string GetWindowTitle() const { 103 /**
70 return m_window_title; 104 * Processes any pending configuration changes from the last SetConfig call.
105 * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
106 * field changed.
107 * @note Implementations will usually want to call this from the GUI thread.
108 * @todo Actually call this in existing implementations.
109 */
110 void ProcessConfigurationChanges() {
111 // TODO: For proper thread safety, we should eventually implement a proper
112 // multiple-writer/single-reader queue...
113
114 if (config.min_client_area_size != active_config.min_client_area_size) {
115 OnMinimalClientAreaChangeRequest(config.min_client_area_size);
116 config.min_client_area_size = active_config.min_client_area_size;
117 }
71 } 118 }
72 119
73 void SetWindowTitle(std::string val) { 120 /**
74 m_window_title = val; 121 * Update internal framebuffer size with the given parameter.
122 * @note EmuWindow implementations will usually use this in window resize event handlers.
123 */
124 void NotifyFramebufferSizeChanged(const std::pair<unsigned,unsigned>& size) {
125 framebuffer_size = size;
75 } 126 }
76 127
77protected: 128 /**
78 EmuWindow(): 129 * Update internal client area size with the given parameter.
79 m_client_area_width(640), 130 * @note EmuWindow implementations will usually use this in window resize event handlers.
80 m_client_area_height(480), 131 */
81 m_window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)) 132 void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) {
82 {} 133 client_area_width = size.first;
83 virtual ~EmuWindow() {} 134 client_area_height = size.second;
135 }
84 136
85 std::string m_window_title; ///< Current window title, should be used by window impl. 137private:
138 /**
139 * Handler called when the minimal client area was requested to be changed via SetConfig.
140 * For the request to be honored, EmuWindow implementations will usually reimplement this function.
141 */
142 virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
143 // By default, ignore this request and do nothing.
144 }
86 145
87 int m_client_area_width; ///< Current client width, should be set by window impl. 146 std::pair<unsigned,unsigned> framebuffer_size;
88 int m_client_area_height; ///< Current client height, should be set by window impl.
89 147
90private: 148 unsigned client_area_width; ///< Current client width, should be set by window impl.
91 WindowConfig m_config; ///< Internal configuration 149 unsigned client_area_height; ///< Current client height, should be set by window impl.
92 150
151 WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
152 WindowConfig active_config; ///< Internal active configuration
93}; 153};
diff --git a/src/common/extended_trace.cpp b/src/common/extended_trace.cpp
index 9cd0398ed..cf7c346d4 100644
--- a/src/common/extended_trace.cpp
+++ b/src/common/extended_trace.cpp
@@ -29,7 +29,7 @@ using namespace std;
29void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) 29void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
30{ 30{
31#if defined(UNICODE)||defined(_UNICODE) 31#if defined(UNICODE)||defined(_UNICODE)
32 ULONG index = 0; 32 ULONG index = 0;
33 PCSTR lpAct = lpszIn; 33 PCSTR lpAct = lpszIn;
34 34
35 for( ; ; lpAct++ ) 35 for( ; ; lpAct++ )
@@ -37,7 +37,7 @@ void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
37 lpszOut[index++] = (TCHAR)(*lpAct); 37 lpszOut[index++] = (TCHAR)(*lpAct);
38 if ( *lpAct == 0 ) 38 if ( *lpAct == 0 )
39 break; 39 break;
40 } 40 }
41#else 41#else
42 // This is trivial :) 42 // This is trivial :)
43 strcpy( lpszOut, lpszIn ); 43 strcpy( lpszOut, lpszIn );
@@ -82,7 +82,7 @@ static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath )
82 } 82 }
83 83
84 // Add user defined path 84 // Add user defined path
85 if ( lpszIniPath != NULL ) 85 if ( lpszIniPath != nullptr )
86 if ( lpszIniPath[0] != '\0' ) 86 if ( lpszIniPath[0] != '\0' )
87 { 87 {
88 strcat( lpszSymbolPath, ";" ); 88 strcat( lpszSymbolPath, ";" );
@@ -101,7 +101,7 @@ BOOL InitSymInfo( PCSTR lpszInitialSymbolPath )
101 CHAR lpszSymbolPath[BUFFERSIZE]; 101 CHAR lpszSymbolPath[BUFFERSIZE];
102 DWORD symOptions = SymGetOptions(); 102 DWORD symOptions = SymGetOptions();
103 103
104 symOptions |= SYMOPT_LOAD_LINES; 104 symOptions |= SYMOPT_LOAD_LINES;
105 symOptions &= ~SYMOPT_UNDNAME; 105 symOptions &= ~SYMOPT_UNDNAME;
106 SymSetOptions( symOptions ); 106 SymSetOptions( symOptions );
107 InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); 107 InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath );
@@ -138,7 +138,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
138 DWORD dwSymSize = 10000; 138 DWORD dwSymSize = 10000;
139 TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); 139 TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
140 CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; 140 CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
141 LPTSTR lpszParamSep = NULL; 141 LPTSTR lpszParamSep = nullptr;
142 LPTSTR lpszParsed = lpszUnDSymbol; 142 LPTSTR lpszParsed = lpszUnDSymbol;
143 PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); 143 PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
144 144
@@ -153,15 +153,15 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
153#ifndef _M_X64 153#ifndef _M_X64
154 DWORD dwDisp = 0; 154 DWORD dwDisp = 0;
155 if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) 155 if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
156#else 156#else
157 //makes it compile but hell im not sure if this works... 157 //makes it compile but hell im not sure if this works...
158 DWORD64 dwDisp = 0; 158 DWORD64 dwDisp = 0;
159 if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) 159 if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) )
160#endif 160#endif
161 { 161 {
162 // Make the symbol readable for humans 162 // Make the symbol readable for humans
163 UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, 163 UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE,
164 UNDNAME_COMPLETE | 164 UNDNAME_COMPLETE |
165 UNDNAME_NO_THISTYPE | 165 UNDNAME_NO_THISTYPE |
166 UNDNAME_NO_SPECIAL_SYMS | 166 UNDNAME_NO_SPECIAL_SYMS |
167 UNDNAME_NO_MEMBER_TYPE | 167 UNDNAME_NO_MEMBER_TYPE |
@@ -187,13 +187,13 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
187 187
188 // Let's go through the stack, and modify the function prototype, and insert the actual 188 // Let's go through the stack, and modify the function prototype, and insert the actual
189 // parameter values from the stack 189 // parameter values from the stack
190 if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL) 190 if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr)
191 { 191 {
192 ULONG index = 0; 192 ULONG index = 0;
193 for( ; ; index++ ) 193 for( ; ; index++ )
194 { 194 {
195 lpszParamSep = _tcschr( lpszParsed, _T(',') ); 195 lpszParamSep = _tcschr( lpszParsed, _T(',') );
196 if ( lpszParamSep == NULL ) 196 if ( lpszParamSep == nullptr )
197 break; 197 break;
198 198
199 *lpszParamSep = _T('\0'); 199 *lpszParamSep = _T('\0');
@@ -205,7 +205,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
205 } 205 }
206 206
207 lpszParamSep = _tcschr( lpszParsed, _T(')') ); 207 lpszParamSep = _tcschr( lpszParsed, _T(')') );
208 if ( lpszParamSep != NULL ) 208 if ( lpszParamSep != nullptr )
209 { 209 {
210 *lpszParamSep = _T('\0'); 210 *lpszParamSep = _T('\0');
211 211
@@ -219,7 +219,7 @@ static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, L
219 _tcscat( lpszSymbol, lpszParsed ); 219 _tcscat( lpszSymbol, lpszParsed );
220 220
221 ret = TRUE; 221 ret = TRUE;
222 } 222 }
223 GlobalFree( pSym ); 223 GlobalFree( pSym );
224 224
225 return ret; 225 return ret;
@@ -248,7 +248,7 @@ static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
248 PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); 248 PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
249 TCHAR fname[_MAX_FNAME]; 249 TCHAR fname[_MAX_FNAME];
250 TCHAR ext[_MAX_EXT]; 250 TCHAR ext[_MAX_EXT];
251 _tsplitpath(lpszFileName, NULL, NULL, fname, ext); 251 _tsplitpath(lpszFileName, nullptr, nullptr, fname, ext);
252 _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); 252 _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber );
253 ret = TRUE; 253 ret = TRUE;
254 } 254 }
@@ -325,23 +325,23 @@ void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file )
325 325
326 PrintFunctionAndSourceInfo(file, callStack); 326 PrintFunctionAndSourceInfo(file, callStack);
327 327
328 for( ULONG index = 0; ; index++ ) 328 for( ULONG index = 0; ; index++ )
329 { 329 {
330 bResult = StackWalk( 330 bResult = StackWalk(
331 IMAGE_FILE_MACHINE_I386, 331 IMAGE_FILE_MACHINE_I386,
332 hProcess, 332 hProcess,
333 hThread, 333 hThread,
334 &callStack, 334 &callStack,
335 NULL, 335 nullptr,
336 NULL, 336 nullptr,
337 SymFunctionTableAccess, 337 SymFunctionTableAccess,
338 SymGetModuleBase, 338 SymGetModuleBase,
339 NULL); 339 nullptr);
340 340
341 if ( index == 0 ) 341 if ( index == 0 )
342 continue; 342 continue;
343 343
344 if( !bResult || callStack.AddrFrame.Offset == 0 ) 344 if( !bResult || callStack.AddrFrame.Offset == 0 )
345 break; 345 break;
346 346
347 PrintFunctionAndSourceInfo(file, callStack); 347 PrintFunctionAndSourceInfo(file, callStack);
@@ -382,23 +382,23 @@ void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip,
382 382
383 PrintFunctionAndSourceInfo(file, callStack); 383 PrintFunctionAndSourceInfo(file, callStack);
384 384
385 for( ULONG index = 0; ; index++ ) 385 for( ULONG index = 0; ; index++ )
386 { 386 {
387 bResult = StackWalk( 387 bResult = StackWalk(
388 IMAGE_FILE_MACHINE_I386, 388 IMAGE_FILE_MACHINE_I386,
389 hProcess, 389 hProcess,
390 hThread, 390 hThread,
391 &callStack, 391 &callStack,
392 NULL, 392 nullptr,
393 NULL, 393 nullptr,
394 SymFunctionTableAccess, 394 SymFunctionTableAccess,
395 SymGetModuleBase, 395 SymGetModuleBase,
396 NULL); 396 nullptr);
397 397
398 if ( index == 0 ) 398 if ( index == 0 )
399 continue; 399 continue;
400 400
401 if( !bResult || callStack.AddrFrame.Offset == 0 ) 401 if( !bResult || callStack.AddrFrame.Offset == 0 )
402 break; 402 break;
403 403
404 PrintFunctionAndSourceInfo(file, callStack); 404 PrintFunctionAndSourceInfo(file, callStack);
diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h
index 78a8f561d..b426e6596 100644
--- a/src/common/fifo_queue.h
+++ b/src/common/fifo_queue.h
@@ -45,7 +45,7 @@ public:
45 // create the element, add it to the queue 45 // create the element, add it to the queue
46 m_write_ptr->current = new T(std::forward<Arg>(t)); 46 m_write_ptr->current = new T(std::forward<Arg>(t));
47 // set the next pointer to a new element ptr 47 // set the next pointer to a new element ptr
48 // then advance the write pointer 48 // then advance the write pointer
49 m_write_ptr = m_write_ptr->next = new ElementPtr(); 49 m_write_ptr = m_write_ptr->next = new ElementPtr();
50 Common::AtomicIncrement(m_size); 50 Common::AtomicIncrement(m_size);
51 } 51 }
@@ -57,7 +57,7 @@ public:
57 // advance the read pointer 57 // advance the read pointer
58 m_read_ptr = m_read_ptr->next; 58 m_read_ptr = m_read_ptr->next;
59 // set the next element to NULL to stop the recursive deletion 59 // set the next element to NULL to stop the recursive deletion
60 tmpptr->next = NULL; 60 tmpptr->next = nullptr;
61 delete tmpptr; // this also deletes the element 61 delete tmpptr; // this also deletes the element
62 } 62 }
63 63
@@ -86,7 +86,7 @@ private:
86 class ElementPtr 86 class ElementPtr
87 { 87 {
88 public: 88 public:
89 ElementPtr() : current(NULL), next(NULL) {} 89 ElementPtr() : current(nullptr), next(nullptr) {}
90 90
91 ~ElementPtr() 91 ~ElementPtr()
92 { 92 {
diff --git a/src/common/file_search.cpp b/src/common/file_search.cpp
index 63580f688..bfb54ce72 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&
43 bool bkeepLooping = true; 43 bool bkeepLooping = true;
44 44
45 while (bkeepLooping) 45 while (bkeepLooping)
46 { 46 {
47 if (findData.cFileName[0] != '.') 47 if (findData.cFileName[0] != '.')
48 { 48 {
49 std::string strFilename; 49 std::string strFilename;
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 35da07306..42cdf3262 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -88,7 +88,7 @@ bool IsDirectory(const std::string &filename)
88#endif 88#endif
89 89
90 if (result < 0) { 90 if (result < 0) {
91 WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", 91 LOG_WARNING(Common_Filesystem, "stat failed on %s: %s",
92 filename.c_str(), GetLastErrorMsg()); 92 filename.c_str(), GetLastErrorMsg());
93 return false; 93 return false;
94 } 94 }
@@ -100,33 +100,33 @@ bool IsDirectory(const std::string &filename)
100// Doesn't supports deleting a directory 100// Doesn't supports deleting a directory
101bool Delete(const std::string &filename) 101bool Delete(const std::string &filename)
102{ 102{
103 INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); 103 LOG_INFO(Common_Filesystem, "file %s", filename.c_str());
104 104
105 // Return true because we care about the file no 105 // Return true because we care about the file no
106 // being there, not the actual delete. 106 // being there, not the actual delete.
107 if (!Exists(filename)) 107 if (!Exists(filename))
108 { 108 {
109 WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str()); 109 LOG_WARNING(Common_Filesystem, "%s does not exist", filename.c_str());
110 return true; 110 return true;
111 } 111 }
112 112
113 // We can't delete a directory 113 // We can't delete a directory
114 if (IsDirectory(filename)) 114 if (IsDirectory(filename))
115 { 115 {
116 WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str()); 116 LOG_ERROR(Common_Filesystem, "Failed: %s is a directory", filename.c_str());
117 return false; 117 return false;
118 } 118 }
119 119
120#ifdef _WIN32 120#ifdef _WIN32
121 if (!DeleteFile(Common::UTF8ToTStr(filename).c_str())) 121 if (!DeleteFile(Common::UTF8ToTStr(filename).c_str()))
122 { 122 {
123 WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", 123 LOG_ERROR(Common_Filesystem, "DeleteFile failed on %s: %s",
124 filename.c_str(), GetLastErrorMsg()); 124 filename.c_str(), GetLastErrorMsg());
125 return false; 125 return false;
126 } 126 }
127#else 127#else
128 if (unlink(filename.c_str()) == -1) { 128 if (unlink(filename.c_str()) == -1) {
129 WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", 129 LOG_ERROR(Common_Filesystem, "unlink failed on %s: %s",
130 filename.c_str(), GetLastErrorMsg()); 130 filename.c_str(), GetLastErrorMsg());
131 return false; 131 return false;
132 } 132 }
@@ -138,17 +138,17 @@ bool Delete(const std::string &filename)
138// Returns true if successful, or path already exists. 138// Returns true if successful, or path already exists.
139bool CreateDir(const std::string &path) 139bool CreateDir(const std::string &path)
140{ 140{
141 INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); 141 LOG_TRACE(Common_Filesystem, "directory %s", path.c_str());
142#ifdef _WIN32 142#ifdef _WIN32
143 if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), NULL)) 143 if (::CreateDirectory(Common::UTF8ToTStr(path).c_str(), nullptr))
144 return true; 144 return true;
145 DWORD error = GetLastError(); 145 DWORD error = GetLastError();
146 if (error == ERROR_ALREADY_EXISTS) 146 if (error == ERROR_ALREADY_EXISTS)
147 { 147 {
148 WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); 148 LOG_WARNING(Common_Filesystem, "CreateDirectory failed on %s: already exists", path.c_str());
149 return true; 149 return true;
150 } 150 }
151 ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); 151 LOG_ERROR(Common_Filesystem, "CreateDirectory failed on %s: %i", path.c_str(), error);
152 return false; 152 return false;
153#else 153#else
154 if (mkdir(path.c_str(), 0755) == 0) 154 if (mkdir(path.c_str(), 0755) == 0)
@@ -158,11 +158,11 @@ bool CreateDir(const std::string &path)
158 158
159 if (err == EEXIST) 159 if (err == EEXIST)
160 { 160 {
161 WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); 161 LOG_WARNING(Common_Filesystem, "mkdir failed on %s: already exists", path.c_str());
162 return true; 162 return true;
163 } 163 }
164 164
165 ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); 165 LOG_ERROR(Common_Filesystem, "mkdir failed on %s: %s", path.c_str(), strerror(err));
166 return false; 166 return false;
167#endif 167#endif
168} 168}
@@ -171,11 +171,11 @@ bool CreateDir(const std::string &path)
171bool CreateFullPath(const std::string &fullPath) 171bool CreateFullPath(const std::string &fullPath)
172{ 172{
173 int panicCounter = 100; 173 int panicCounter = 100;
174 INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); 174 LOG_TRACE(Common_Filesystem, "path %s", fullPath.c_str());
175 175
176 if (FileUtil::Exists(fullPath)) 176 if (FileUtil::Exists(fullPath))
177 { 177 {
178 INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); 178 LOG_WARNING(Common_Filesystem, "path exists %s", fullPath.c_str());
179 return true; 179 return true;
180 } 180 }
181 181
@@ -192,7 +192,7 @@ bool CreateFullPath(const std::string &fullPath)
192 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") 192 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
193 std::string const subPath(fullPath.substr(0, position + 1)); 193 std::string const subPath(fullPath.substr(0, position + 1));
194 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { 194 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) {
195 ERROR_LOG(COMMON, "CreateFullPath: directory creation failed"); 195 LOG_ERROR(Common, "CreateFullPath: directory creation failed");
196 return false; 196 return false;
197 } 197 }
198 198
@@ -200,7 +200,7 @@ bool CreateFullPath(const std::string &fullPath)
200 panicCounter--; 200 panicCounter--;
201 if (panicCounter <= 0) 201 if (panicCounter <= 0)
202 { 202 {
203 ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); 203 LOG_ERROR(Common, "CreateFullPath: directory structure is too deep");
204 return false; 204 return false;
205 } 205 }
206 position++; 206 position++;
@@ -211,12 +211,12 @@ bool CreateFullPath(const std::string &fullPath)
211// Deletes a directory filename, returns true on success 211// Deletes a directory filename, returns true on success
212bool DeleteDir(const std::string &filename) 212bool DeleteDir(const std::string &filename)
213{ 213{
214 INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); 214 LOG_INFO(Common_Filesystem, "directory %s", filename.c_str());
215 215
216 // check if a directory 216 // check if a directory
217 if (!FileUtil::IsDirectory(filename)) 217 if (!FileUtil::IsDirectory(filename))
218 { 218 {
219 ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); 219 LOG_ERROR(Common_Filesystem, "Not a directory %s", filename.c_str());
220 return false; 220 return false;
221 } 221 }
222 222
@@ -227,33 +227,33 @@ bool DeleteDir(const std::string &filename)
227 if (rmdir(filename.c_str()) == 0) 227 if (rmdir(filename.c_str()) == 0)
228 return true; 228 return true;
229#endif 229#endif
230 ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); 230 LOG_ERROR(Common_Filesystem, "failed %s: %s", filename.c_str(), GetLastErrorMsg());
231 231
232 return false; 232 return false;
233} 233}
234 234
235// renames file srcFilename to destFilename, returns true on success 235// renames file srcFilename to destFilename, returns true on success
236bool Rename(const std::string &srcFilename, const std::string &destFilename) 236bool Rename(const std::string &srcFilename, const std::string &destFilename)
237{ 237{
238 INFO_LOG(COMMON, "Rename: %s --> %s", 238 LOG_TRACE(Common_Filesystem, "%s --> %s",
239 srcFilename.c_str(), destFilename.c_str()); 239 srcFilename.c_str(), destFilename.c_str());
240 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) 240 if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
241 return true; 241 return true;
242 ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", 242 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
243 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 243 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
244 return false; 244 return false;
245} 245}
246 246
247// copies file srcFilename to destFilename, returns true on success 247// copies file srcFilename to destFilename, returns true on success
248bool Copy(const std::string &srcFilename, const std::string &destFilename) 248bool Copy(const std::string &srcFilename, const std::string &destFilename)
249{ 249{
250 INFO_LOG(COMMON, "Copy: %s --> %s", 250 LOG_TRACE(Common_Filesystem, "%s --> %s",
251 srcFilename.c_str(), destFilename.c_str()); 251 srcFilename.c_str(), destFilename.c_str());
252#ifdef _WIN32 252#ifdef _WIN32
253 if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE)) 253 if (CopyFile(Common::UTF8ToTStr(srcFilename).c_str(), Common::UTF8ToTStr(destFilename).c_str(), FALSE))
254 return true; 254 return true;
255 255
256 ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", 256 LOG_ERROR(Common_Filesystem, "failed %s --> %s: %s",
257 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 257 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
258 return false; 258 return false;
259#else 259#else
@@ -267,7 +267,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
267 FILE *input = fopen(srcFilename.c_str(), "rb"); 267 FILE *input = fopen(srcFilename.c_str(), "rb");
268 if (!input) 268 if (!input)
269 { 269 {
270 ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", 270 LOG_ERROR(Common_Filesystem, "opening input failed %s --> %s: %s",
271 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 271 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
272 return false; 272 return false;
273 } 273 }
@@ -277,7 +277,7 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
277 if (!output) 277 if (!output)
278 { 278 {
279 fclose(input); 279 fclose(input);
280 ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", 280 LOG_ERROR(Common_Filesystem, "opening output failed %s --> %s: %s",
281 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 281 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
282 return false; 282 return false;
283 } 283 }
@@ -291,8 +291,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
291 { 291 {
292 if (ferror(input) != 0) 292 if (ferror(input) != 0)
293 { 293 {
294 ERROR_LOG(COMMON, 294 LOG_ERROR(Common_Filesystem,
295 "Copy: failed reading from source, %s --> %s: %s", 295 "failed reading from source, %s --> %s: %s",
296 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 296 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
297 goto bail; 297 goto bail;
298 } 298 }
@@ -302,8 +302,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
302 int wnum = fwrite(buffer, sizeof(char), rnum, output); 302 int wnum = fwrite(buffer, sizeof(char), rnum, output);
303 if (wnum != rnum) 303 if (wnum != rnum)
304 { 304 {
305 ERROR_LOG(COMMON, 305 LOG_ERROR(Common_Filesystem,
306 "Copy: failed writing to output, %s --> %s: %s", 306 "failed writing to output, %s --> %s: %s",
307 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); 307 srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
308 goto bail; 308 goto bail;
309 } 309 }
@@ -326,16 +326,16 @@ u64 GetSize(const std::string &filename)
326{ 326{
327 if (!Exists(filename)) 327 if (!Exists(filename))
328 { 328 {
329 WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); 329 LOG_ERROR(Common_Filesystem, "failed %s: No such file", filename.c_str());
330 return 0; 330 return 0;
331 } 331 }
332 332
333 if (IsDirectory(filename)) 333 if (IsDirectory(filename))
334 { 334 {
335 WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); 335 LOG_ERROR(Common_Filesystem, "failed %s: is a directory", filename.c_str());
336 return 0; 336 return 0;
337 } 337 }
338 338
339 struct stat64 buf; 339 struct stat64 buf;
340#ifdef _WIN32 340#ifdef _WIN32
341 if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0) 341 if (_tstat64(Common::UTF8ToTStr(filename).c_str(), &buf) == 0)
@@ -343,12 +343,12 @@ u64 GetSize(const std::string &filename)
343 if (stat64(filename.c_str(), &buf) == 0) 343 if (stat64(filename.c_str(), &buf) == 0)
344#endif 344#endif
345 { 345 {
346 DEBUG_LOG(COMMON, "GetSize: %s: %lld", 346 LOG_TRACE(Common_Filesystem, "%s: %lld",
347 filename.c_str(), (long long)buf.st_size); 347 filename.c_str(), (long long)buf.st_size);
348 return buf.st_size; 348 return buf.st_size;
349 } 349 }
350 350
351 ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", 351 LOG_ERROR(Common_Filesystem, "Stat failed %s: %s",
352 filename.c_str(), GetLastErrorMsg()); 352 filename.c_str(), GetLastErrorMsg());
353 return 0; 353 return 0;
354} 354}
@@ -358,7 +358,7 @@ u64 GetSize(const int fd)
358{ 358{
359 struct stat64 buf; 359 struct stat64 buf;
360 if (fstat64(fd, &buf) != 0) { 360 if (fstat64(fd, &buf) != 0) {
361 ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", 361 LOG_ERROR(Common_Filesystem, "GetSize: stat failed %i: %s",
362 fd, GetLastErrorMsg()); 362 fd, GetLastErrorMsg());
363 return 0; 363 return 0;
364 } 364 }
@@ -371,27 +371,27 @@ u64 GetSize(FILE *f)
371 // can't use off_t here because it can be 32-bit 371 // can't use off_t here because it can be 32-bit
372 u64 pos = ftello(f); 372 u64 pos = ftello(f);
373 if (fseeko(f, 0, SEEK_END) != 0) { 373 if (fseeko(f, 0, SEEK_END) != 0) {
374 ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", 374 LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
375 f, GetLastErrorMsg()); 375 f, GetLastErrorMsg());
376 return 0; 376 return 0;
377 } 377 }
378 u64 size = ftello(f); 378 u64 size = ftello(f);
379 if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { 379 if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
380 ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", 380 LOG_ERROR(Common_Filesystem, "GetSize: seek failed %p: %s",
381 f, GetLastErrorMsg()); 381 f, GetLastErrorMsg());
382 return 0; 382 return 0;
383 } 383 }
384 return size; 384 return size;
385} 385}
386 386
387// creates an empty file filename, returns true on success 387// creates an empty file filename, returns true on success
388bool CreateEmptyFile(const std::string &filename) 388bool CreateEmptyFile(const std::string &filename)
389{ 389{
390 INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); 390 LOG_TRACE(Common_Filesystem, "%s", filename.c_str());
391 391
392 if (!FileUtil::IOFile(filename, "wb")) 392 if (!FileUtil::IOFile(filename, "wb"))
393 { 393 {
394 ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", 394 LOG_ERROR(Common_Filesystem, "failed %s: %s",
395 filename.c_str(), GetLastErrorMsg()); 395 filename.c_str(), GetLastErrorMsg());
396 return false; 396 return false;
397 } 397 }
@@ -404,7 +404,7 @@ bool CreateEmptyFile(const std::string &filename)
404// results into parentEntry. Returns the number of files+directories found 404// results into parentEntry. Returns the number of files+directories found
405u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) 405u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
406{ 406{
407 INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str()); 407 LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
408 // How many files + directories we found 408 // How many files + directories we found
409 u32 foundEntries = 0; 409 u32 foundEntries = 0;
410#ifdef _WIN32 410#ifdef _WIN32
@@ -423,7 +423,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
423 FSTEntry entry; 423 FSTEntry entry;
424 const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); 424 const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
425#else 425#else
426 struct dirent dirent, *result = NULL; 426 struct dirent dirent, *result = nullptr;
427 427
428 DIR *dirp = opendir(directory.c_str()); 428 DIR *dirp = opendir(directory.c_str());
429 if (!dirp) 429 if (!dirp)
@@ -437,7 +437,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
437#endif 437#endif
438 // check for "." and ".." 438 // check for "." and ".."
439 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || 439 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
440 ((virtualName[0] == '.') && (virtualName[1] == '.') && 440 ((virtualName[0] == '.') && (virtualName[1] == '.') &&
441 (virtualName[2] == '\0'))) 441 (virtualName[2] == '\0')))
442 continue; 442 continue;
443 entry.virtualName = virtualName; 443 entry.virtualName = virtualName;
@@ -452,14 +452,14 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
452 foundEntries += (u32)entry.size; 452 foundEntries += (u32)entry.size;
453 } 453 }
454 else 454 else
455 { // is a file 455 { // is a file
456 entry.isDirectory = false; 456 entry.isDirectory = false;
457 entry.size = GetSize(entry.physicalName.c_str()); 457 entry.size = GetSize(entry.physicalName.c_str());
458 } 458 }
459 ++foundEntries; 459 ++foundEntries;
460 // Push into the tree 460 // Push into the tree
461 parentEntry.children.push_back(entry); 461 parentEntry.children.push_back(entry);
462#ifdef _WIN32 462#ifdef _WIN32
463 } while (FindNextFile(hFind, &ffd) != 0); 463 } while (FindNextFile(hFind, &ffd) != 0);
464 FindClose(hFind); 464 FindClose(hFind);
465#else 465#else
@@ -474,7 +474,7 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
474// Deletes the given directory and anything under it. Returns true on success. 474// Deletes the given directory and anything under it. Returns true on success.
475bool DeleteDirRecursively(const std::string &directory) 475bool DeleteDirRecursively(const std::string &directory)
476{ 476{
477 INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); 477 LOG_TRACE(Common_Filesystem, "%s", directory.c_str());
478#ifdef _WIN32 478#ifdef _WIN32
479 // Find the first file in the directory. 479 // Find the first file in the directory.
480 WIN32_FIND_DATA ffd; 480 WIN32_FIND_DATA ffd;
@@ -491,7 +491,7 @@ bool DeleteDirRecursively(const std::string &directory)
491 { 491 {
492 const std::string virtualName(Common::TStrToUTF8(ffd.cFileName)); 492 const std::string virtualName(Common::TStrToUTF8(ffd.cFileName));
493#else 493#else
494 struct dirent dirent, *result = NULL; 494 struct dirent dirent, *result = nullptr;
495 DIR *dirp = opendir(directory.c_str()); 495 DIR *dirp = opendir(directory.c_str());
496 if (!dirp) 496 if (!dirp)
497 return false; 497 return false;
@@ -504,7 +504,7 @@ bool DeleteDirRecursively(const std::string &directory)
504 504
505 // check for "." and ".." 505 // check for "." and ".."
506 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || 506 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
507 ((virtualName[0] == '.') && (virtualName[1] == '.') && 507 ((virtualName[0] == '.') && (virtualName[1] == '.') &&
508 (virtualName[2] == '\0'))) 508 (virtualName[2] == '\0')))
509 continue; 509 continue;
510 510
@@ -540,7 +540,7 @@ bool DeleteDirRecursively(const std::string &directory)
540 closedir(dirp); 540 closedir(dirp);
541#endif 541#endif
542 FileUtil::DeleteDir(directory); 542 FileUtil::DeleteDir(directory);
543 543
544 return true; 544 return true;
545} 545}
546 546
@@ -552,7 +552,7 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
552 if (!FileUtil::Exists(source_path)) return; 552 if (!FileUtil::Exists(source_path)) return;
553 if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path); 553 if (!FileUtil::Exists(dest_path)) FileUtil::CreateFullPath(dest_path);
554 554
555 struct dirent dirent, *result = NULL; 555 struct dirent dirent, *result = nullptr;
556 DIR *dirp = opendir(source_path.c_str()); 556 DIR *dirp = opendir(source_path.c_str());
557 if (!dirp) return; 557 if (!dirp) return;
558 558
@@ -585,12 +585,12 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
585std::string GetCurrentDir() 585std::string GetCurrentDir()
586{ 586{
587 char *dir; 587 char *dir;
588 // Get the current working directory (getcwd uses malloc) 588 // Get the current working directory (getcwd uses malloc)
589 if (!(dir = __getcwd(NULL, 0))) { 589 if (!(dir = __getcwd(nullptr, 0))) {
590 590
591 ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", 591 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: %s",
592 GetLastErrorMsg()); 592 GetLastErrorMsg());
593 return NULL; 593 return nullptr;
594 } 594 }
595 std::string strDir = dir; 595 std::string strDir = dir;
596 free(dir); 596 free(dir);
@@ -604,7 +604,7 @@ bool SetCurrentDir(const std::string &directory)
604} 604}
605 605
606#if defined(__APPLE__) 606#if defined(__APPLE__)
607std::string GetBundleDirectory() 607std::string GetBundleDirectory()
608{ 608{
609 CFURLRef BundleRef; 609 CFURLRef BundleRef;
610 char AppBundlePath[MAXPATHLEN]; 610 char AppBundlePath[MAXPATHLEN];
@@ -626,7 +626,7 @@ std::string& GetExeDirectory()
626 if (DolphinPath.empty()) 626 if (DolphinPath.empty())
627 { 627 {
628 TCHAR Dolphin_exe_Path[2048]; 628 TCHAR Dolphin_exe_Path[2048];
629 GetModuleFileName(NULL, Dolphin_exe_Path, 2048); 629 GetModuleFileName(nullptr, Dolphin_exe_Path, 2048);
630 DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path); 630 DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path);
631 DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); 631 DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\'));
632 } 632 }
@@ -647,7 +647,7 @@ std::string GetSysDirectory()
647#endif 647#endif
648 sysDir += DIR_SEP; 648 sysDir += DIR_SEP;
649 649
650 INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str()); 650 LOG_DEBUG(Common_Filesystem, "Setting to %s:", sysDir.c_str());
651 return sysDir; 651 return sysDir;
652} 652}
653 653
@@ -666,8 +666,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
666 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) 666 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR))
667 paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; 667 paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
668 else 668 else
669 paths[D_USER_IDX] = std::string(getenv("HOME") ? 669 paths[D_USER_IDX] = std::string(getenv("HOME") ?
670 getenv("HOME") : getenv("PWD") ? 670 getenv("HOME") : getenv("PWD") ?
671 getenv("PWD") : "") + DIR_SEP EMU_DATA_DIR DIR_SEP; 671 getenv("PWD") : "") + DIR_SEP EMU_DATA_DIR DIR_SEP;
672#endif 672#endif
673 673
@@ -676,6 +676,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
676 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; 676 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
677 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 677 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
678 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; 678 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
679 paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
680 paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
679 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; 681 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
680 paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; 682 paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
681 paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; 683 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
694 { 696 {
695 if (!FileUtil::IsDirectory(newPath)) 697 if (!FileUtil::IsDirectory(newPath))
696 { 698 {
697 WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); 699 LOG_ERROR(Common_Filesystem, "Invalid path specified %s", newPath.c_str());
698 return paths[DirIDX]; 700 return paths[DirIDX];
699 } 701 }
700 else 702 else
@@ -717,6 +719,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
717 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; 719 paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
718 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 720 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
719 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; 721 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
722 paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP;
720 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; 723 paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
721 paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; 724 paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
722 paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; 725 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
749 paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; 752 paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
750 } 753 }
751 } 754 }
752 755
753 return paths[DirIDX]; 756 return paths[DirIDX];
754} 757}
755 758
756//std::string GetThemeDir(const std::string& theme_name)
757//{
758// std::string dir = FileUtil::GetUserPath(D_THEMES_IDX) + theme_name + "/";
759//
760//#if !defined(_WIN32)
761// // If theme does not exist in user's dir load from shared directory
762// if (!FileUtil::Exists(dir))
763// dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/";
764//#endif
765//
766// return dir;
767//}
768
769size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename) 759size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename)
770{ 760{
771 return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); 761 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
826} 816}
827 817
828IOFile::IOFile() 818IOFile::IOFile()
829 : m_file(NULL), m_good(true) 819 : m_file(nullptr), m_good(true)
830{} 820{}
831 821
832IOFile::IOFile(std::FILE* file) 822IOFile::IOFile(std::FILE* file)
@@ -834,7 +824,7 @@ IOFile::IOFile(std::FILE* file)
834{} 824{}
835 825
836IOFile::IOFile(const std::string& filename, const char openmode[]) 826IOFile::IOFile(const std::string& filename, const char openmode[])
837 : m_file(NULL), m_good(true) 827 : m_file(nullptr), m_good(true)
838{ 828{
839 Open(filename, openmode); 829 Open(filename, openmode);
840} 830}
@@ -845,7 +835,7 @@ IOFile::~IOFile()
845} 835}
846 836
847IOFile::IOFile(IOFile&& other) 837IOFile::IOFile(IOFile&& other)
848 : m_file(NULL), m_good(true) 838 : m_file(nullptr), m_good(true)
849{ 839{
850 Swap(other); 840 Swap(other);
851} 841}
@@ -880,14 +870,14 @@ bool IOFile::Close()
880 if (!IsOpen() || 0 != std::fclose(m_file)) 870 if (!IsOpen() || 0 != std::fclose(m_file))
881 m_good = false; 871 m_good = false;
882 872
883 m_file = NULL; 873 m_file = nullptr;
884 return m_good; 874 return m_good;
885} 875}
886 876
887std::FILE* IOFile::ReleaseHandle() 877std::FILE* IOFile::ReleaseHandle()
888{ 878{
889 std::FILE* const ret = m_file; 879 std::FILE* const ret = m_file;
890 m_file = NULL; 880 m_file = nullptr;
891 return ret; 881 return ret;
892} 882}
893 883
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 173ce6623..e691b6139 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -16,33 +16,35 @@
16 16
17// User directory indices for GetUserPath 17// User directory indices for GetUserPath
18enum { 18enum {
19 D_USER_IDX, 19 D_USER_IDX,
20 D_ROOT_IDX, 20 D_ROOT_IDX,
21 D_CONFIG_IDX, 21 D_CONFIG_IDX,
22 D_GAMECONFIG_IDX, 22 D_GAMECONFIG_IDX,
23 D_MAPS_IDX, 23 D_MAPS_IDX,
24 D_CACHE_IDX, 24 D_CACHE_IDX,
25 D_SHADERCACHE_IDX, 25 D_SHADERCACHE_IDX,
26 D_SHADERS_IDX, 26 D_SHADERS_IDX,
27 D_STATESAVES_IDX, 27 D_STATESAVES_IDX,
28 D_SCREENSHOTS_IDX, 28 D_SCREENSHOTS_IDX,
29 D_SDMC_IDX, 29 D_SDMC_IDX,
30 D_HIRESTEXTURES_IDX, 30 D_SAVEDATA_IDX,
31 D_DUMP_IDX, 31 D_SYSDATA_IDX,
32 D_DUMPFRAMES_IDX, 32 D_HIRESTEXTURES_IDX,
33 D_DUMPAUDIO_IDX, 33 D_DUMP_IDX,
34 D_DUMPTEXTURES_IDX, 34 D_DUMPFRAMES_IDX,
35 D_DUMPDSP_IDX, 35 D_DUMPAUDIO_IDX,
36 D_LOGS_IDX, 36 D_DUMPTEXTURES_IDX,
37 D_SYSCONF_IDX, 37 D_DUMPDSP_IDX,
38 F_EMUCONFIG_IDX, 38 D_LOGS_IDX,
39 F_DEBUGGERCONFIG_IDX, 39 D_SYSCONF_IDX,
40 F_LOGGERCONFIG_IDX, 40 F_EMUCONFIG_IDX,
41 F_MAINLOG_IDX, 41 F_DEBUGGERCONFIG_IDX,
42 F_RAMDUMP_IDX, 42 F_LOGGERCONFIG_IDX,
43 F_ARAMDUMP_IDX, 43 F_MAINLOG_IDX,
44 F_SYSCONF_IDX, 44 F_RAMDUMP_IDX,
45 NUM_PATH_INDICES 45 F_ARAMDUMP_IDX,
46 F_SYSCONF_IDX,
47 NUM_PATH_INDICES
46}; 48};
47 49
48namespace FileUtil 50namespace FileUtil
@@ -51,11 +53,11 @@ namespace FileUtil
51// FileSystem tree node/ 53// FileSystem tree node/
52struct FSTEntry 54struct FSTEntry
53{ 55{
54 bool isDirectory; 56 bool isDirectory;
55 u64 size; // file length or number of entries from children 57 u64 size; // file length or number of entries from children
56 std::string physicalName; // name on disk 58 std::string physicalName; // name on disk
57 std::string virtualName; // name in FST names table 59 std::string virtualName; // name in FST names table
58 std::vector<FSTEntry> children; 60 std::vector<FSTEntry> children;
59}; 61};
60 62
61// Returns true if file filename exists 63// Returns true if file filename exists
@@ -148,86 +150,86 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
148class IOFile : public NonCopyable 150class IOFile : public NonCopyable
149{ 151{
150public: 152public:
151 IOFile(); 153 IOFile();
152 IOFile(std::FILE* file); 154 IOFile(std::FILE* file);
153 IOFile(const std::string& filename, const char openmode[]); 155 IOFile(const std::string& filename, const char openmode[]);
154 156
155 ~IOFile(); 157 ~IOFile();
156 158
157 IOFile(IOFile&& other); 159 IOFile(IOFile&& other);
158 IOFile& operator=(IOFile&& other); 160 IOFile& operator=(IOFile&& other);
159 161
160 void Swap(IOFile& other); 162 void Swap(IOFile& other);
161 163
162 bool Open(const std::string& filename, const char openmode[]); 164 bool Open(const std::string& filename, const char openmode[]);
163 bool Close(); 165 bool Close();
164 166
165 template <typename T> 167 template <typename T>
166 size_t ReadArray(T* data, size_t length) 168 size_t ReadArray(T* data, size_t length)
167 { 169 {
168 if (!IsOpen()) { 170 if (!IsOpen()) {
169 m_good = false; 171 m_good = false;
170 return -1; 172 return -1;
171 } 173 }
172 174
173 size_t items_read = std::fread(data, sizeof(T), length, m_file); 175 size_t items_read = std::fread(data, sizeof(T), length, m_file);
174 if (items_read != length) 176 if (items_read != length)
175 m_good = false; 177 m_good = false;
176 178
177 return items_read; 179 return items_read;
178 } 180 }
179 181
180 template <typename T> 182 template <typename T>
181 size_t WriteArray(const T* data, size_t length) 183 size_t WriteArray(const T* data, size_t length)
182 { 184 {
183 if (!IsOpen()) { 185 if (!IsOpen()) {
184 m_good = false; 186 m_good = false;
185 return -1; 187 return -1;
186 } 188 }
187 189
188 size_t items_written = std::fwrite(data, sizeof(T), length, m_file); 190 size_t items_written = std::fwrite(data, sizeof(T), length, m_file);
189 if (items_written != length) 191 if (items_written != length)
190 m_good = false; 192 m_good = false;
191 193
192 return items_written; 194 return items_written;
193 } 195 }
194 196
195 size_t ReadBytes(void* data, size_t length) 197 size_t ReadBytes(void* data, size_t length)
196 { 198 {
197 return ReadArray(reinterpret_cast<char*>(data), length); 199 return ReadArray(reinterpret_cast<char*>(data), length);
198 } 200 }
199 201
200 size_t WriteBytes(const void* data, size_t length) 202 size_t WriteBytes(const void* data, size_t length)
201 { 203 {
202 return WriteArray(reinterpret_cast<const char*>(data), length); 204 return WriteArray(reinterpret_cast<const char*>(data), length);
203 } 205 }
204 206
205 bool IsOpen() { return NULL != m_file; } 207 bool IsOpen() { return nullptr != m_file; }
206 208
207 // m_good is set to false when a read, write or other function fails 209 // m_good is set to false when a read, write or other function fails
208 bool IsGood() { return m_good; } 210 bool IsGood() { return m_good; }
209 operator void*() { return m_good ? m_file : NULL; } 211 operator void*() { return m_good ? m_file : nullptr; }
210 212
211 std::FILE* ReleaseHandle(); 213 std::FILE* ReleaseHandle();
212 214
213 std::FILE* GetHandle() { return m_file; } 215 std::FILE* GetHandle() { return m_file; }
214 216
215 void SetHandle(std::FILE* file); 217 void SetHandle(std::FILE* file);
216 218
217 bool Seek(s64 off, int origin); 219 bool Seek(s64 off, int origin);
218 u64 Tell(); 220 u64 Tell();
219 u64 GetSize(); 221 u64 GetSize();
220 bool Resize(u64 size); 222 bool Resize(u64 size);
221 bool Flush(); 223 bool Flush();
222 224
223 // clear error state 225 // clear error state
224 void Clear() { m_good = true; std::clearerr(m_file); } 226 void Clear() { m_good = true; std::clearerr(m_file); }
225 227
226 std::FILE* m_file; 228 std::FILE* m_file;
227 bool m_good; 229 bool m_good;
228private: 230private:
229 IOFile(IOFile&); 231 IOFile(IOFile&);
230 IOFile& operator=(IOFile& other); 232 IOFile& operator=(IOFile& other);
231}; 233};
232 234
233} // namespace 235} // namespace
@@ -237,8 +239,8 @@ template <typename T>
237void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) 239void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode)
238{ 240{
239#ifdef _WIN32 241#ifdef _WIN32
240 fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode); 242 fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
241#else 243#else
242 fstream.open(filename.c_str(), openmode); 244 fstream.open(filename.c_str(), openmode);
243#endif 245#endif
244} 246}
diff --git a/src/common/hash.cpp b/src/common/hash.cpp
index d2ebc7341..2ddcfe6b7 100644
--- a/src/common/hash.cpp
+++ b/src/common/hash.cpp
@@ -115,15 +115,15 @@ inline u64 getblock(const u64 * p, int i)
115 115
116inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2) 116inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2)
117{ 117{
118 k1 *= c1; 118 k1 *= c1;
119 k1 = _rotl64(k1,23); 119 k1 = _rotl64(k1,23);
120 k1 *= c2; 120 k1 *= c2;
121 h1 ^= k1; 121 h1 ^= k1;
122 h1 += h2; 122 h1 += h2;
123 123
124 h2 = _rotl64(h2,41); 124 h2 = _rotl64(h2,41);
125 125
126 k2 *= c2; 126 k2 *= c2;
127 k2 = _rotl64(k2,23); 127 k2 = _rotl64(k2,23);
128 k2 *= c1; 128 k2 *= c1;
129 h2 ^= k2; 129 h2 ^= k2;
@@ -250,7 +250,7 @@ u64 GetCRC32(const u8 *src, int len, u32 samples)
250} 250}
251 251
252 252
253/* 253/*
254 * NOTE: This hash function is used for custom texture loading/dumping, so 254 * NOTE: This hash function is used for custom texture loading/dumping, so
255 * it should not be changed, which would require all custom textures to be 255 * it should not be changed, which would require all custom textures to be
256 * recalculated for their new hash values. If the hashing function is 256 * recalculated for their new hash values. If the hashing function is
@@ -273,7 +273,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
273 u64 k = data[0]; 273 u64 k = data[0];
274 data+=Step; 274 data+=Step;
275 k *= m; 275 k *= m;
276 k ^= k >> r; 276 k ^= k >> r;
277 k *= m; 277 k *= m;
278 h ^= k; 278 h ^= k;
279 h *= m; 279 h *= m;
@@ -292,13 +292,13 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
292 case 1: h ^= u64(data2[0]); 292 case 1: h ^= u64(data2[0]);
293 h *= m; 293 h *= m;
294 }; 294 };
295 295
296 h ^= h >> r; 296 h ^= h >> r;
297 h *= m; 297 h *= m;
298 h ^= h >> r; 298 h ^= h >> r;
299 299
300 return h; 300 return h;
301} 301}
302#else 302#else
303// CRC32 hash using the SSE4.2 instruction 303// CRC32 hash using the SSE4.2 instruction
304u64 GetCRC32(const u8 *src, int len, u32 samples) 304u64 GetCRC32(const u8 *src, int len, u32 samples)
@@ -351,15 +351,15 @@ inline u32 fmix32(u32 h)
351 351
352inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2) 352inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2)
353{ 353{
354 k1 *= c1; 354 k1 *= c1;
355 k1 = _rotl(k1,11); 355 k1 = _rotl(k1,11);
356 k1 *= c2; 356 k1 *= c2;
357 h1 ^= k1; 357 h1 ^= k1;
358 h1 += h2; 358 h1 += h2;
359 359
360 h2 = _rotl(h2,17); 360 h2 = _rotl(h2,17);
361 361
362 k2 *= c2; 362 k2 *= c2;
363 k2 = _rotl(k2,11); 363 k2 = _rotl(k2,11);
364 k2 *= c1; 364 k2 *= c1;
365 h2 ^= k2; 365 h2 ^= k2;
@@ -405,7 +405,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples)
405 405
406 //---------- 406 //----------
407 // tail 407 // tail
408 408
409 const u8 * tail = (const u8*)(data + nblocks*8); 409 const u8 * tail = (const u8*)(data + nblocks*8);
410 410
411 u32 k1 = 0; 411 u32 k1 = 0;
@@ -439,7 +439,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples)
439 439
440 out[0] = h1; 440 out[0] = h1;
441 out[1] = h2; 441 out[1] = h2;
442 442
443 return *((u64 *)&out); 443 return *((u64 *)&out);
444} 444}
445 445
@@ -463,11 +463,11 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
463 { 463 {
464 u64 k = data[0]; 464 u64 k = data[0];
465 data+=Step; 465 data+=Step;
466 k *= m; 466 k *= m;
467 k ^= k >> r; 467 k ^= k >> r;
468 k *= m; 468 k *= m;
469 h ^= k; 469 h ^= k;
470 h *= m; 470 h *= m;
471 } 471 }
472 472
473 const u8 * data2 = (const u8*)end; 473 const u8 * data2 = (const u8*)end;
@@ -483,7 +483,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
483 case 1: h ^= u64(data2[0]); 483 case 1: h ^= u64(data2[0]);
484 h *= m; 484 h *= m;
485 }; 485 };
486 486
487 h ^= h >> r; 487 h ^= h >> r;
488 h *= m; 488 h *= m;
489 h ^= h >> r; 489 h ^= h >> r;
diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h
index 96dce3155..bb1b5174f 100644
--- a/src/common/linear_disk_cache.h
+++ b/src/common/linear_disk_cache.h
@@ -64,13 +64,13 @@ public:
64 m_file.seekg(0, std::ios::beg); 64 m_file.seekg(0, std::ios::beg);
65 std::fstream::pos_type start_pos = m_file.tellg(); 65 std::fstream::pos_type start_pos = m_file.tellg();
66 std::streamoff file_size = end_pos - start_pos; 66 std::streamoff file_size = end_pos - start_pos;
67 67
68 if (m_file.is_open() && ValidateHeader()) 68 if (m_file.is_open() && ValidateHeader())
69 { 69 {
70 // good header, read some key/value pairs 70 // good header, read some key/value pairs
71 K key; 71 K key;
72 72
73 V *value = NULL; 73 V *value = nullptr;
74 u32 value_size; 74 u32 value_size;
75 u32 entry_number; 75 u32 entry_number;
76 76
@@ -87,7 +87,7 @@ public:
87 87
88 // read key/value and pass to reader 88 // read key/value and pass to reader
89 if (Read(&key) && 89 if (Read(&key) &&
90 Read(value, value_size) && 90 Read(value, value_size) &&
91 Read(&entry_number) && 91 Read(&entry_number) &&
92 entry_number == m_num_entries+1) 92 entry_number == m_num_entries+1)
93 { 93 {
@@ -115,7 +115,7 @@ public:
115 WriteHeader(); 115 WriteHeader();
116 return 0; 116 return 0;
117 } 117 }
118 118
119 void Sync() 119 void Sync()
120 { 120 {
121 m_file.flush(); 121 m_file.flush();
diff --git a/src/common/log.h b/src/common/log.h
index bfd73f8a5..663eda9ad 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -4,104 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#ifndef LOGGING 7#include "common/common_funcs.h"
8#define LOGGING 8#include "common/msg_handler.h"
9#endif 9#include "common/logging/log.h"
10
11enum {
12 OS_LEVEL, // Printed by the emulated operating system
13 NOTICE_LEVEL, // VERY important information that is NOT errors. Like startup and OSReports.
14 ERROR_LEVEL, // Critical errors
15 WARNING_LEVEL, // Something is suspicious.
16 INFO_LEVEL, // General information.
17 DEBUG_LEVEL, // Detailed debugging - might make things slow.
18};
19
20namespace LogTypes
21{
22
23enum LOG_TYPE {
24 ACTIONREPLAY,
25 AUDIO,
26 AUDIO_INTERFACE,
27 BOOT,
28 COMMANDPROCESSOR,
29 COMMON,
30 CONSOLE,
31 CONFIG,
32 DISCIO,
33 FILEMON,
34 DSPHLE,
35 DSPLLE,
36 DSP_MAIL,
37 DSPINTERFACE,
38 DVDINTERFACE,
39 DYNA_REC,
40 EXPANSIONINTERFACE,
41 GDB_STUB,
42 ARM11,
43 GSP,
44 OSHLE,
45 MASTER_LOG,
46 MEMMAP,
47 MEMCARD_MANAGER,
48 OSREPORT,
49 PAD,
50 PROCESSORINTERFACE,
51 PIXELENGINE,
52 SERIALINTERFACE,
53 SP1,
54 STREAMINGINTERFACE,
55 VIDEO,
56 VIDEOINTERFACE,
57 LOADER,
58 FILESYS,
59 WII_IPC_DVD,
60 WII_IPC_ES,
61 WII_IPC_FILEIO,
62 WII_IPC_HID,
63 KERNEL,
64 SVC,
65 NDMA,
66 HLE,
67 RENDER,
68 GPU,
69 HW,
70 TIME,
71 NETPLAY,
72
73 NUMBER_OF_LOGS // Must be last
74};
75
76// FIXME: should this be removed?
77enum LOG_LEVELS {
78 LOS = OS_LEVEL,
79 LNOTICE = NOTICE_LEVEL,
80 LERROR = ERROR_LEVEL,
81 LWARNING = WARNING_LEVEL,
82 LINFO = INFO_LEVEL,
83 LDEBUG = DEBUG_LEVEL,
84};
85
86#define LOGTYPES_LEVELS LogTypes::LOG_LEVELS
87#define LOGTYPES_TYPE LogTypes::LOG_TYPE
88
89} // namespace
90
91void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int line,
92 const char* function, const char* fmt, ...)
93#ifdef __GNUC__
94 __attribute__((format(printf, 6, 7)))
95#endif
96 ;
97
98#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
99#define MAX_LOGLEVEL LDEBUG
100#else
101#ifndef MAX_LOGLEVEL
102#define MAX_LOGLEVEL LWARNING
103#endif // loglevel
104#endif // logging
105 10
106#ifdef _WIN32 11#ifdef _WIN32
107#ifndef __func__ 12#ifndef __func__
@@ -109,29 +14,16 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
109#endif 14#endif
110#endif 15#endif
111 16
112// Let the compiler optimize this out 17#if _DEBUG
113#define GENERIC_LOG(t, v, ...) { \
114 if (v <= LogTypes::MAX_LOGLEVEL) \
115 GenericLog(v, t, __FILE__, __LINE__, __func__, __VA_ARGS__); \
116 }
117
118#define OS_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LOS, __VA_ARGS__) } while (0)
119#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
120#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
121#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
122#define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0)
123#define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0)
124
125#if MAX_LOGLEVEL >= DEBUG_LEVEL
126#define _dbg_assert_(_t_, _a_) \ 18#define _dbg_assert_(_t_, _a_) \
127 if (!(_a_)) {\ 19 if (!(_a_)) {\
128 ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ 20 LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
129 __LINE__, __FILE__, __TIME__); \ 21 __LINE__, __FILE__, __TIME__); \
130 if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ 22 if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \
131 } 23 }
132#define _dbg_assert_msg_(_t_, _a_, ...)\ 24#define _dbg_assert_msg_(_t_, _a_, ...)\
133 if (!(_a_)) {\ 25 if (!(_a_)) {\
134 ERROR_LOG(_t_, __VA_ARGS__); \ 26 LOG_CRITICAL(_t_, __VA_ARGS__); \
135 if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ 27 if (!PanicYesNo(__VA_ARGS__)) {Crash();} \
136 } 28 }
137#define _dbg_update_() Host_UpdateLogDisplay(); 29#define _dbg_update_() Host_UpdateLogDisplay();
@@ -143,11 +35,10 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
143#define _dbg_assert_(_t_, _a_) {} 35#define _dbg_assert_(_t_, _a_) {}
144#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {} 36#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {}
145#endif // dbg_assert 37#endif // dbg_assert
146#endif // MAX_LOGLEVEL DEBUG 38#endif
147 39
148#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) 40#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_)
149 41
150#ifndef GEKKO
151#ifdef _WIN32 42#ifdef _WIN32
152#define _assert_msg_(_t_, _a_, _fmt_, ...) \ 43#define _assert_msg_(_t_, _a_, _fmt_, ...) \
153 if (!(_a_)) {\ 44 if (!(_a_)) {\
@@ -159,6 +50,3 @@ void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, const char*file, int
159 if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ 50 if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \
160 } 51 }
161#endif // WIN32 52#endif // WIN32
162#else // GEKKO
163#define _assert_msg_(_t_, _a_, _fmt_, ...)
164#endif
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp
deleted file mode 100644
index 4d590d98f..000000000
--- a/src/common/log_manager.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/log_manager.h"
8#include "common/console_listener.h"
9#include "common/timer.h"
10#include "common/thread.h"
11
12void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
13 const char* function, const char* fmt, ...)
14{
15 va_list args;
16 va_start(args, fmt);
17
18 if (LogManager::GetInstance()) {
19 LogManager::GetInstance()->Log(level, type,
20 file, line, function, fmt, args);
21 }
22 va_end(args);
23}
24
25LogManager *LogManager::m_logManager = NULL;
26
27LogManager::LogManager()
28{
29 // create log files
30 m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log");
31 m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot");
32 m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common");
33 m_Log[LogTypes::CONFIG] = new LogContainer("CONFIG", "Configuration");
34 m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO");
35 m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor");
36 m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad");
37 m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine");
38 m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc");
39 m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt");
40 m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt");
41 m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt");
42 m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap");
43 m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1");
44 m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt");
45 m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface");
46 m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface");
47 m_Log[LogTypes::GSP] = new LogContainer("GSP", "GSP");
48 m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt");
49 m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
50 m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt");
51 m_Log[LogTypes::ARM11] = new LogContainer("ARM11", "ARM11");
52 m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");
53 m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE");
54 m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE");
55 m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails");
56 m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend");
57 m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator");
58 m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "JIT");
59 m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
60 m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
61 m_Log[LogTypes::TIME] = new LogContainer("Time", "Core Timing");
62 m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader");
63 m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System");
64 m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID");
65 m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE");
66 m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD");
67 m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES");
68 m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO");
69 m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER");
70 m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU");
71 m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE");
72 m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA");
73 m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation");
74 m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware");
75 m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
76 m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager");
77 m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay");
78
79 m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str());
80 m_consoleLog = new ConsoleListener();
81 m_debuggerLog = new DebuggerLogListener();
82
83 for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
84 {
85 m_Log[i]->SetEnable(true);
86 m_Log[i]->AddListener(m_fileLog);
87 m_Log[i]->AddListener(m_consoleLog);
88#ifdef _MSC_VER
89 if (IsDebuggerPresent())
90 m_Log[i]->AddListener(m_debuggerLog);
91#endif
92 }
93
94 m_consoleLog->Open();
95}
96
97LogManager::~LogManager()
98{
99 for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
100 {
101 m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
102 m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog);
103 m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog);
104 }
105
106 for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
107 delete m_Log[i];
108
109 delete m_fileLog;
110 delete m_consoleLog;
111 delete m_debuggerLog;
112}
113
114void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file,
115 int line, const char* function, const char *fmt, va_list args)
116{
117 char temp[MAX_MSGLEN];
118 char msg[MAX_MSGLEN * 2];
119 LogContainer *log = m_Log[type];
120
121 if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
122 return;
123
124 Common::CharArrayFromFormatV(temp, MAX_MSGLEN, fmt, args);
125
126 static const char level_to_char[7] = "ONEWID";
127 sprintf(msg, "%s %s:%u %c[%s] %s: %s\n", Common::Timer::GetTimeFormatted().c_str(), file, line,
128 level_to_char[(int)level], log->GetShortName(), function, temp);
129
130#ifdef ANDROID
131 Host_SysMessage(msg);
132#endif
133 log->Trigger(level, msg);
134}
135
136void LogManager::Init()
137{
138 m_logManager = new LogManager();
139}
140
141void LogManager::Shutdown()
142{
143 delete m_logManager;
144 m_logManager = NULL;
145}
146
147LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable)
148 : m_enable(enable)
149{
150 strncpy(m_fullName, fullName, 128);
151 strncpy(m_shortName, shortName, 32);
152 m_level = LogTypes::MAX_LOGLEVEL;
153}
154
155// LogContainer
156void LogContainer::AddListener(LogListener *listener)
157{
158 std::lock_guard<std::mutex> lk(m_listeners_lock);
159 m_listeners.insert(listener);
160}
161
162void LogContainer::RemoveListener(LogListener *listener)
163{
164 std::lock_guard<std::mutex> lk(m_listeners_lock);
165 m_listeners.erase(listener);
166}
167
168void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg)
169{
170 std::lock_guard<std::mutex> lk(m_listeners_lock);
171
172 std::set<LogListener*>::const_iterator i;
173 for (i = m_listeners.begin(); i != m_listeners.end(); ++i)
174 {
175 (*i)->Log(level, msg);
176 }
177}
178
179FileLogListener::FileLogListener(const char *filename)
180{
181 OpenFStream(m_logfile, filename, std::ios::app);
182 SetEnable(true);
183}
184
185void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
186{
187 if (!IsEnabled() || !IsValid())
188 return;
189
190 std::lock_guard<std::mutex> lk(m_log_lock);
191 m_logfile << msg << std::flush;
192}
193
194void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
195{
196#if _MSC_VER
197 ::OutputDebugStringA(msg);
198#endif
199}
diff --git a/src/common/log_manager.h b/src/common/log_manager.h
deleted file mode 100644
index de1d16ee5..000000000
--- a/src/common/log_manager.h
+++ /dev/null
@@ -1,166 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/log.h"
8#include "common/string_util.h"
9#include "common/file_util.h"
10
11#include <cstring>
12#include <set>
13#include <mutex>
14
15#define MAX_MESSAGES 8000
16#define MAX_MSGLEN 1024
17
18
19// pure virtual interface
20class LogListener
21{
22public:
23 virtual ~LogListener() {}
24
25 virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0;
26};
27
28class FileLogListener : public LogListener
29{
30public:
31 FileLogListener(const char *filename);
32
33 void Log(LogTypes::LOG_LEVELS, const char *msg) override;
34
35 bool IsValid() { return !m_logfile.fail(); }
36 bool IsEnabled() const { return m_enable; }
37 void SetEnable(bool enable) { m_enable = enable; }
38
39 const char* GetName() const { return "file"; }
40
41private:
42 std::mutex m_log_lock;
43 std::ofstream m_logfile;
44 bool m_enable;
45};
46
47class DebuggerLogListener : public LogListener
48{
49public:
50 void Log(LogTypes::LOG_LEVELS, const char *msg) override;
51};
52
53class LogContainer
54{
55public:
56 LogContainer(const char* shortName, const char* fullName, bool enable = false);
57
58 const char* GetShortName() const { return m_shortName; }
59 const char* GetFullName() const { return m_fullName; }
60
61 void AddListener(LogListener* listener);
62 void RemoveListener(LogListener* listener);
63
64 void Trigger(LogTypes::LOG_LEVELS, const char *msg);
65
66 bool IsEnabled() const { return m_enable; }
67 void SetEnable(bool enable) { m_enable = enable; }
68
69 LogTypes::LOG_LEVELS GetLevel() const { return m_level; }
70
71 void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; }
72
73 bool HasListeners() const { return !m_listeners.empty(); }
74
75private:
76 char m_fullName[128];
77 char m_shortName[32];
78 bool m_enable;
79 LogTypes::LOG_LEVELS m_level;
80 std::mutex m_listeners_lock;
81 std::set<LogListener*> m_listeners;
82};
83
84class ConsoleListener;
85
86class LogManager : NonCopyable
87{
88private:
89 LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS];
90 FileLogListener *m_fileLog;
91 ConsoleListener *m_consoleLog;
92 DebuggerLogListener *m_debuggerLog;
93 static LogManager *m_logManager; // Singleton. Ugh.
94
95 LogManager();
96 ~LogManager();
97public:
98
99 static u32 GetMaxLevel() { return LogTypes::MAX_LOGLEVEL; }
100
101 void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char* file, int line,
102 const char* function, const char *fmt, va_list args);
103
104 void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
105 {
106 m_Log[type]->SetLevel(level);
107 }
108
109 void SetEnable(LogTypes::LOG_TYPE type, bool enable)
110 {
111 m_Log[type]->SetEnable(enable);
112 }
113
114 bool IsEnabled(LogTypes::LOG_TYPE type) const
115 {
116 return m_Log[type]->IsEnabled();
117 }
118
119 const char* GetShortName(LogTypes::LOG_TYPE type) const
120 {
121 return m_Log[type]->GetShortName();
122 }
123
124 const char* GetFullName(LogTypes::LOG_TYPE type) const
125 {
126 return m_Log[type]->GetFullName();
127 }
128
129 void AddListener(LogTypes::LOG_TYPE type, LogListener *listener)
130 {
131 m_Log[type]->AddListener(listener);
132 }
133
134 void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener)
135 {
136 m_Log[type]->RemoveListener(listener);
137 }
138
139 FileLogListener *GetFileListener() const
140 {
141 return m_fileLog;
142 }
143
144 ConsoleListener *GetConsoleListener() const
145 {
146 return m_consoleLog;
147 }
148
149 DebuggerLogListener *GetDebuggerListener() const
150 {
151 return m_debuggerLog;
152 }
153
154 static LogManager* GetInstance()
155 {
156 return m_logManager;
157 }
158
159 static void SetInstance(LogManager *logManager)
160 {
161 m_logManager = logManager;
162 }
163
164 static void Init();
165 static void Shutdown();
166};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
new file mode 100644
index 000000000..e79b84604
--- /dev/null
+++ b/src/common/logging/backend.cpp
@@ -0,0 +1,151 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/log.h" // For _dbg_assert_
8
9#include "common/logging/backend.h"
10#include "common/logging/log.h"
11#include "common/logging/text_formatter.h"
12
13namespace Log {
14
15static std::shared_ptr<Logger> global_logger;
16
17/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
18#define ALL_LOG_CLASSES() \
19 CLS(Log) \
20 CLS(Common) \
21 SUB(Common, Filesystem) \
22 SUB(Common, Memory) \
23 CLS(Core) \
24 SUB(Core, ARM11) \
25 CLS(Config) \
26 CLS(Debug) \
27 SUB(Debug, Emulated) \
28 SUB(Debug, GPU) \
29 SUB(Debug, Breakpoint) \
30 CLS(Kernel) \
31 SUB(Kernel, SVC) \
32 CLS(Service) \
33 SUB(Service, SRV) \
34 SUB(Service, FS) \
35 SUB(Service, APT) \
36 SUB(Service, GSP) \
37 SUB(Service, AC) \
38 SUB(Service, PTM) \
39 SUB(Service, CFG) \
40 SUB(Service, DSP) \
41 SUB(Service, HID) \
42 CLS(HW) \
43 SUB(HW, Memory) \
44 SUB(HW, GPU) \
45 CLS(Frontend) \
46 CLS(Render) \
47 SUB(Render, Software) \
48 SUB(Render, OpenGL) \
49 CLS(Loader)
50
51Logger::Logger() {
52 // Register logging classes so that they can be queried at runtime
53 size_t parent_class;
54 all_classes.reserve((size_t)Class::Count);
55
56#define CLS(x) \
57 all_classes.push_back(Class::x); \
58 parent_class = all_classes.size() - 1;
59#define SUB(x, y) \
60 all_classes.push_back(Class::x##_##y); \
61 all_classes[parent_class].num_children += 1;
62
63 ALL_LOG_CLASSES()
64#undef CLS
65#undef SUB
66
67 // Ensures that ALL_LOG_CLASSES isn't missing any entries.
68 _dbg_assert_(Log, all_classes.size() == (size_t)Class::Count);
69}
70
71// GetClassName is a macro defined by Windows.h, grrr...
72const char* Logger::GetLogClassName(Class log_class) {
73 switch (log_class) {
74#define CLS(x) case Class::x: return #x;
75#define SUB(x, y) case Class::x##_##y: return #x "." #y;
76 ALL_LOG_CLASSES()
77#undef CLS
78#undef SUB
79 }
80 return "Unknown";
81}
82
83const char* Logger::GetLevelName(Level log_level) {
84#define LVL(x) case Level::x: return #x
85 switch (log_level) {
86 LVL(Trace);
87 LVL(Debug);
88 LVL(Info);
89 LVL(Warning);
90 LVL(Error);
91 LVL(Critical);
92 }
93 return "Unknown";
94#undef LVL
95}
96
97void Logger::LogMessage(Entry entry) {
98 ring_buffer.Push(std::move(entry));
99}
100
101size_t Logger::GetEntries(Entry* out_buffer, size_t buffer_len) {
102 return ring_buffer.BlockingPop(out_buffer, buffer_len);
103}
104
105std::shared_ptr<Logger> InitGlobalLogger() {
106 global_logger = std::make_shared<Logger>();
107 return global_logger;
108}
109
110Entry CreateEntry(Class log_class, Level log_level,
111 const char* filename, unsigned int line_nr, const char* function,
112 const char* format, va_list args) {
113 using std::chrono::steady_clock;
114 using std::chrono::duration_cast;
115
116 static steady_clock::time_point time_origin = steady_clock::now();
117
118 std::array<char, 4 * 1024> formatting_buffer;
119
120 Entry entry;
121 entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
122 entry.log_class = log_class;
123 entry.log_level = log_level;
124
125 snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function, line_nr);
126 entry.location = std::string(formatting_buffer.data());
127
128 vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
129 entry.message = std::string(formatting_buffer.data());
130
131 return std::move(entry);
132}
133
134void LogMessage(Class log_class, Level log_level,
135 const char* filename, unsigned int line_nr, const char* function,
136 const char* format, ...) {
137 va_list args;
138 va_start(args, format);
139 Entry entry = CreateEntry(log_class, log_level,
140 filename, line_nr, function, format, args);
141 va_end(args);
142
143 if (global_logger != nullptr && !global_logger->IsClosed()) {
144 global_logger->LogMessage(std::move(entry));
145 } else {
146 // Fall back to directly printing to stderr
147 PrintMessage(entry);
148 }
149}
150
151}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
new file mode 100644
index 000000000..ae270efe8
--- /dev/null
+++ b/src/common/logging/backend.h
@@ -0,0 +1,134 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstdarg>
8#include <memory>
9#include <vector>
10
11#include "common/concurrent_ring_buffer.h"
12
13#include "common/logging/log.h"
14
15namespace Log {
16
17/**
18 * A log entry. Log entries are store in a structured format to permit more varied output
19 * formatting on different frontends, as well as facilitating filtering and aggregation.
20 */
21struct Entry {
22 std::chrono::microseconds timestamp;
23 Class log_class;
24 Level log_level;
25 std::string location;
26 std::string message;
27
28 Entry() = default;
29
30 // TODO(yuriks) Use defaulted move constructors once MSVC supports them
31#define MOVE(member) member(std::move(o.member))
32 Entry(Entry&& o)
33 : MOVE(timestamp), MOVE(log_class), MOVE(log_level),
34 MOVE(location), MOVE(message)
35 {}
36#undef MOVE
37
38 Entry& operator=(const Entry&& o) {
39#define MOVE(member) member = std::move(o.member)
40 MOVE(timestamp);
41 MOVE(log_class);
42 MOVE(log_level);
43 MOVE(location);
44 MOVE(message);
45#undef MOVE
46 return *this;
47 }
48};
49
50struct ClassInfo {
51 Class log_class;
52
53 /**
54 * Total number of (direct or indirect) sub classes this class has. If any, they follow in
55 * sequence after this class in the class list.
56 */
57 unsigned int num_children = 0;
58
59 ClassInfo(Class log_class) : log_class(log_class) {}
60};
61
62/**
63 * Logging management class. This class has the dual purpose of acting as an exchange point between
64 * the logging clients and the log outputter, as well as containing reflection info about available
65 * log classes.
66 */
67class Logger {
68private:
69 using Buffer = Common::ConcurrentRingBuffer<Entry, 16 * 1024 / sizeof(Entry)>;
70
71public:
72 static const size_t QUEUE_CLOSED = Buffer::QUEUE_CLOSED;
73
74 Logger();
75
76 /**
77 * Returns a list of all vector classes and subclasses. The sequence returned is a pre-order of
78 * classes and subclasses, which together with the `num_children` field in ClassInfo, allows
79 * you to recover the hierarchy.
80 */
81 const std::vector<ClassInfo>& GetClasses() const { return all_classes; }
82
83 /**
84 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
85 * instead of underscores as in the enumeration.
86 */
87 static const char* GetLogClassName(Class log_class);
88
89 /**
90 * Returns the name of the passed log level as a C-string.
91 */
92 static const char* GetLevelName(Level log_level);
93
94 /**
95 * Appends a messages to the log buffer.
96 * @note This function is thread safe.
97 */
98 void LogMessage(Entry entry);
99
100 /**
101 * Retrieves a batch of messages from the log buffer, blocking until they are available.
102 * @note This function is thread safe.
103 *
104 * @param out_buffer Destination buffer that will receive the log entries.
105 * @param buffer_len The maximum size of `out_buffer`.
106 * @return The number of entries stored. In case the logger is shutting down, `QUEUE_CLOSED` is
107 * returned, no entries are stored and the logger should shutdown.
108 */
109 size_t GetEntries(Entry* out_buffer, size_t buffer_len);
110
111 /**
112 * Initiates a shutdown of the logger. This will indicate to log output clients that they
113 * should shutdown.
114 */
115 void Close() { ring_buffer.Close(); }
116
117 /**
118 * Returns true if Close() has already been called on the Logger.
119 */
120 bool IsClosed() const { return ring_buffer.IsClosed(); }
121
122private:
123 Buffer ring_buffer;
124 std::vector<ClassInfo> all_classes;
125};
126
127/// Creates a log entry by formatting the given source location, and message.
128Entry CreateEntry(Class log_class, Level log_level,
129 const char* filename, unsigned int line_nr, const char* function,
130 const char* format, va_list args);
131/// Initializes the default Logger.
132std::shared_ptr<Logger> InitGlobalLogger();
133
134}
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
new file mode 100644
index 000000000..0cf9b05e7
--- /dev/null
+++ b/src/common/logging/filter.cpp
@@ -0,0 +1,132 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/logging/filter.h"
8#include "common/logging/backend.h"
9#include "common/string_util.h"
10
11namespace Log {
12
13Filter::Filter(Level default_level) {
14 ResetAll(default_level);
15}
16
17void Filter::ResetAll(Level level) {
18 class_levels.fill(level);
19}
20
21void Filter::SetClassLevel(Class log_class, Level level) {
22 class_levels[static_cast<size_t>(log_class)] = level;
23}
24
25void Filter::SetSubclassesLevel(const ClassInfo& log_class, Level level) {
26 const size_t log_class_i = static_cast<size_t>(log_class.log_class);
27
28 const size_t begin = log_class_i + 1;
29 const size_t end = begin + log_class.num_children;
30 for (size_t i = begin; begin < end; ++i) {
31 class_levels[i] = level;
32 }
33}
34
35void Filter::ParseFilterString(const std::string& filter_str) {
36 auto clause_begin = filter_str.cbegin();
37 while (clause_begin != filter_str.cend()) {
38 auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
39
40 // If clause isn't empty
41 if (clause_end != clause_begin) {
42 ParseFilterRule(clause_begin, clause_end);
43 }
44
45 if (clause_end != filter_str.cend()) {
46 // Skip over the whitespace
47 ++clause_end;
48 }
49 clause_begin = clause_end;
50 }
51}
52
53template <typename It>
54static Level GetLevelByName(const It begin, const It end) {
55 for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
56 const char* level_name = Logger::GetLevelName(static_cast<Level>(i));
57 if (Common::ComparePartialString(begin, end, level_name)) {
58 return static_cast<Level>(i);
59 }
60 }
61 return Level::Count;
62}
63
64template <typename It>
65static Class GetClassByName(const It begin, const It end) {
66 for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
67 const char* level_name = Logger::GetLogClassName(static_cast<Class>(i));
68 if (Common::ComparePartialString(begin, end, level_name)) {
69 return static_cast<Class>(i);
70 }
71 }
72 return Class::Count;
73}
74
75template <typename InputIt, typename T>
76static InputIt find_last(InputIt begin, const InputIt end, const T& value) {
77 auto match = end;
78 while (begin != end) {
79 auto new_match = std::find(begin, end, value);
80 if (new_match != end) {
81 match = new_match;
82 ++new_match;
83 }
84 begin = new_match;
85 }
86 return match;
87}
88
89bool Filter::ParseFilterRule(const std::string::const_iterator begin,
90 const std::string::const_iterator end) {
91 auto level_separator = std::find(begin, end, ':');
92 if (level_separator == end) {
93 LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
94 std::string(begin, end).c_str());
95 return false;
96 }
97
98 const Level level = GetLevelByName(level_separator + 1, end);
99 if (level == Level::Count) {
100 LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
101 return false;
102 }
103
104 if (Common::ComparePartialString(begin, level_separator, "*")) {
105 ResetAll(level);
106 return true;
107 }
108
109 auto class_name_end = find_last(begin, level_separator, '.');
110 if (class_name_end != level_separator &&
111 !Common::ComparePartialString(class_name_end + 1, level_separator, "*")) {
112 class_name_end = level_separator;
113 }
114
115 const Class log_class = GetClassByName(begin, class_name_end);
116 if (log_class == Class::Count) {
117 LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
118 return false;
119 }
120
121 if (class_name_end == level_separator) {
122 SetClassLevel(log_class, level);
123 }
124 SetSubclassesLevel(log_class, level);
125 return true;
126}
127
128bool Filter::CheckMessage(Class log_class, Level level) const {
129 return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
130}
131
132}
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
new file mode 100644
index 000000000..32b14b159
--- /dev/null
+++ b/src/common/logging/filter.h
@@ -0,0 +1,63 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <array>
6#include <string>
7
8#include "common/logging/log.h"
9
10namespace Log {
11
12struct ClassInfo;
13
14/**
15 * Implements a log message filter which allows different log classes to have different minimum
16 * severity levels. The filter can be changed at runtime and can be parsed from a string to allow
17 * editing via the interface or loading from a configuration file.
18 */
19class Filter {
20public:
21 /// Initializes the filter with all classes having `default_level` as the minimum level.
22 Filter(Level default_level);
23
24 /// Resets the filter so that all classes have `level` as the minimum displayed level.
25 void ResetAll(Level level);
26 /// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
27 void SetClassLevel(Class log_class, Level level);
28 /**
29 * Sets the minimum level of all of `log_class` subclasses to `level`. The level of `log_class`
30 * itself is not changed.
31 */
32 void SetSubclassesLevel(const ClassInfo& log_class, Level level);
33
34 /**
35 * Parses a filter string and applies it to this filter.
36 *
37 * A filter string consists of a space-separated list of filter rules, each of the format
38 * `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods.
39 * A rule for a given class also affects all of its subclasses. `*` wildcards are allowed and
40 * can be used to apply a rule to all classes or to all subclasses of a class without affecting
41 * the parent class. `<level>` a severity level name which will be set as the minimum logging
42 * level of the matched classes. Rules are applied left to right, with each rule overriding
43 * previous ones in the sequence.
44 *
45 * A few examples of filter rules:
46 * - `*:Info` -- Resets the level of all classes to Info.
47 * - `Service:Info` -- Sets the level of Service and all subclasses (Service.FS, Service.APT,
48 * etc.) to Info.
49 * - `Service.*:Debug` -- Sets the level of all Service subclasses to Debug, while leaving the
50 * level of Service unchanged.
51 * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
52 */
53 void ParseFilterString(const std::string& filter_str);
54 bool ParseFilterRule(const std::string::const_iterator start, const std::string::const_iterator end);
55
56 /// Matches class/level combination against the filter, returning true if it passed.
57 bool CheckMessage(Class log_class, Level level) const;
58
59private:
60 std::array<Level, (size_t)Class::Count> class_levels;
61};
62
63}
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
new file mode 100644
index 000000000..1eec34fbb
--- /dev/null
+++ b/src/common/logging/log.h
@@ -0,0 +1,115 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cassert>
8#include <chrono>
9#include <string>
10
11#include "common/common_types.h"
12
13namespace Log {
14
15/// Specifies the severity or level of detail of the log message.
16enum class Level : u8 {
17 Trace, ///< Extremely detailed and repetitive debugging information that is likely to
18 /// pollute logs.
19 Debug, ///< Less detailed debugging information.
20 Info, ///< Status information from important points during execution.
21 Warning, ///< Minor or potential problems found during execution of a task.
22 Error, ///< Major problems found during execution of a task that prevent it from being
23 /// completed.
24 Critical, ///< Major problems during execution that threathen the stability of the entire
25 /// application.
26
27 Count ///< Total number of logging levels
28};
29
30typedef u8 ClassType;
31
32/**
33 * Specifies the sub-system that generated the log message.
34 *
35 * @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in log.cpp.
36 */
37enum class Class : ClassType {
38 Log, ///< Messages about the log system itself
39 Common, ///< Library routines
40 Common_Filesystem, ///< Filesystem interface library
41 Common_Memory, ///< Memory mapping and management functions
42 Core, ///< LLE emulation core
43 Core_ARM11, ///< ARM11 CPU core
44 Config, ///< Emulator configuration (including commandline)
45 Debug, ///< Debugging tools
46 Debug_Emulated, ///< Debug messages from the emulated programs
47 Debug_GPU, ///< GPU debugging tools
48 Debug_Breakpoint, ///< Logging breakpoints and watchpoints
49 Kernel, ///< The HLE implementation of the CTR kernel
50 Kernel_SVC, ///< Kernel system calls
51 Service, ///< HLE implementation of system services. Each major service
52 /// should have its own subclass.
53 Service_SRV, ///< The SRV (Service Directory) implementation
54 Service_FS, ///< The FS (Filesystem) service implementation
55 Service_APT, ///< The APT (Applets) service
56 Service_GSP, ///< The GSP (GPU control) service
57 Service_AC, ///< The AC (WiFi status) service
58 Service_PTM, ///< The PTM (Power status & misc.) service
59 Service_CFG, ///< The CFG (Configuration) service
60 Service_DSP, ///< The DSP (DSP control) service
61 Service_HID, ///< The HID (User input) service
62 HW, ///< Low-level hardware emulation
63 HW_Memory, ///< Memory-map and address translation
64 HW_GPU, ///< GPU control emulation
65 Frontend, ///< Emulator UI
66 Render, ///< Emulator video output and hardware acceleration
67 Render_Software, ///< Software renderer backend
68 Render_OpenGL, ///< OpenGL backend
69 Loader, ///< ROM loader
70
71 Count ///< Total number of logging classes
72};
73
74/**
75 * Level below which messages are simply discarded without buffering regardless of the display
76 * settings.
77 */
78const Level MINIMUM_LEVEL =
79#ifdef _DEBUG
80 Level::Trace;
81#else
82 Level::Debug;
83#endif
84
85/**
86 * Logs a message to the global logger. This proxy exists to avoid exposing the details of the
87 * Logger class, including the ConcurrentRingBuffer template, to all files that desire to log
88 * messages, reducing unecessary recompilations.
89 */
90void LogMessage(Class log_class, Level log_level,
91 const char* filename, unsigned int line_nr, const char* function,
92#ifdef _MSC_VER
93 _Printf_format_string_
94#endif
95 const char* format, ...)
96#ifdef __GNUC__
97 __attribute__((format(printf, 6, 7)))
98#endif
99 ;
100
101} // namespace Log
102
103#define LOG_GENERIC(log_class, log_level, ...) \
104 do { \
105 if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \
106 ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \
107 __FILE__, __LINE__, __func__, __VA_ARGS__); \
108 } while (0)
109
110#define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__)
111#define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__)
112#define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__)
113#define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__)
114#define LOG_ERROR( log_class, ...) LOG_GENERIC(log_class, Error, __VA_ARGS__)
115#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 000000000..f6b02fd47
--- /dev/null
+++ b/src/common/logging/text_formatter.cpp
@@ -0,0 +1,136 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <array>
6#include <cstdio>
7
8#ifdef _WIN32
9# define WIN32_LEAN_AND_MEAN
10# include <Windows.h>
11#endif
12
13#include "common/logging/backend.h"
14#include "common/logging/filter.h"
15#include "common/logging/log.h"
16#include "common/logging/text_formatter.h"
17
18#include "common/string_util.h"
19
20namespace Log {
21
22// TODO(bunnei): This should be moved to a generic path manipulation library
23const char* TrimSourcePath(const char* path, const char* root) {
24 const char* p = path;
25
26 while (*p != '\0') {
27 const char* next_slash = p;
28 while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
29 ++next_slash;
30 }
31
32 bool is_src = Common::ComparePartialString(p, next_slash, root);
33 p = next_slash;
34
35 if (*p != '\0') {
36 ++p;
37 }
38 if (is_src) {
39 path = p;
40 }
41 }
42 return path;
43}
44
45void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
46 unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
47 unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
48
49 const char* class_name = Logger::GetLogClassName(entry.log_class);
50 const char* level_name = Logger::GetLevelName(entry.log_level);
51
52 snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s",
53 time_seconds, time_fractional, class_name, level_name,
54 TrimSourcePath(entry.location.c_str()), entry.message.c_str());
55}
56
57void PrintMessage(const Entry& entry) {
58 std::array<char, 4 * 1024> format_buffer;
59 FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
60 fputs(format_buffer.data(), stderr);
61 fputc('\n', stderr);
62}
63
64void PrintColoredMessage(const Entry& entry) {
65#ifdef _WIN32
66 static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
67
68 CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
69 GetConsoleScreenBufferInfo(console_handle, &original_info);
70
71 WORD color = 0;
72 switch (entry.log_level) {
73 case Level::Trace: // Grey
74 color = FOREGROUND_INTENSITY; break;
75 case Level::Debug: // Cyan
76 color = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
77 case Level::Info: // Bright gray
78 color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
79 case Level::Warning: // Bright yellow
80 color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
81 case Level::Error: // Bright red
82 color = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
83 case Level::Critical: // Bright magenta
84 color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
85 }
86
87 SetConsoleTextAttribute(console_handle, color);
88#else
89# define ESC "\x1b"
90 const char* color = "";
91 switch (entry.log_level) {
92 case Level::Trace: // Grey
93 color = ESC "[1;30m"; break;
94 case Level::Debug: // Cyan
95 color = ESC "[0;36m"; break;
96 case Level::Info: // Bright gray
97 color = ESC "[0;37m"; break;
98 case Level::Warning: // Bright yellow
99 color = ESC "[1;33m"; break;
100 case Level::Error: // Bright red
101 color = ESC "[1;31m"; break;
102 case Level::Critical: // Bright magenta
103 color = ESC "[1;35m"; break;
104 }
105
106 fputs(color, stderr);
107#endif
108
109 PrintMessage(entry);
110
111#ifdef _WIN32
112 SetConsoleTextAttribute(console_handle, original_info.wAttributes);
113#else
114 fputs(ESC "[0m", stderr);
115# undef ESC
116#endif
117}
118
119void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter) {
120 std::array<Entry, 256> entry_buffer;
121
122 while (true) {
123 size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size());
124 if (num_entries == Logger::QUEUE_CLOSED) {
125 break;
126 }
127 for (size_t i = 0; i < num_entries; ++i) {
128 const Entry& entry = entry_buffer[i];
129 if (filter->CheckMessage(entry.log_class, entry.log_level)) {
130 PrintColoredMessage(entry);
131 }
132 }
133 }
134}
135
136}
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
new file mode 100644
index 000000000..1f73ca44a
--- /dev/null
+++ b/src/common/logging/text_formatter.h
@@ -0,0 +1,41 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <memory>
9
10namespace Log {
11
12class Logger;
13struct Entry;
14class Filter;
15
16/**
17 * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
18 * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
19 * leaving only the path relative to the sources root.
20 *
21 * @param path The input file path as a null-terminated string
22 * @param root The name of the root source directory as a null-terminated string. Path up to and
23 * including the last occurence of this name will be stripped
24 * @return A pointer to the same string passed as `path`, but starting at the trimmed portion
25 */
26const char* TrimSourcePath(const char* path, const char* root = "src");
27
28/// Formats a log entry into the provided text buffer.
29void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
30/// Formats and prints a log entry to stderr.
31void PrintMessage(const Entry& entry);
32/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
33void PrintColoredMessage(const Entry& entry);
34
35/**
36 * Logging loop that repeatedly reads messages from the provided logger and prints them to the
37 * console. It is the baseline barebones log outputter.
38 */
39void TextLoggingLoop(std::shared_ptr<Logger> logger, const Filter* filter);
40
41}
diff --git a/src/common/math_util.cpp b/src/common/math_util.cpp
index ab0e6b75c..3613e82a6 100644
--- a/src/common/math_util.cpp
+++ b/src/common/math_util.cpp
@@ -18,7 +18,7 @@ u32 ClassifyDouble(double dvalue)
18 value.d = dvalue; 18 value.d = dvalue;
19 u64 sign = value.i & DOUBLE_SIGN; 19 u64 sign = value.i & DOUBLE_SIGN;
20 u64 exp = value.i & DOUBLE_EXP; 20 u64 exp = value.i & DOUBLE_EXP;
21 if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP) 21 if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP)
22 { 22 {
23 // Nice normalized number. 23 // Nice normalized number.
24 return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; 24 return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
@@ -58,7 +58,7 @@ u32 ClassifyFloat(float fvalue)
58 value.f = fvalue; 58 value.f = fvalue;
59 u32 sign = value.i & FLOAT_SIGN; 59 u32 sign = value.i & FLOAT_SIGN;
60 u32 exp = value.i & FLOAT_EXP; 60 u32 exp = value.i & FLOAT_EXP;
61 if (exp > FLOAT_ZERO && exp < FLOAT_EXP) 61 if (exp > FLOAT_ZERO && exp < FLOAT_EXP)
62 { 62 {
63 // Nice normalized number. 63 // Nice normalized number.
64 return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; 64 return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
@@ -77,13 +77,13 @@ u32 ClassifyFloat(float fvalue)
77 // Denormalized number. 77 // Denormalized number.
78 return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD; 78 return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
79 } 79 }
80 } 80 }
81 else if (exp) 81 else if (exp)
82 { 82 {
83 // Infinite 83 // Infinite
84 return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF; 84 return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
85 } 85 }
86 else 86 else
87 { 87 {
88 //Zero 88 //Zero
89 return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ; 89 return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
@@ -143,7 +143,7 @@ void Matrix33::RotateY(Matrix33 &mtx, float rad)
143 mtx.data[0] = c; 143 mtx.data[0] = c;
144 mtx.data[2] = s; 144 mtx.data[2] = s;
145 mtx.data[4] = 1; 145 mtx.data[4] = 1;
146 mtx.data[6] = -s; 146 mtx.data[6] = -s;
147 mtx.data[8] = c; 147 mtx.data[8] = c;
148} 148}
149 149
diff --git a/src/common/math_util.h b/src/common/math_util.h
index b32e7bb14..b10a25c13 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -7,6 +7,7 @@
7#include "common/common.h" 7#include "common/common.h"
8 8
9#include <algorithm> 9#include <algorithm>
10#include <type_traits>
10#include <vector> 11#include <vector>
11 12
12namespace MathUtil 13namespace MathUtil
@@ -109,11 +110,11 @@ struct Rectangle
109 Rectangle(T theLeft, T theTop, T theRight, T theBottom) 110 Rectangle(T theLeft, T theTop, T theRight, T theBottom)
110 : left(theLeft), top(theTop), right(theRight), bottom(theBottom) 111 : left(theLeft), top(theTop), right(theRight), bottom(theBottom)
111 { } 112 { }
112 113
113 bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } 114 bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; }
114 115
115 T GetWidth() const { return abs(right - left); } 116 T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); }
116 T GetHeight() const { return abs(bottom - top); } 117 T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); }
117 118
118 // If the rectangle is in a coordinate system with a lower-left origin, use 119 // If the rectangle is in a coordinate system with a lower-left origin, use
119 // this Clamp. 120 // this Clamp.
@@ -127,7 +128,7 @@ struct Rectangle
127 128
128 // If the rectangle is in a coordinate system with an upper-left origin, 129 // If the rectangle is in a coordinate system with an upper-left origin,
129 // use this Clamp. 130 // use this Clamp.
130 void ClampUL(T x1, T y1, T x2, T y2) 131 void ClampUL(T x1, T y1, T x2, T y2)
131 { 132 {
132 if (left < x1) left = x1; 133 if (left < x1) left = x1;
133 if (right > x2) right = x2; 134 if (right > x2) right = x2;
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp
index 40d9c03a2..9904d2472 100644
--- a/src/common/mem_arena.cpp
+++ b/src/common/mem_arena.cpp
@@ -30,7 +30,7 @@
30#endif 30#endif
31 31
32#ifdef IOS 32#ifdef IOS
33void* globalbase = NULL; 33void* globalbase = nullptr;
34#endif 34#endif
35 35
36#ifdef ANDROID 36#ifdef ANDROID
@@ -38,7 +38,7 @@ void* globalbase = NULL;
38// Hopefully this ABI will never change... 38// Hopefully this ABI will never change...
39 39
40 40
41#define ASHMEM_DEVICE "/dev/ashmem" 41#define ASHMEM_DEVICE "/dev/ashmem"
42 42
43/* 43/*
44* ashmem_create_region - creates a new ashmem region and returns the file 44* 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)
71 return fd; 71 return fd;
72 72
73error: 73error:
74 ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret); 74 LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret);
75 close(fd); 75 close(fd);
76 return ret; 76 return ret;
77} 77}
@@ -121,7 +121,7 @@ void MemArena::GrabLowMemSpace(size_t size)
121{ 121{
122#ifdef _WIN32 122#ifdef _WIN32
123#ifndef _XBOX 123#ifndef _XBOX
124 hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL); 124 hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr);
125 GetSystemInfo(&sysInfo); 125 GetSystemInfo(&sysInfo);
126#endif 126#endif
127#elif defined(ANDROID) 127#elif defined(ANDROID)
@@ -130,7 +130,7 @@ void MemArena::GrabLowMemSpace(size_t size)
130 // Note that it appears that ashmem is pinned by default, so no need to pin. 130 // Note that it appears that ashmem is pinned by default, so no need to pin.
131 if (fd < 0) 131 if (fd < 0)
132 { 132 {
133 ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno)); 133 LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
134 return; 134 return;
135 } 135 }
136#else 136#else
@@ -148,12 +148,12 @@ void MemArena::GrabLowMemSpace(size_t size)
148 } 148 }
149 else if (errno != EEXIST) 149 else if (errno != EEXIST)
150 { 150 {
151 ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno)); 151 LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno));
152 return; 152 return;
153 } 153 }
154 } 154 }
155 if (ftruncate(fd, size) < 0) 155 if (ftruncate(fd, size) < 0)
156 ERROR_LOG(MEMMAP, "Failed to allocate low memory space"); 156 LOG_ERROR(Common_Memory, "Failed to allocate low memory space");
157#endif 157#endif
158} 158}
159 159
@@ -178,7 +178,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
178#ifdef _XBOX 178#ifdef _XBOX
179 size = roundup(size); 179 size = roundup(size);
180 // use 64kb pages 180 // use 64kb pages
181 void * ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); 181 void * ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
182 return ptr; 182 return ptr;
183#else 183#else
184 size = roundup(size); 184 size = roundup(size);
@@ -197,7 +197,7 @@ void *MemArena::CreateView(s64 offset, size_t size, void *base)
197 197
198 if (retval == MAP_FAILED) 198 if (retval == MAP_FAILED)
199 { 199 {
200 NOTICE_LOG(MEMMAP, "mmap failed"); 200 LOG_ERROR(Common_Memory, "mmap failed");
201 return nullptr; 201 return nullptr;
202 } 202 }
203 return retval; 203 return retval;
@@ -243,8 +243,8 @@ u8* MemArena::Find4GBBase()
243 return base; 243 return base;
244#else 244#else
245#ifdef IOS 245#ifdef IOS
246 void* base = NULL; 246 void* base = nullptr;
247 if (globalbase == NULL){ 247 if (globalbase == nullptr){
248 base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE, 248 base = mmap(0, 0x08000000, PROT_READ | PROT_WRITE,
249 MAP_ANON | MAP_SHARED, -1, 0); 249 MAP_ANON | MAP_SHARED, -1, 0);
250 if (base == MAP_FAILED) { 250 if (base == MAP_FAILED) {
@@ -272,11 +272,11 @@ u8* MemArena::Find4GBBase()
272 272
273 273
274// yeah, this could also be done in like two bitwise ops... 274// yeah, this could also be done in like two bitwise ops...
275#define SKIP(a_flags, b_flags) 275#define SKIP(a_flags, b_flags)
276// if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) 276//if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY))
277// continue; 277// continue;
278// if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) 278//if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM))
279// continue; 279// continue;
280 280
281static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) { 281static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) {
282 // OK, we know where to find free space. Now grab it! 282 // OK, we know where to find free space. Now grab it!
@@ -357,7 +357,7 @@ bail:
357 if (views[j].out_ptr_low && *views[j].out_ptr_low) 357 if (views[j].out_ptr_low && *views[j].out_ptr_low)
358 { 358 {
359 arena->ReleaseView(*views[j].out_ptr_low, views[j].size); 359 arena->ReleaseView(*views[j].out_ptr_low, views[j].size);
360 *views[j].out_ptr_low = NULL; 360 *views[j].out_ptr_low = nullptr;
361 } 361 }
362 if (*views[j].out_ptr) 362 if (*views[j].out_ptr)
363 { 363 {
@@ -369,7 +369,7 @@ bail:
369 arena->ReleaseView(*views[j].out_ptr, views[j].size); 369 arena->ReleaseView(*views[j].out_ptr, views[j].size);
370 } 370 }
371#endif 371#endif
372 *views[j].out_ptr = NULL; 372 *views[j].out_ptr = nullptr;
373 } 373 }
374 } 374 }
375 return false; 375 return false;
@@ -415,7 +415,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
415#elif defined(_WIN32) 415#elif defined(_WIN32)
416 // Try a whole range of possible bases. Return once we got a valid one. 416 // Try a whole range of possible bases. Return once we got a valid one.
417 u32 max_base_addr = 0x7FFF0000 - 0x10000000; 417 u32 max_base_addr = 0x7FFF0000 - 0x10000000;
418 u8 *base = NULL; 418 u8 *base = nullptr;
419 419
420 for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000) 420 for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000)
421 { 421 {
@@ -423,7 +423,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
423 base = (u8 *)base_addr; 423 base = (u8 *)base_addr;
424 if (Memory_TryBase(base, views, num_views, flags, arena)) 424 if (Memory_TryBase(base, views, num_views, flags, arena))
425 { 425 {
426 INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts); 426 LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts);
427 base_attempts = 0; 427 base_attempts = 0;
428 break; 428 break;
429 } 429 }
@@ -442,7 +442,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena
442 u8 *base = MemArena::Find4GBBase(); 442 u8 *base = MemArena::Find4GBBase();
443 if (!Memory_TryBase(base, views, num_views, flags, arena)) 443 if (!Memory_TryBase(base, views, num_views, flags, arena))
444 { 444 {
445 ERROR_LOG(MEMMAP, "MemoryMap_Setup: Failed finding a memory base."); 445 LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
446 PanicAlert("MemoryMap_Setup: Failed finding a memory base."); 446 PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
447 return 0; 447 return 0;
448 } 448 }
@@ -463,8 +463,8 @@ void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemAr
463 arena->ReleaseView(*views[i].out_ptr_low, views[i].size); 463 arena->ReleaseView(*views[i].out_ptr_low, views[i].size);
464 if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low)) 464 if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low))
465 arena->ReleaseView(*views[i].out_ptr, views[i].size); 465 arena->ReleaseView(*views[i].out_ptr, views[i].size);
466 *views[i].out_ptr = NULL; 466 *views[i].out_ptr = nullptr;
467 if (views[i].out_ptr_low) 467 if (views[i].out_ptr_low)
468 *views[i].out_ptr_low = NULL; 468 *views[i].out_ptr_low = nullptr;
469 } 469 }
470} 470}
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
index bab7d9f7a..ca8a2a9e4 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -47,7 +47,7 @@ void* AllocateExecutableMemory(size_t size, bool low)
47 47
48 // printf("Mapped executable memory at %p (size %ld)\n", ptr, 48 // printf("Mapped executable memory at %p (size %ld)\n", ptr,
49 // (unsigned long)size); 49 // (unsigned long)size);
50 50
51#ifdef _WIN32 51#ifdef _WIN32
52 if (ptr == nullptr) 52 if (ptr == nullptr)
53 { 53 {
@@ -55,7 +55,7 @@ void* AllocateExecutableMemory(size_t size, bool low)
55 if (ptr == MAP_FAILED) 55 if (ptr == MAP_FAILED)
56 { 56 {
57 ptr = nullptr; 57 ptr = nullptr;
58#endif 58#endif
59 PanicAlert("Failed to allocate executable memory"); 59 PanicAlert("Failed to allocate executable memory");
60 } 60 }
61#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) 61#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT)
@@ -93,7 +93,7 @@ void* AllocateMemoryPages(size_t size)
93 // printf("Mapped memory at %p (size %ld)\n", ptr, 93 // printf("Mapped memory at %p (size %ld)\n", ptr,
94 // (unsigned long)size); 94 // (unsigned long)size);
95 95
96 if (ptr == NULL) 96 if (ptr == nullptr)
97 PanicAlert("Failed to allocate raw memory"); 97 PanicAlert("Failed to allocate raw memory");
98 98
99 return ptr; 99 return ptr;
@@ -104,19 +104,19 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
104#ifdef _WIN32 104#ifdef _WIN32
105 void* ptr = _aligned_malloc(size,alignment); 105 void* ptr = _aligned_malloc(size,alignment);
106#else 106#else
107 void* ptr = NULL; 107 void* ptr = nullptr;
108#ifdef ANDROID 108#ifdef ANDROID
109 ptr = memalign(alignment, size); 109 ptr = memalign(alignment, size);
110#else 110#else
111 if (posix_memalign(&ptr, alignment, size) != 0) 111 if (posix_memalign(&ptr, alignment, size) != 0)
112 ERROR_LOG(MEMMAP, "Failed to allocate aligned memory"); 112 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
113#endif 113#endif
114#endif 114#endif
115 115
116 // printf("Mapped memory at %p (size %ld)\n", ptr, 116 // printf("Mapped memory at %p (size %ld)\n", ptr,
117 // (unsigned long)size); 117 // (unsigned long)size);
118 118
119 if (ptr == NULL) 119 if (ptr == nullptr)
120 PanicAlert("Failed to allocate aligned memory"); 120 PanicAlert("Failed to allocate aligned memory");
121 121
122 return ptr; 122 return ptr;
@@ -127,11 +127,11 @@ void FreeMemoryPages(void* ptr, size_t size)
127 if (ptr) 127 if (ptr)
128 { 128 {
129#ifdef _WIN32 129#ifdef _WIN32
130 130
131 if (!VirtualFree(ptr, 0, MEM_RELEASE)) 131 if (!VirtualFree(ptr, 0, MEM_RELEASE))
132 PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); 132 PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg());
133 ptr = NULL; // Is this our responsibility? 133 ptr = nullptr; // Is this our responsibility?
134 134
135#else 135#else
136 munmap(ptr, size); 136 munmap(ptr, size);
137#endif 137#endif
@@ -184,7 +184,7 @@ std::string MemUsage()
184 // Print information about the memory usage of the process. 184 // Print information about the memory usage of the process.
185 185
186 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); 186 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
187 if (NULL == hProcess) return "MemUsage Error"; 187 if (nullptr == hProcess) return "MemUsage Error";
188 188
189 if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) 189 if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
190 Ret = Common::StringFromFormat("%s K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str()); 190 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 cf6df44e8..bc9d26188 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -23,9 +23,9 @@ const char* GetLastErrorMsg()
23#ifdef _WIN32 23#ifdef _WIN32
24 static __declspec(thread) char err_str[buff_size] = {}; 24 static __declspec(thread) char err_str[buff_size] = {};
25 25
26 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 26 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
27 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 27 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
28 err_str, buff_size, NULL); 28 err_str, buff_size, nullptr);
29#else 29#else
30 static __thread char err_str[buff_size] = {}; 30 static __thread char err_str[buff_size] = {};
31 31
diff --git a/src/common/msg_handler.cpp b/src/common/msg_handler.cpp
index b3556aaa8..7ffedc45a 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, ...)
75 Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); 75 Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args);
76 va_end(args); 76 va_end(args);
77 77
78 ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer); 78 LOG_INFO(Common, "%s: %s", caption.c_str(), buffer);
79 79
80 // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored 80 // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored
81 if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) 81 if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL))
diff --git a/src/common/msg_handler.h b/src/common/msg_handler.h
index 7c5319ec3..9bfdf950e 100644
--- a/src/common/msg_handler.h
+++ b/src/common/msg_handler.h
@@ -15,7 +15,7 @@ enum MSG_TYPE
15 CRITICAL 15 CRITICAL
16}; 16};
17 17
18typedef bool (*MsgAlertHandler)(const char* caption, const char* text, 18typedef bool (*MsgAlertHandler)(const char* caption, const char* text,
19 bool yes_no, int Style); 19 bool yes_no, int Style);
20typedef std::string (*StringTranslator)(const char* text); 20typedef std::string (*StringTranslator)(const char* text);
21 21
@@ -31,29 +31,29 @@ void SetEnableAlert(bool enable);
31 31
32#ifndef GEKKO 32#ifndef GEKKO
33#ifdef _WIN32 33#ifdef _WIN32
34 #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) 34 #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__)
35 #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) 35 #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__)
36 #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) 36 #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__)
37 #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) 37 #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__)
38 #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) 38 #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__)
39 // Use these macros (that do the same thing) if the message should be translated. 39 // Use these macros (that do the same thing) if the message should be translated.
40 #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) 40 #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__)
41 #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) 41 #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__)
42 #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) 42 #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__)
43 #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) 43 #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__)
44 #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) 44 #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__)
45#else 45#else
46 #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) 46 #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__)
47 #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) 47 #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__)
48 #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) 48 #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__)
49 #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) 49 #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__)
50 #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) 50 #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__)
51 // Use these macros (that do the same thing) if the message should be translated. 51 // Use these macros (that do the same thing) if the message should be translated.
52 #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) 52 #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__)
53 #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) 53 #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__)
54 #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) 54 #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__)
55 #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) 55 #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__)
56 #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) 56 #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__)
57#endif 57#endif
58#else 58#else
59// GEKKO 59// GEKKO
diff --git a/src/common/platform.h b/src/common/platform.h
index d9f095433..53d98fe74 100644
--- a/src/common/platform.h
+++ b/src/common/platform.h
@@ -77,7 +77,7 @@
77inline struct tm* localtime_r(const time_t *clock, struct tm *result) { 77inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
78 if (localtime_s(result, clock) == 0) 78 if (localtime_s(result, clock) == 0)
79 return result; 79 return result;
80 return NULL; 80 return nullptr;
81} 81}
82 82
83#else 83#else
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h
new file mode 100644
index 000000000..1d3e59319
--- /dev/null
+++ b/src/common/scope_exit.h
@@ -0,0 +1,37 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace detail {
8 template <typename Func>
9 struct ScopeExitHelper {
10 explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
11 ~ScopeExitHelper() { func(); }
12
13 Func func;
14 };
15
16 template <typename Func>
17 ScopeExitHelper<Func> ScopeExit(Func&& func) { return ScopeExitHelper<Func>(std::move(func)); }
18}
19
20/**
21 * This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
22 * for doing ad-hoc clean-up tasks in a function with multiple returns.
23 *
24 * Example usage:
25 * \code
26 * const int saved_val = g_foo;
27 * g_foo = 55;
28 * SCOPE_EXIT({ g_foo = saved_val; });
29 *
30 * if (Bar()) {
31 * return 0;
32 * } else {
33 * return 20;
34 * }
35 * \endcode
36 */
37#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 61f0939c4..6d9612fb5 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -2,13 +2,14 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <boost/range/algorithm.hpp>
6 6
7#include "common/common.h" 7#include "common/common.h"
8#include "common/string_util.h" 8#include "common/string_util.h"
9 9
10#ifdef _WIN32 10#ifdef _WIN32
11 #include <Windows.h> 11 #include <Windows.h>
12 #include <codecvt>
12#else 13#else
13 #include <iconv.h> 14 #include <iconv.h>
14#endif 15#endif
@@ -17,20 +18,20 @@ namespace Common {
17 18
18/// Make a string lowercase 19/// Make a string lowercase
19std::string ToLower(std::string str) { 20std::string ToLower(std::string str) {
20 std::transform(str.begin(), str.end(), str.begin(), ::tolower); 21 boost::transform(str, str.begin(), ::tolower);
21 return str; 22 return str;
22} 23}
23 24
24/// Make a string uppercase 25/// Make a string uppercase
25std::string ToUpper(std::string str) { 26std::string ToUpper(std::string str) {
26 std::transform(str.begin(), str.end(), str.begin(), ::toupper); 27 boost::transform(str, str.begin(), ::toupper);
27 return str; 28 return str;
28} 29}
29 30
30// faster than sscanf 31// faster than sscanf
31bool AsciiToHex(const char* _szValue, u32& result) 32bool AsciiToHex(const char* _szValue, u32& result)
32{ 33{
33 char *endptr = NULL; 34 char *endptr = nullptr;
34 const u32 value = strtoul(_szValue, &endptr, 16); 35 const u32 value = strtoul(_szValue, &endptr, 16);
35 36
36 if (!endptr || *endptr) 37 if (!endptr || *endptr)
@@ -68,7 +69,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar
68 // will be present in the middle of a multibyte sequence. 69 // will be present in the middle of a multibyte sequence.
69 // 70 //
70 // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l. 71 // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
71 static locale_t c_locale = NULL; 72 static locale_t c_locale = nullptr;
72 if (!c_locale) 73 if (!c_locale)
73 c_locale = _create_locale(LC_ALL, ".1252"); 74 c_locale = _create_locale(LC_ALL, ".1252");
74 writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args); 75 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
91std::string StringFromFormat(const char* format, ...) 92std::string StringFromFormat(const char* format, ...)
92{ 93{
93 va_list args; 94 va_list args;
94 char *buf = NULL; 95 char *buf = nullptr;
95#ifdef _WIN32 96#ifdef _WIN32
96 int required = 0; 97 int required = 0;
97 98
@@ -106,7 +107,7 @@ std::string StringFromFormat(const char* format, ...)
106#else 107#else
107 va_start(args, format); 108 va_start(args, format);
108 if (vasprintf(&buf, format, args) < 0) 109 if (vasprintf(&buf, format, args) < 0)
109 ERROR_LOG(COMMON, "Unable to allocate memory for string"); 110 LOG_ERROR(Common, "Unable to allocate memory for string");
110 va_end(args); 111 va_end(args);
111 112
112 std::string temp = buf; 113 std::string temp = buf;
@@ -120,11 +121,11 @@ std::string ArrayToString(const u8 *data, u32 size, int line_len, bool spaces)
120{ 121{
121 std::ostringstream oss; 122 std::ostringstream oss;
122 oss << std::setfill('0') << std::hex; 123 oss << std::setfill('0') << std::hex;
123 124
124 for (int line = 0; size; ++data, --size) 125 for (int line = 0; size; ++data, --size)
125 { 126 {
126 oss << std::setw(2) << (int)*data; 127 oss << std::setw(2) << (int)*data;
127 128
128 if (line_len == ++line) 129 if (line_len == ++line)
129 { 130 {
130 oss << '\n'; 131 oss << '\n';
@@ -161,13 +162,13 @@ std::string StripQuotes(const std::string& s)
161 162
162bool TryParse(const std::string &str, u32 *const output) 163bool TryParse(const std::string &str, u32 *const output)
163{ 164{
164 char *endptr = NULL; 165 char *endptr = nullptr;
165 166
166 // Reset errno to a value other than ERANGE 167 // Reset errno to a value other than ERANGE
167 errno = 0; 168 errno = 0;
168 169
169 unsigned long value = strtoul(str.c_str(), &endptr, 0); 170 unsigned long value = strtoul(str.c_str(), &endptr, 0);
170 171
171 if (!endptr || *endptr) 172 if (!endptr || *endptr)
172 return false; 173 return false;
173 174
@@ -293,7 +294,7 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
293//#include <string> 294//#include <string>
294//#include <assert.h> 295//#include <assert.h>
295 296
296const char HEX2DEC[256] = 297const char HEX2DEC[256] =
297{ 298{
298 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 299 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
299 /* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, 300 /* 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)
326 const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); 327 const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
327 const size_t SRC_LEN = sSrc.length(); 328 const size_t SRC_LEN = sSrc.length();
328 const unsigned char * const SRC_END = pSrc + SRC_LEN; 329 const unsigned char * const SRC_END = pSrc + SRC_LEN;
329 const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%' 330 const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%'
330 331
331 char * const pStart = new char[SRC_LEN]; 332 char * const pStart = new char[SRC_LEN];
332 char * pEnd = pStart; 333 char * pEnd = pStart;
@@ -393,7 +394,7 @@ std::string UriEncode(const std::string & sSrc)
393 394
394 for (; pSrc < SRC_END; ++pSrc) 395 for (; pSrc < SRC_END; ++pSrc)
395 { 396 {
396 if (SAFE[*pSrc]) 397 if (SAFE[*pSrc])
397 *pEnd++ = *pSrc; 398 *pEnd++ = *pSrc;
398 else 399 else
399 { 400 {
@@ -411,7 +412,19 @@ std::string UriEncode(const std::string & sSrc)
411 412
412#ifdef _WIN32 413#ifdef _WIN32
413 414
414std::string UTF16ToUTF8(const std::wstring& input) 415std::string UTF16ToUTF8(const std::u16string& input)
416{
417 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
418 return convert.to_bytes(input);
419}
420
421std::u16string UTF8ToUTF16(const std::string& input)
422{
423 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
424 return convert.from_bytes(input);
425}
426
427static std::string UTF16ToUTF8(const std::wstring& input)
415{ 428{
416 auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), nullptr, 0, nullptr, nullptr); 429 auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), nullptr, 0, nullptr, nullptr);
417 430
@@ -424,7 +437,7 @@ std::string UTF16ToUTF8(const std::wstring& input)
424 return output; 437 return output;
425} 438}
426 439
427std::wstring CPToUTF16(u32 code_page, const std::string& input) 440static std::wstring CPToUTF16(u32 code_page, const std::string& input)
428{ 441{
429 auto const size = MultiByteToWideChar(code_page, 0, input.data(), input.size(), nullptr, 0); 442 auto const size = MultiByteToWideChar(code_page, 0, input.data(), input.size(), nullptr, 0);
430 443
@@ -437,7 +450,7 @@ std::wstring CPToUTF16(u32 code_page, const std::string& input)
437 return output; 450 return output;
438} 451}
439 452
440std::wstring UTF8ToUTF16(const std::string& input) 453std::wstring UTF8ToUTF16W(const std::string &input)
441{ 454{
442 return CPToUTF16(CP_UTF8, input); 455 return CPToUTF16(CP_UTF8, input);
443} 456}
@@ -455,61 +468,123 @@ std::string CP1252ToUTF8(const std::string& input)
455#else 468#else
456 469
457template <typename T> 470template <typename T>
458std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) 471static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input)
459{ 472{
460 std::string result; 473 std::string result;
461 474
462 iconv_t const conv_desc = iconv_open("UTF-8", fromcode); 475 iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
463 if ((iconv_t)-1 == conv_desc) 476 if ((iconv_t)(-1) == conv_desc)
464 { 477 {
465 ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); 478 LOG_ERROR(Common, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
479 iconv_close(conv_desc);
480 return {};
466 } 481 }
467 else
468 {
469 size_t const in_bytes = sizeof(T) * input.size();
470 size_t const out_buffer_size = 4 * in_bytes;
471 482
472 std::string out_buffer; 483 const size_t in_bytes = sizeof(T) * input.size();
473 out_buffer.resize(out_buffer_size); 484 // Multiply by 4, which is the max number of bytes to encode a codepoint
485 const size_t out_buffer_size = 4 * in_bytes;
474 486
475 auto src_buffer = &input[0]; 487 std::string out_buffer;
476 size_t src_bytes = in_bytes; 488 out_buffer.resize(out_buffer_size);
477 auto dst_buffer = &out_buffer[0];
478 size_t dst_bytes = out_buffer.size();
479 489
480 while (src_bytes != 0) 490 auto src_buffer = &input[0];
481 { 491 size_t src_bytes = in_bytes;
482 size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes, 492 auto dst_buffer = &out_buffer[0];
483 &dst_buffer, &dst_bytes); 493 size_t dst_bytes = out_buffer.size();
484 494
485 if ((size_t)-1 == iconv_result) 495 while (0 != src_bytes)
496 {
497 size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes,
498 &dst_buffer, &dst_bytes);
499
500 if (static_cast<size_t>(-1) == iconv_result)
501 {
502 if (EILSEQ == errno || EINVAL == errno)
486 { 503 {
487 if (EILSEQ == errno || EINVAL == errno) 504 // Try to skip the bad character
488 { 505 if (0 != src_bytes)
489 // Try to skip the bad character
490 if (src_bytes != 0)
491 {
492 --src_bytes;
493 ++src_buffer;
494 }
495 }
496 else
497 { 506 {
498 ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); 507 --src_bytes;
499 break; 508 ++src_buffer;
500 } 509 }
501 } 510 }
511 else
512 {
513 LOG_ERROR(Common, "iconv failure [%s]: %s", fromcode, strerror(errno));
514 break;
515 }
502 } 516 }
517 }
518
519 out_buffer.resize(out_buffer_size - dst_bytes);
520 out_buffer.swap(result);
521
522 iconv_close(conv_desc);
523
524 return result;
525}
526
527std::u16string UTF8ToUTF16(const std::string& input)
528{
529 std::u16string result;
503 530
504 out_buffer.resize(out_buffer_size - dst_bytes); 531 iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
505 out_buffer.swap(result); 532 if ((iconv_t)(-1) == conv_desc)
506 533 {
534 LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: %s", strerror(errno));
507 iconv_close(conv_desc); 535 iconv_close(conv_desc);
536 return {};
508 } 537 }
509 538
539 const size_t in_bytes = sizeof(char) * input.size();
540 // Multiply by 4, which is the max number of bytes to encode a codepoint
541 const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
542
543 std::u16string out_buffer;
544 out_buffer.resize(out_buffer_size);
545
546 char* src_buffer = const_cast<char*>(&input[0]);
547 size_t src_bytes = in_bytes;
548 char* dst_buffer = (char*)(&out_buffer[0]);
549 size_t dst_bytes = out_buffer.size();
550
551 while (0 != src_bytes)
552 {
553 size_t const iconv_result = iconv(conv_desc, &src_buffer, &src_bytes,
554 &dst_buffer, &dst_bytes);
555
556 if (static_cast<size_t>(-1) == iconv_result)
557 {
558 if (EILSEQ == errno || EINVAL == errno)
559 {
560 // Try to skip the bad character
561 if (0 != src_bytes)
562 {
563 --src_bytes;
564 ++src_buffer;
565 }
566 }
567 else
568 {
569 LOG_ERROR(Common, "iconv failure [UTF-8]: %s", strerror(errno));
570 break;
571 }
572 }
573 }
574
575 out_buffer.resize(out_buffer_size - dst_bytes);
576 out_buffer.swap(result);
577
578 iconv_close(conv_desc);
579
510 return result; 580 return result;
511} 581}
512 582
583std::string UTF16ToUTF8(const std::u16string& input)
584{
585 return CodeToUTF8("UTF-16LE", input);
586}
587
513std::string CP1252ToUTF8(const std::string& input) 588std::string CP1252ToUTF8(const std::string& input)
514{ 589{
515 //return CodeToUTF8("CP1252//TRANSLIT", input); 590 //return CodeToUTF8("CP1252//TRANSLIT", input);
@@ -523,19 +598,6 @@ std::string SHIFTJISToUTF8(const std::string& input)
523 return CodeToUTF8("SJIS", input); 598 return CodeToUTF8("SJIS", input);
524} 599}
525 600
526std::string UTF16ToUTF8(const std::wstring& input)
527{
528 std::string result =
529 // CodeToUTF8("UCS-2", input);
530 // CodeToUTF8("UCS-2LE", input);
531 // CodeToUTF8("UTF-16", input);
532 CodeToUTF8("UTF-16LE", input);
533
534 // TODO: why is this needed?
535 result.erase(std::remove(result.begin(), result.end(), 0x00), result.end());
536 return result;
537}
538
539#endif 601#endif
540 602
541} 603}
diff --git a/src/common/string_util.h b/src/common/string_util.h
index a41ccc691..7d75691b1 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -63,7 +63,7 @@ template <typename N>
63static bool TryParse(const std::string &str, N *const output) 63static bool TryParse(const std::string &str, N *const output)
64{ 64{
65 std::istringstream iss(str); 65 std::istringstream iss(str);
66 66
67 N tmp = 0; 67 N tmp = 0;
68 if (iss >> tmp) 68 if (iss >> tmp)
69 { 69 {
@@ -89,20 +89,22 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
89std::string UriDecode(const std::string & sSrc); 89std::string UriDecode(const std::string & sSrc);
90std::string UriEncode(const std::string & sSrc); 90std::string UriEncode(const std::string & sSrc);
91 91
92std::string UTF16ToUTF8(const std::u16string& input);
93std::u16string UTF8ToUTF16(const std::string& input);
94
92std::string CP1252ToUTF8(const std::string& str); 95std::string CP1252ToUTF8(const std::string& str);
93std::string SHIFTJISToUTF8(const std::string& str); 96std::string SHIFTJISToUTF8(const std::string& str);
94std::string UTF16ToUTF8(const std::wstring& str);
95 97
96#ifdef _WIN32 98#ifdef _WIN32
97 99
98std::wstring UTF8ToUTF16(const std::string& str); 100std::wstring UTF8ToUTF16W(const std::string& str);
99 101
100#ifdef _UNICODE 102#ifdef _UNICODE
101inline std::string TStrToUTF8(const std::wstring& str) 103inline std::string TStrToUTF8(const std::wstring& str)
102{ return UTF16ToUTF8(str); } 104{ return UTF16ToUTF8(str); }
103 105
104inline std::wstring UTF8ToTStr(const std::string& str) 106inline std::wstring UTF8ToTStr(const std::string& str)
105{ return UTF8ToUTF16(str); } 107{ return UTF8ToUTF16W(str); }
106#else 108#else
107inline std::string TStrToUTF8(const std::string& str) 109inline std::string TStrToUTF8(const std::string& str)
108{ return str; } 110{ return str; }
@@ -113,4 +115,19 @@ inline std::string UTF8ToTStr(const std::string& str)
113 115
114#endif 116#endif
115 117
118/**
119 * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
120 * `other` for equality.
121 */
122template <typename InIt>
123bool ComparePartialString(InIt begin, InIt end, const char* other) {
124 for (; begin != end && *other != '\0'; ++begin, ++other) {
125 if (*begin != *other) {
126 return false;
127 }
128 }
129 // Only return true if both strings finished at the same point
130 return (begin == end) == (*other == '\0');
131}
132
116} 133}
diff --git a/src/common/symbols.cpp b/src/common/symbols.cpp
index d61f4c0c6..63ad6218b 100644
--- a/src/common/symbols.cpp
+++ b/src/common/symbols.cpp
@@ -31,7 +31,7 @@ namespace Symbols
31 { 31 {
32 TSymbolsMap::iterator foundSymbolItr; 32 TSymbolsMap::iterator foundSymbolItr;
33 TSymbol symbol; 33 TSymbol symbol;
34 34
35 foundSymbolItr = g_symbols.find(_address); 35 foundSymbolItr = g_symbols.find(_address);
36 if (foundSymbolItr != g_symbols.end()) 36 if (foundSymbolItr != g_symbols.end())
37 { 37 {
@@ -44,7 +44,7 @@ namespace Symbols
44 { 44 {
45 return GetSymbol(_address).name; 45 return GetSymbol(_address).name;
46 } 46 }
47 47
48 void Remove(u32 _address) 48 void Remove(u32 _address)
49 { 49 {
50 g_symbols.erase(_address); 50 g_symbols.erase(_address);
diff --git a/src/common/symbols.h b/src/common/symbols.h
index b13a8001a..4560f5240 100644
--- a/src/common/symbols.h
+++ b/src/common/symbols.h
@@ -33,5 +33,5 @@ namespace Symbols
33 const std::string GetName(u32 _address); 33 const std::string GetName(u32 _address);
34 void Remove(u32 _address); 34 void Remove(u32 _address);
35 void Clear(); 35 void Clear();
36}; 36}
37 37
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 60d8ed075..dc153ba71 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -25,7 +25,7 @@ int CurrentThreadId()
25 return 0; 25 return 0;
26#endif 26#endif
27} 27}
28 28
29#ifdef _WIN32 29#ifdef _WIN32
30 30
31void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) 31void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
@@ -52,7 +52,7 @@ void SwitchCurrentThread()
52// Sets the debugger-visible name of the current thread. 52// Sets the debugger-visible name of the current thread.
53// Uses undocumented (actually, it is now documented) trick. 53// Uses undocumented (actually, it is now documented) trick.
54// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp 54// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
55 55
56// This is implemented much nicer in upcoming msvc++, see: 56// This is implemented much nicer in upcoming msvc++, see:
57// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx 57// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
58void SetCurrentThreadName(const char* szThreadName) 58void SetCurrentThreadName(const char* szThreadName)
@@ -81,7 +81,7 @@ void SetCurrentThreadName(const char* szThreadName)
81 __except(EXCEPTION_CONTINUE_EXECUTION) 81 __except(EXCEPTION_CONTINUE_EXECUTION)
82 {} 82 {}
83} 83}
84 84
85#else // !WIN32, so must be POSIX threads 85#else // !WIN32, so must be POSIX threads
86 86
87void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) 87void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
diff --git a/src/common/thread.h b/src/common/thread.h
index f7ace21b4..8c36d3f07 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -21,6 +21,7 @@
21//for gettimeofday and struct time(spec|val) 21//for gettimeofday and struct time(spec|val)
22#include <time.h> 22#include <time.h>
23#include <sys/time.h> 23#include <sys/time.h>
24#include <unistd.h>
24#endif 25#endif
25 26
26namespace Common 27namespace Common
@@ -30,13 +31,13 @@ int CurrentThreadId();
30 31
31void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); 32void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
32void SetCurrentThreadAffinity(u32 mask); 33void SetCurrentThreadAffinity(u32 mask);
33 34
34class Event 35class Event
35{ 36{
36public: 37public:
37 Event() 38 Event()
38 : is_set(false) 39 : is_set(false)
39 {}; 40 {}
40 41
41 void Set() 42 void Set()
42 { 43 {
@@ -135,7 +136,7 @@ private:
135 const size_t m_count; 136 const size_t m_count;
136 volatile size_t m_waiting; 137 volatile size_t m_waiting;
137}; 138};
138 139
139void SleepCurrentThread(int ms); 140void SleepCurrentThread(int ms);
140void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms 141void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
141 142
@@ -146,7 +147,7 @@ inline void YieldCPU()
146{ 147{
147 std::this_thread::yield(); 148 std::this_thread::yield();
148} 149}
149 150
150void SetCurrentThreadName(const char *name); 151void SetCurrentThreadName(const char *name);
151 152
152} // namespace Common 153} // namespace Common
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 4a89572f6..7e3b620c7 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
@@ -12,7 +12,7 @@ template<class IdType>
12struct ThreadQueueList { 12struct ThreadQueueList {
13 // Number of queues (number of priority levels starting at 0.) 13 // Number of queues (number of priority levels starting at 0.)
14 static const int NUM_QUEUES = 128; 14 static const int NUM_QUEUES = 128;
15 15
16 // Initial number of threads a single queue can handle. 16 // Initial number of threads a single queue can handle.
17 static const int INITIAL_CAPACITY = 32; 17 static const int INITIAL_CAPACITY = 32;
18 18
@@ -37,7 +37,7 @@ struct ThreadQueueList {
37 ~ThreadQueueList() { 37 ~ThreadQueueList() {
38 for (int i = 0; i < NUM_QUEUES; ++i) 38 for (int i = 0; i < NUM_QUEUES; ++i)
39 { 39 {
40 if (queues[i].data != NULL) 40 if (queues[i].data != nullptr)
41 free(queues[i].data); 41 free(queues[i].data);
42 } 42 }
43 } 43 }
@@ -46,7 +46,7 @@ struct ThreadQueueList {
46 int contains(const IdType uid) { 46 int contains(const IdType uid) {
47 for (int i = 0; i < NUM_QUEUES; ++i) 47 for (int i = 0; i < NUM_QUEUES; ++i)
48 { 48 {
49 if (queues[i].data == NULL) 49 if (queues[i].data == nullptr)
50 continue; 50 continue;
51 51
52 Queue *cur = &queues[i]; 52 Queue *cur = &queues[i];
@@ -133,7 +133,7 @@ struct ThreadQueueList {
133 inline void clear() { 133 inline void clear() {
134 for (int i = 0; i < NUM_QUEUES; ++i) 134 for (int i = 0; i < NUM_QUEUES; ++i)
135 { 135 {
136 if (queues[i].data != NULL) 136 if (queues[i].data != nullptr)
137 free(queues[i].data); 137 free(queues[i].data);
138 } 138 }
139 memset(queues, 0, sizeof(queues)); 139 memset(queues, 0, sizeof(queues));
@@ -147,7 +147,7 @@ struct ThreadQueueList {
147 147
148 inline void prepare(u32 priority) { 148 inline void prepare(u32 priority) {
149 Queue *cur = &queues[priority]; 149 Queue *cur = &queues[priority];
150 if (cur->next == NULL) 150 if (cur->next == nullptr)
151 link(priority, INITIAL_CAPACITY); 151 link(priority, INITIAL_CAPACITY);
152 } 152 }
153 153
@@ -176,7 +176,7 @@ private:
176 176
177 for (int i = (int) priority - 1; i >= 0; --i) 177 for (int i = (int) priority - 1; i >= 0; --i)
178 { 178 {
179 if (queues[i].next != NULL) 179 if (queues[i].next != nullptr)
180 { 180 {
181 cur->next = queues[i].next; 181 cur->next = queues[i].next;
182 queues[i].next = cur; 182 queues[i].next = cur;
@@ -193,7 +193,7 @@ private:
193 int size = cur->end - cur->first; 193 int size = cur->end - cur->first;
194 if (size >= cur->capacity - 2) { 194 if (size >= cur->capacity - 2) {
195 IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); 195 IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
196 if (new_data != NULL) { 196 if (new_data != nullptr) {
197 cur->capacity *= 2; 197 cur->capacity *= 2;
198 cur->data = new_data; 198 cur->data = new_data;
199 } 199 }
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index ded4a344e..4a797f751 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -25,7 +25,7 @@ u32 Timer::GetTimeMs()
25 return timeGetTime(); 25 return timeGetTime();
26#else 26#else
27 struct timeval t; 27 struct timeval t;
28 (void)gettimeofday(&t, NULL); 28 (void)gettimeofday(&t, nullptr);
29 return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); 29 return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000));
30#endif 30#endif
31} 31}
@@ -183,7 +183,7 @@ std::string Timer::GetTimeFormatted()
183 return StringFromFormat("%s:%03i", tmp, tp.millitm); 183 return StringFromFormat("%s:%03i", tmp, tp.millitm);
184#else 184#else
185 struct timeval t; 185 struct timeval t;
186 (void)gettimeofday(&t, NULL); 186 (void)gettimeofday(&t, nullptr);
187 return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000)); 187 return StringFromFormat("%s:%03d", tmp, (int)(t.tv_usec / 1000));
188#endif 188#endif
189} 189}
@@ -197,7 +197,7 @@ double Timer::GetDoubleTime()
197 (void)::ftime(&tp); 197 (void)::ftime(&tp);
198#else 198#else
199 struct timeval t; 199 struct timeval t;
200 (void)gettimeofday(&t, NULL); 200 (void)gettimeofday(&t, nullptr);
201#endif 201#endif
202 // Get continuous timestamp 202 // Get continuous timestamp
203 u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); 203 u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970();
diff --git a/src/common/utf8.cpp b/src/common/utf8.cpp
index be4ebc855..66a2f6339 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)
281 do { 281 do {
282 digs[dno++] = str[i++]; 282 digs[dno++] = str[i++];
283 } while (octal_digit(str[i]) && dno < 3); 283 } while (octal_digit(str[i]) && dno < 3);
284 ch = strtol(digs, NULL, 8); 284 ch = strtol(digs, nullptr, 8);
285 } 285 }
286 else if (str[0] == 'x') { 286 else if (str[0] == 'x') {
287 while (hex_digit(str[i]) && dno < 2) { 287 while (hex_digit(str[i]) && dno < 2) {
288 digs[dno++] = str[i++]; 288 digs[dno++] = str[i++];
289 } 289 }
290 if (dno > 0) 290 if (dno > 0)
291 ch = strtol(digs, NULL, 16); 291 ch = strtol(digs, nullptr, 16);
292 } 292 }
293 else if (str[0] == 'u') { 293 else if (str[0] == 'u') {
294 while (hex_digit(str[i]) && dno < 4) { 294 while (hex_digit(str[i]) && dno < 4) {
295 digs[dno++] = str[i++]; 295 digs[dno++] = str[i++];
296 } 296 }
297 if (dno > 0) 297 if (dno > 0)
298 ch = strtol(digs, NULL, 16); 298 ch = strtol(digs, nullptr, 16);
299 } 299 }
300 else if (str[0] == 'U') { 300 else if (str[0] == 'U') {
301 while (hex_digit(str[i]) && dno < 8) { 301 while (hex_digit(str[i]) && dno < 8) {
302 digs[dno++] = str[i++]; 302 digs[dno++] = str[i++];
303 } 303 }
304 if (dno > 0) 304 if (dno > 0)
305 ch = strtol(digs, NULL, 16); 305 ch = strtol(digs, nullptr, 16);
306 } 306 }
307 *dest = ch; 307 *dest = ch;
308 308
@@ -353,7 +353,7 @@ const char *u8_strchr(const char *s, u32 ch, int *charn)
353 lasti = i; 353 lasti = i;
354 (*charn)++; 354 (*charn)++;
355 } 355 }
356 return NULL; 356 return nullptr;
357} 357}
358 358
359const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) 359const 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)
378 lasti = i; 378 lasti = i;
379 (*charn)++; 379 (*charn)++;
380 } 380 }
381 return NULL; 381 return nullptr;
382} 382}
383 383
384int u8_is_locale_utf8(const char *locale) 384int u8_is_locale_utf8(const char *locale)
@@ -419,35 +419,35 @@ bool UTF8StringHasNonASCII(const char *utf8string) {
419 419
420std::string ConvertWStringToUTF8(const wchar_t *wstr) { 420std::string ConvertWStringToUTF8(const wchar_t *wstr) {
421 int len = (int)wcslen(wstr); 421 int len = (int)wcslen(wstr);
422 int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, NULL, NULL); 422 int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, nullptr, nullptr);
423 std::string s; 423 std::string s;
424 s.resize(size); 424 s.resize(size);
425 if (size > 0) { 425 if (size > 0) {
426 WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, NULL, NULL); 426 WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, nullptr, nullptr);
427 } 427 }
428 return s; 428 return s;
429} 429}
430 430
431std::string ConvertWStringToUTF8(const std::wstring &wstr) { 431std::string ConvertWStringToUTF8(const std::wstring &wstr) {
432 int len = (int)wstr.size(); 432 int len = (int)wstr.size();
433 int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, NULL, NULL); 433 int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, nullptr, nullptr);
434 std::string s; 434 std::string s;
435 s.resize(size); 435 s.resize(size);
436 if (size > 0) { 436 if (size > 0) {
437 WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, NULL, NULL); 437 WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, nullptr, nullptr);
438 } 438 }
439 return s; 439 return s;
440} 440}
441 441
442void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) { 442void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) {
443 int len = (int)source.size(); 443 int len = (int)source.size();
444 int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0); 444 int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0);
445 MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size)); 445 MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size));
446} 446}
447 447
448std::wstring ConvertUTF8ToWString(const std::string &source) { 448std::wstring ConvertUTF8ToWString(const std::string &source) {
449 int len = (int)source.size(); 449 int len = (int)source.size();
450 int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0); 450 int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0);
451 std::wstring str; 451 std::wstring str;
452 str.resize(size); 452 str.resize(size);
453 if (size > 0) { 453 if (size > 0) {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f67481359..f71232c1a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,29 +18,42 @@ set(SRCS
18 arm/skyeye_common/vfp/vfpinstr.cpp 18 arm/skyeye_common/vfp/vfpinstr.cpp
19 arm/skyeye_common/vfp/vfpsingle.cpp 19 arm/skyeye_common/vfp/vfpsingle.cpp
20 file_sys/archive_romfs.cpp 20 file_sys/archive_romfs.cpp
21 file_sys/archive_savedata.cpp
21 file_sys/archive_sdmc.cpp 22 file_sys/archive_sdmc.cpp
23 file_sys/disk_archive.cpp
22 file_sys/file_romfs.cpp 24 file_sys/file_romfs.cpp
23 file_sys/file_sdmc.cpp
24 file_sys/directory_romfs.cpp 25 file_sys/directory_romfs.cpp
25 file_sys/directory_sdmc.cpp
26 hle/kernel/address_arbiter.cpp 26 hle/kernel/address_arbiter.cpp
27 hle/kernel/archive.cpp
28 hle/kernel/event.cpp 27 hle/kernel/event.cpp
29 hle/kernel/kernel.cpp 28 hle/kernel/kernel.cpp
30 hle/kernel/mutex.cpp 29 hle/kernel/mutex.cpp
30 hle/kernel/semaphore.cpp
31 hle/kernel/shared_memory.cpp 31 hle/kernel/shared_memory.cpp
32 hle/kernel/thread.cpp 32 hle/kernel/thread.cpp
33 hle/service/ac_u.cpp 33 hle/service/ac_u.cpp
34 hle/service/am_app.cpp
35 hle/service/am_net.cpp
34 hle/service/apt_u.cpp 36 hle/service/apt_u.cpp
37 hle/service/boss_u.cpp
38 hle/service/cecd_u.cpp
39 hle/service/cfg_i.cpp
35 hle/service/cfg_u.cpp 40 hle/service/cfg_u.cpp
41 hle/service/csnd_snd.cpp
36 hle/service/dsp_dsp.cpp 42 hle/service/dsp_dsp.cpp
37 hle/service/err_f.cpp 43 hle/service/err_f.cpp
38 hle/service/fs_user.cpp 44 hle/service/fs/archive.cpp
45 hle/service/fs/fs_user.cpp
46 hle/service/frd_u.cpp
39 hle/service/gsp_gpu.cpp 47 hle/service/gsp_gpu.cpp
40 hle/service/hid_user.cpp 48 hle/service/hid_user.cpp
49 hle/service/ir_rst.cpp
50 hle/service/ir_u.cpp
51 hle/service/ldr_ro.cpp
41 hle/service/mic_u.cpp 52 hle/service/mic_u.cpp
53 hle/service/nim_aoc.cpp
42 hle/service/ndm_u.cpp 54 hle/service/ndm_u.cpp
43 hle/service/nwm_uds.cpp 55 hle/service/nwm_uds.cpp
56 hle/service/pm_app.cpp
44 hle/service/ptm_u.cpp 57 hle/service/ptm_u.cpp
45 hle/service/service.cpp 58 hle/service/service.cpp
46 hle/service/soc_u.cpp 59 hle/service/soc_u.cpp
@@ -51,10 +64,10 @@ set(SRCS
51 hle/svc.cpp 64 hle/svc.cpp
52 hw/gpu.cpp 65 hw/gpu.cpp
53 hw/hw.cpp 66 hw/hw.cpp
54 hw/ndma.cpp
55 loader/elf.cpp 67 loader/elf.cpp
56 loader/loader.cpp 68 loader/loader.cpp
57 loader/ncch.cpp 69 loader/ncch.cpp
70 loader/3dsx.cpp
58 core.cpp 71 core.cpp
59 core_timing.cpp 72 core_timing.cpp
60 mem_map.cpp 73 mem_map.cpp
@@ -84,48 +97,63 @@ set(HEADERS
84 arm/skyeye_common/vfp/vfp.h 97 arm/skyeye_common/vfp/vfp.h
85 arm/skyeye_common/vfp/vfp_helper.h 98 arm/skyeye_common/vfp/vfp_helper.h
86 arm/arm_interface.h 99 arm/arm_interface.h
87 file_sys/archive.h 100 file_sys/archive_backend.h
88 file_sys/archive_romfs.h 101 file_sys/archive_romfs.h
102 file_sys/archive_savedata.h
89 file_sys/archive_sdmc.h 103 file_sys/archive_sdmc.h
90 file_sys/file.h 104 file_sys/disk_archive.h
105 file_sys/file_backend.h
91 file_sys/file_romfs.h 106 file_sys/file_romfs.h
92 file_sys/file_sdmc.h 107 file_sys/directory_backend.h
93 file_sys/directory.h
94 file_sys/directory_romfs.h 108 file_sys/directory_romfs.h
95 file_sys/directory_sdmc.h
96 hle/kernel/address_arbiter.h 109 hle/kernel/address_arbiter.h
97 hle/kernel/archive.h
98 hle/kernel/event.h 110 hle/kernel/event.h
99 hle/kernel/kernel.h 111 hle/kernel/kernel.h
100 hle/kernel/mutex.h 112 hle/kernel/mutex.h
113 hle/kernel/semaphore.h
114 hle/kernel/session.h
101 hle/kernel/shared_memory.h 115 hle/kernel/shared_memory.h
102 hle/kernel/thread.h 116 hle/kernel/thread.h
103 hle/service/ac_u.h 117 hle/service/ac_u.h
118 hle/service/am_app.h
119 hle/service/am_net.h
104 hle/service/apt_u.h 120 hle/service/apt_u.h
121 hle/service/boss_u.h
122 hle/service/cecd_u.h
123 hle/service/cfg_i.h
105 hle/service/cfg_u.h 124 hle/service/cfg_u.h
125 hle/service/csnd_snd.h
106 hle/service/dsp_dsp.h 126 hle/service/dsp_dsp.h
107 hle/service/err_f.h 127 hle/service/err_f.h
108 hle/service/fs_user.h 128 hle/service/fs/archive.h
129 hle/service/fs/fs_user.h
130 hle/service/frd_u.h
109 hle/service/gsp_gpu.h 131 hle/service/gsp_gpu.h
110 hle/service/hid_user.h 132 hle/service/hid_user.h
133 hle/service/ir_rst.h
134 hle/service/ir_u.h
135 hle/service/ldr_ro.h
111 hle/service/mic_u.h 136 hle/service/mic_u.h
137 hle/service/nim_aoc.h
112 hle/service/ndm_u.h 138 hle/service/ndm_u.h
113 hle/service/nwm_uds.h 139 hle/service/nwm_uds.h
140 hle/service/pm_app.h
114 hle/service/ptm_u.h 141 hle/service/ptm_u.h
115 hle/service/service.h 142 hle/service/service.h
116 hle/service/soc_u.h 143 hle/service/soc_u.h
117 hle/service/srv.h 144 hle/service/srv.h
118 hle/service/ssl_c.h 145 hle/service/ssl_c.h
119 hle/config_mem.h 146 hle/config_mem.h
147 hle/result.h
120 hle/function_wrappers.h 148 hle/function_wrappers.h
121 hle/hle.h 149 hle/hle.h
122 hle/svc.h 150 hle/svc.h
123 hw/gpu.h 151 hw/gpu.h
124 hw/hw.h 152 hw/hw.h
125 hw/ndma.h
126 loader/elf.h 153 loader/elf.h
127 loader/loader.h 154 loader/loader.h
128 loader/ncch.h 155 loader/ncch.h
156 loader/3dsx.h
129 core.h 157 core.h
130 core_timing.h 158 core_timing.h
131 mem_map.h 159 mem_map.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index be677ae20..3ae528562 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
@@ -16,7 +16,7 @@ public:
16 num_instructions = 0; 16 num_instructions = 0;
17 } 17 }
18 18
19 ~ARM_Interface() { 19 virtual ~ARM_Interface() {
20 } 20 }
21 21
22 /** 22 /**
@@ -63,7 +63,7 @@ public:
63 * Get the current CPSR register 63 * Get the current CPSR register
64 * @return Returns the value of the CPSR register 64 * @return Returns the value of the CPSR register
65 */ 65 */
66 virtual u32 GetCPSR() const = 0; 66 virtual u32 GetCPSR() const = 0;
67 67
68 /** 68 /**
69 * Set the current CPSR register 69 * Set the current CPSR register
@@ -98,7 +98,7 @@ public:
98 } 98 }
99 99
100protected: 100protected:
101 101
102 /** 102 /**
103 * Executes the given number of instructions 103 * Executes the given number of instructions
104 * @param num_instructions Number of instructions to executes 104 * @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 0f384ad3e..55278474b 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) {
22 22
23 while (std::getline(infile, line)) { 23 while (std::getline(infile, line)) {
24 std::istringstream iss(line); 24 std::istringstream iss(line);
25 if (!(iss >> address_str >> size >> function_name)) { 25 if (!(iss >> address_str >> size >> function_name)) {
26 break; // Error parsing 26 break; // Error parsing
27 } 27 }
28 u32 address = std::stoul(address_str, nullptr, 16); 28 u32 address = std::stoul(address_str, nullptr, 16);
29 29
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index 669b612fc..6c8ea211e 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/arm/skyeye_common/armcpu.h" 5#include "core/arm/skyeye_common/armcpu.h"
6#include "core/arm/skyeye_common/armemu.h" 6#include "core/arm/skyeye_common/armemu.h"
@@ -60,7 +60,7 @@ void ARM_DynCom::SetPC(u32 pc) {
60 * @return Returns current PC 60 * @return Returns current PC
61 */ 61 */
62u32 ARM_DynCom::GetPC() const { 62u32 ARM_DynCom::GetPC() const {
63 return state->pc; 63 return state->Reg[15];
64} 64}
65 65
66/** 66/**
@@ -110,9 +110,12 @@ u64 ARM_DynCom::GetTicks() const {
110 * @param num_instructions Number of instructions to executes 110 * @param num_instructions Number of instructions to executes
111 */ 111 */
112void ARM_DynCom::ExecuteInstructions(int num_instructions) { 112void ARM_DynCom::ExecuteInstructions(int num_instructions) {
113 ticks += num_instructions;
114 state->NumInstrsToExecute = num_instructions; 113 state->NumInstrsToExecute = num_instructions;
115 InterpreterMainLoop(state.get()); 114
115 // Dyncom only breaks on instruction dispatch. This only happens on every instruction when
116 // executing one instruction at a time. Otherwise, if a block is being executed, more
117 // instructions may actually be executed than specified.
118 ticks += InterpreterMainLoop(state.get());
116} 119}
117 120
118/** 121/**
@@ -126,7 +129,7 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) {
126 129
127 ctx.sp = state->Reg[13]; 130 ctx.sp = state->Reg[13];
128 ctx.lr = state->Reg[14]; 131 ctx.lr = state->Reg[14];
129 ctx.pc = state->pc; 132 ctx.pc = state->Reg[15];
130 ctx.cpsr = state->Cpsr; 133 ctx.cpsr = state->Cpsr;
131 134
132 ctx.fpscr = state->VFP[1]; 135 ctx.fpscr = state->VFP[1];
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 1f8cd3a3a..51eea41ed 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
@@ -19,7 +19,7 @@ public:
19 19
20 /** 20 /**
21 * Set the Program Counter to an address 21 * Set the Program Counter to an address
22 * @param addr Address to set PC to 22 * @param pc Address to set PC to
23 */ 23 */
24 void SetPC(u32 pc) override; 24 void SetPC(u32 pc) override;
25 25
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index fe1501b59..68012bffd 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -26,7 +26,7 @@
26#define CITRA_IGNORE_EXIT(x) 26#define CITRA_IGNORE_EXIT(x)
27 27
28#include <algorithm> 28#include <algorithm>
29#include <map> 29#include <unordered_map>
30#include <stdio.h> 30#include <stdio.h>
31#include <assert.h> 31#include <assert.h>
32#include <cstdio> 32#include <cstdio>
@@ -94,9 +94,8 @@ typedef unsigned int (*shtop_fp_t)(arm_processor *cpu, unsigned int sht_oper);
94 94
95/* exclusive memory access */ 95/* exclusive memory access */
96static int exclusive_detect(ARMul_State* state, ARMword addr){ 96static int exclusive_detect(ARMul_State* state, ARMword addr){
97 int i;
98 #if 0 97 #if 0
99 for(i = 0; i < 128; i++){ 98 for(int i = 0; i < 128; i++){
100 if(state->exclusive_tag_array[i] == addr) 99 if(state->exclusive_tag_array[i] == addr)
101 return 0; 100 return 0;
102 } 101 }
@@ -108,9 +107,8 @@ static int exclusive_detect(ARMul_State* state, ARMword addr){
108} 107}
109 108
110static void add_exclusive_addr(ARMul_State* state, ARMword addr){ 109static void add_exclusive_addr(ARMul_State* state, ARMword addr){
111 int i;
112 #if 0 110 #if 0
113 for(i = 0; i < 128; i++){ 111 for(int i = 0; i < 128; i++){
114 if(state->exclusive_tag_array[i] == 0xffffffff){ 112 if(state->exclusive_tag_array[i] == 0xffffffff){
115 state->exclusive_tag_array[i] = addr; 113 state->exclusive_tag_array[i] = addr;
116 //DEBUG_LOG(ARM11, "In %s, add addr 0x%x\n", __func__, addr); 114 //DEBUG_LOG(ARM11, "In %s, add addr 0x%x\n", __func__, addr);
@@ -435,9 +433,7 @@ typedef struct _ldst_inst {
435 unsigned int inst; 433 unsigned int inst;
436 get_addr_fp_t get_addr; 434 get_addr_fp_t get_addr;
437} ldst_inst; 435} ldst_inst;
438#define DEBUG_MSG DEBUG_LOG(ARM11, "in %s %d\n", __FUNCTION__, __LINE__); \ 436#define DEBUG_MSG LOG_DEBUG(Core_ARM11, "inst is %x", inst); CITRA_IGNORE_EXIT(0)
439 DEBUG_LOG(ARM11, "inst is %x\n", inst); \
440 CITRA_IGNORE_EXIT(0)
441 437
442int CondPassed(arm_processor *cpu, unsigned int cond); 438int CondPassed(arm_processor *cpu, unsigned int cond);
443#define LnSWoUB(s) glue(LnSWoUB, s) 439#define LnSWoUB(s) glue(LnSWoUB, s)
@@ -1425,7 +1421,7 @@ inline void *AllocBuffer(unsigned int size)
1425 int start = top; 1421 int start = top;
1426 top += size; 1422 top += size;
1427 if (top > CACHE_BUFFER_SIZE) { 1423 if (top > CACHE_BUFFER_SIZE) {
1428 DEBUG_LOG(ARM11, "inst_buf is full\n"); 1424 LOG_ERROR(Core_ARM11, "inst_buf is full");
1429 CITRA_IGNORE_EXIT(-1); 1425 CITRA_IGNORE_EXIT(-1);
1430 } 1426 }
1431 return (void *)&inst_buf[start]; 1427 return (void *)&inst_buf[start];
@@ -1611,6 +1607,10 @@ get_addr_fp_t get_calc_addr_op(unsigned int inst)
1611#define CHECK_RM (inst_cream->Rm == 15) 1607#define CHECK_RM (inst_cream->Rm == 15)
1612#define CHECK_RS (inst_cream->Rs == 15) 1608#define CHECK_RS (inst_cream->Rs == 15)
1613 1609
1610#define UNIMPLEMENTED_INSTRUCTION(mnemonic) \
1611 LOG_ERROR(Core_ARM11, "unimplemented instruction: %s", mnemonic); \
1612 CITRA_IGNORE_EXIT(-1); \
1613 return nullptr;
1614 1614
1615ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index) 1615ARM_INST_PTR INTERPRETER_TRANSLATE(adc)(unsigned int inst, int index)
1616{ 1616{
@@ -1725,7 +1725,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(bic)(unsigned int inst, int index)
1725 inst_base->br = INDIRECT_BRANCH; 1725 inst_base->br = INDIRECT_BRANCH;
1726 return inst_base; 1726 return inst_base;
1727} 1727}
1728ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 1728ARM_INST_PTR INTERPRETER_TRANSLATE(bkpt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BKPT"); }
1729ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index) 1729ARM_INST_PTR INTERPRETER_TRANSLATE(blx)(unsigned int inst, int index)
1730{ 1730{
1731 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(blx_inst)); 1731 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)
1760 1760
1761 return inst_base; 1761 return inst_base;
1762} 1762}
1763ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 1763ARM_INST_PTR INTERPRETER_TRANSLATE(bxj)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("BXJ"); }
1764ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){ 1764ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){
1765 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst)); 1765 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(cdp_inst));
1766 cdp_inst *inst_cream = (cdp_inst *)inst_base->component; 1766 cdp_inst *inst_cream = (cdp_inst *)inst_base->component;
@@ -1777,7 +1777,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(cdp)(unsigned int inst, int index){
1777 inst_cream->opcode_1 = BITS(inst, 20, 23); 1777 inst_cream->opcode_1 = BITS(inst, 20, 23);
1778 inst_cream->inst = inst; 1778 inst_cream->inst = inst;
1779 1779
1780 DEBUG_LOG(ARM11, "in func %s inst %x index %x\n", __FUNCTION__, inst, index); 1780 LOG_TRACE(Core_ARM11, "inst %x index %x", inst, index);
1781 return inst_base; 1781 return inst_base;
1782} 1782}
1783ARM_INST_PTR INTERPRETER_TRANSLATE(clrex)(unsigned int inst, int index) 1783ARM_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)
2207 inst_cream->inst = inst; 2207 inst_cream->inst = inst;
2208 return inst_base; 2208 return inst_base;
2209} 2209}
2210ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2210ARM_INST_PTR INTERPRETER_TRANSLATE(mcrr)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MCRR"); }
2211ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index) 2211ARM_INST_PTR INTERPRETER_TRANSLATE(mla)(unsigned int inst, int index)
2212{ 2212{
2213 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mla_inst)); 2213 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)
2266 inst_cream->inst = inst; 2266 inst_cream->inst = inst;
2267 return inst_base; 2267 return inst_base;
2268} 2268}
2269ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2269ARM_INST_PTR INTERPRETER_TRANSLATE(mrrc)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("MRRC"); }
2270ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index) 2270ARM_INST_PTR INTERPRETER_TRANSLATE(mrs)(unsigned int inst, int index)
2271{ 2271{
2272 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(mrs_inst)); 2272 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)
2360 } 2360 }
2361 return inst_base; 2361 return inst_base;
2362} 2362}
2363ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2363ARM_INST_PTR INTERPRETER_TRANSLATE(pkhbt)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHBT"); }
2364ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2364ARM_INST_PTR INTERPRETER_TRANSLATE(pkhtb)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("PKHTB"); }
2365ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index) 2365ARM_INST_PTR INTERPRETER_TRANSLATE(pld)(unsigned int inst, int index)
2366{ 2366{
2367 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(pld_inst)); 2367 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)
2373 2373
2374 return inst_base; 2374 return inst_base;
2375} 2375}
2376ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2376ARM_INST_PTR INTERPRETER_TRANSLATE(qadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD"); }
2377ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2377ARM_INST_PTR INTERPRETER_TRANSLATE(qadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD16"); }
2378ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2378ARM_INST_PTR INTERPRETER_TRANSLATE(qadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADD8"); }
2379ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2379ARM_INST_PTR INTERPRETER_TRANSLATE(qaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QADDSUBX"); }
2380ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2380ARM_INST_PTR INTERPRETER_TRANSLATE(qdadd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDADD"); }
2381ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2381ARM_INST_PTR INTERPRETER_TRANSLATE(qdsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QDSUB"); }
2382ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2382ARM_INST_PTR INTERPRETER_TRANSLATE(qsub)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB"); }
2383ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2383ARM_INST_PTR INTERPRETER_TRANSLATE(qsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB16"); }
2384ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2384ARM_INST_PTR INTERPRETER_TRANSLATE(qsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUB8"); }
2385ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2385ARM_INST_PTR INTERPRETER_TRANSLATE(qsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("QSUBADDX"); }
2386ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index) 2386ARM_INST_PTR INTERPRETER_TRANSLATE(rev)(unsigned int inst, int index)
2387{ 2387{
2388 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rev_inst)); 2388 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){
2412 2412
2413 return inst_base; 2413 return inst_base;
2414} 2414}
2415ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2415ARM_INST_PTR INTERPRETER_TRANSLATE(revsh)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("REVSH"); }
2416ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2416ARM_INST_PTR INTERPRETER_TRANSLATE(rfe)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("RFE"); }
2417ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index) 2417ARM_INST_PTR INTERPRETER_TRANSLATE(rsb)(unsigned int inst, int index)
2418{ 2418{
2419 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(rsb_inst)); 2419 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)
2462 } 2462 }
2463 return inst_base; 2463 return inst_base;
2464} 2464}
2465ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2465ARM_INST_PTR INTERPRETER_TRANSLATE(sadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD16"); }
2466ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2466ARM_INST_PTR INTERPRETER_TRANSLATE(sadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADD8"); }
2467ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2467ARM_INST_PTR INTERPRETER_TRANSLATE(saddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SADDSUBX"); }
2468ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index) 2468ARM_INST_PTR INTERPRETER_TRANSLATE(sbc)(unsigned int inst, int index)
2469{ 2469{
2470 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sbc_inst)); 2470 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)
2489 } 2489 }
2490 return inst_base; 2490 return inst_base;
2491} 2491}
2492ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2492ARM_INST_PTR INTERPRETER_TRANSLATE(sel)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SEL"); }
2493ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2493ARM_INST_PTR INTERPRETER_TRANSLATE(setend)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SETEND"); }
2494ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2494ARM_INST_PTR INTERPRETER_TRANSLATE(shadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD16"); }
2495ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2495ARM_INST_PTR INTERPRETER_TRANSLATE(shadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADD8"); }
2496ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2496ARM_INST_PTR INTERPRETER_TRANSLATE(shaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHADDSUBX"); }
2497ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2497ARM_INST_PTR INTERPRETER_TRANSLATE(shsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB16"); }
2498ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2498ARM_INST_PTR INTERPRETER_TRANSLATE(shsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUB8"); }
2499ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2499ARM_INST_PTR INTERPRETER_TRANSLATE(shsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SHSUBADDX"); }
2500ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index) 2500ARM_INST_PTR INTERPRETER_TRANSLATE(smla)(unsigned int inst, int index)
2501{ 2501{
2502 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smla_inst)); 2502 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)
2555 inst_base->load_r15 = 1; 2555 inst_base->load_r15 = 1;
2556 return inst_base; 2556 return inst_base;
2557} 2557}
2558ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2558ARM_INST_PTR INTERPRETER_TRANSLATE(smlalxy)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALXY"); }
2559ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2559ARM_INST_PTR INTERPRETER_TRANSLATE(smlald)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLALD"); }
2560ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2560ARM_INST_PTR INTERPRETER_TRANSLATE(smlaw)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLAW"); }
2561ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2561ARM_INST_PTR INTERPRETER_TRANSLATE(smlsd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSD"); }
2562ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2562ARM_INST_PTR INTERPRETER_TRANSLATE(smlsld)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMLSLD"); }
2563ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2563ARM_INST_PTR INTERPRETER_TRANSLATE(smmla)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLA"); }
2564ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2564ARM_INST_PTR INTERPRETER_TRANSLATE(smmls)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMLS"); }
2565ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2565ARM_INST_PTR INTERPRETER_TRANSLATE(smmul)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMMUL"); }
2566ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2566ARM_INST_PTR INTERPRETER_TRANSLATE(smuad)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUAD"); }
2567ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index) 2567ARM_INST_PTR INTERPRETER_TRANSLATE(smul)(unsigned int inst, int index)
2568{ 2568{
2569 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(smul_inst)); 2569 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)
2626 inst_base->load_r15 = 1; 2626 inst_base->load_r15 = 1;
2627 return inst_base; 2627 return inst_base;
2628} 2628}
2629ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2629ARM_INST_PTR INTERPRETER_TRANSLATE(smusd)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SMUSD"); }
2630ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2630ARM_INST_PTR INTERPRETER_TRANSLATE(srs)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SRS"); }
2631ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2631ARM_INST_PTR INTERPRETER_TRANSLATE(ssat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT"); }
2632ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2632ARM_INST_PTR INTERPRETER_TRANSLATE(ssat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSAT16"); }
2633ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2633ARM_INST_PTR INTERPRETER_TRANSLATE(ssub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB16"); }
2634ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2634ARM_INST_PTR INTERPRETER_TRANSLATE(ssub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUB8"); }
2635ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2635ARM_INST_PTR INTERPRETER_TRANSLATE(ssubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SSUBADDX"); }
2636ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index) 2636ARM_INST_PTR INTERPRETER_TRANSLATE(stc)(unsigned int inst, int index)
2637{ 2637{
2638 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(stc_inst)); 2638 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){
2939 2939
2940 return inst_base; 2940 return inst_base;
2941} 2941}
2942ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2942ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTAB16"); }
2943ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){ 2943ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){
2944 DEBUG_LOG(ARM11, "in func %s, SXTAH untested\n", __func__); 2944 LOG_WARNING(Core_ARM11, "SXTAH untested");
2945 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst)); 2945 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(sxtah_inst));
2946 sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component; 2946 sxtah_inst *inst_cream = (sxtah_inst *)inst_base->component;
2947 2947
@@ -2957,7 +2957,7 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(sxtah)(unsigned int inst, int index){
2957 2957
2958 return inst_base; 2958 return inst_base;
2959} 2959}
2960ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 2960ARM_INST_PTR INTERPRETER_TRANSLATE(sxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("SXTB16"); }
2961ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index) 2961ARM_INST_PTR INTERPRETER_TRANSLATE(teq)(unsigned int inst, int index)
2962{ 2962{
2963 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(teq_inst)); 2963 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)
3001 inst_base->load_r15 = 1; 3001 inst_base->load_r15 = 1;
3002 return inst_base; 3002 return inst_base;
3003} 3003}
3004ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3004ARM_INST_PTR INTERPRETER_TRANSLATE(uadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD16"); }
3005ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3005ARM_INST_PTR INTERPRETER_TRANSLATE(uadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADD8"); }
3006ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3006ARM_INST_PTR INTERPRETER_TRANSLATE(uaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UADDSUBX"); }
3007ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3007ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD16"); }
3008ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3008ARM_INST_PTR INTERPRETER_TRANSLATE(uhadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADD8"); }
3009ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3009ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHADDSUBX"); }
3010ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3010ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); }
3011ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3011ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); }
3012ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3012ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); }
3013ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3013ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UMAAL"); }
3014ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) 3014ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index)
3015{ 3015{
3016 arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); 3016 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)
3113 return inst_base; 3113 return inst_base;
3114} 3114}
3115 3115
3116ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3116ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD16"); }
3117ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3117ARM_INST_PTR INTERPRETER_TRANSLATE(uqadd8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADD8"); }
3118ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3118ARM_INST_PTR INTERPRETER_TRANSLATE(uqaddsubx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQADDSUBX"); }
3119ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3119ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB16"); }
3120ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3120ARM_INST_PTR INTERPRETER_TRANSLATE(uqsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUB8"); }
3121ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3121ARM_INST_PTR INTERPRETER_TRANSLATE(uqsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UQSUBADDX"); }
3122ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3122ARM_INST_PTR INTERPRETER_TRANSLATE(usad8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAD8"); }
3123ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3123ARM_INST_PTR INTERPRETER_TRANSLATE(usada8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USADA8"); }
3124ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3124ARM_INST_PTR INTERPRETER_TRANSLATE(usat)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT"); }
3125ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3125ARM_INST_PTR INTERPRETER_TRANSLATE(usat16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USAT16"); }
3126ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3126ARM_INST_PTR INTERPRETER_TRANSLATE(usub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB16"); }
3127ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3127ARM_INST_PTR INTERPRETER_TRANSLATE(usub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUB8"); }
3128ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3128ARM_INST_PTR INTERPRETER_TRANSLATE(usubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("USUBADDX"); }
3129ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3129ARM_INST_PTR INTERPRETER_TRANSLATE(uxtab16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTAB16"); }
3130ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index){DEBUG_LOG(ARM11, "in func %s\n", __FUNCTION__);CITRA_IGNORE_EXIT(-1); return nullptr;} 3130ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UXTB16"); }
3131 3131
3132 3132
3133 3133
@@ -3309,9 +3309,8 @@ const transop_fp_t arm_instruction_trans[] = {
3309 INTERPRETER_TRANSLATE(blx_1_thumb) 3309 INTERPRETER_TRANSLATE(blx_1_thumb)
3310}; 3310};
3311 3311
3312typedef map<unsigned int, int> bb_map; 3312typedef std::unordered_map<u32, int> bb_map;
3313bb_map CreamCache[65536]; 3313bb_map CreamCache;
3314bb_map ProfileCache[65536];
3315 3314
3316//#define USE_DUMMY_CACHE 3315//#define USE_DUMMY_CACHE
3317 3316
@@ -3319,14 +3318,12 @@ bb_map ProfileCache[65536];
3319unsigned int DummyCache[0x100000]; 3318unsigned int DummyCache[0x100000];
3320#endif 3319#endif
3321 3320
3322#define HASH(x) ((x + (x << 3) + (x >> 6)) % 65536)
3323void insert_bb(unsigned int addr, int start) 3321void insert_bb(unsigned int addr, int start)
3324{ 3322{
3325#ifdef USE_DUMMY_CACHE 3323#ifdef USE_DUMMY_CACHE
3326 DummyCache[addr] = start; 3324 DummyCache[addr] = start;
3327#else 3325#else
3328// CreamCache[addr] = start; 3326 CreamCache[addr] = start;
3329 CreamCache[HASH(addr)][addr] = start;
3330#endif 3327#endif
3331} 3328}
3332 3329
@@ -3341,8 +3338,8 @@ int find_bb(unsigned int addr, int &start)
3341 } else 3338 } else
3342 ret = -1; 3339 ret = -1;
3343#else 3340#else
3344 bb_map::const_iterator it = CreamCache[HASH(addr)].find(addr); 3341 bb_map::const_iterator it = CreamCache.find(addr);
3345 if (it != CreamCache[HASH(addr)].end()) { 3342 if (it != CreamCache.end()) {
3346 start = static_cast<int>(it->second); 3343 start = static_cast<int>(it->second);
3347 ret = 0; 3344 ret = 0;
3348#if HYBRID_MODE 3345#if HYBRID_MODE
@@ -3396,7 +3393,7 @@ static tdstate decode_thumb_instr(arm_processor *cpu, uint32_t inst, addr_t addr
3396 } 3393 }
3397 else{ 3394 else{
3398 /* something wrong */ 3395 /* something wrong */
3399 DEBUG_LOG(ARM11, "In %s, thumb decoder error\n", __FUNCTION__); 3396 LOG_ERROR(Core_ARM11, "thumb decoder error");
3400 } 3397 }
3401 break; 3398 break;
3402 case 28: 3399 case 28:
@@ -3473,30 +3470,15 @@ void flush_bb(uint32_t addr)
3473 uint32_t start; 3470 uint32_t start;
3474 3471
3475 addr &= 0xfffff000; 3472 addr &= 0xfffff000;
3476 for (int i = 0; i < 65536; i ++) { 3473 for (it = CreamCache.begin(); it != CreamCache.end(); ) {
3477 for (it = CreamCache[i].begin(); it != CreamCache[i].end(); ) { 3474 start = static_cast<uint32_t>(it->first);
3478 start = static_cast<uint32_t>(it->first); 3475 //start = (start >> 12) << 12;
3479 //start = (start >> 12) << 12; 3476 start &= 0xfffff000;
3480 start &= 0xfffff000; 3477 if (start == addr) {
3481 if (start == addr) { 3478 //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
3482 //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first)); 3479 CreamCache.erase(it++);
3483 CreamCache[i].erase(it ++); 3480 } else
3484 } else 3481 ++it;
3485 ++it;
3486 }
3487 }
3488
3489 for (int i = 0; i < 65536; i ++) {
3490 for (it = ProfileCache[i].begin(); it != ProfileCache[i].end(); ) {
3491 start = static_cast<uint32_t>(it->first);
3492 //start = (start >> 12) << 12;
3493 start &= 0xfffff000;
3494 if (start == addr) {
3495 //DEBUG_LOG(ARM11, "[ERASE][0x%08x]\n", static_cast<int>(it->first));
3496 ProfileCache[i].erase(it ++);
3497 } else
3498 ++it;
3499 }
3500 } 3482 }
3501 3483
3502 //DEBUG_LOG(ARM11, "flush bb @ %x\n", addr); 3484 //DEBUG_LOG(ARM11, "flush bb @ %x\n", addr);
@@ -3619,7 +3601,7 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr)
3619 bank->bank_read(32, phys_addr, &inst); 3601 bank->bank_read(32, phys_addr, &inst);
3620 } 3602 }
3621 else { 3603 else {
3622 DEBUG_LOG(ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr); 3604 LOG_ERROR(Core_ARM11, "SKYEYE: Read physical addr 0x%x error!!\n", phys_addr);
3623 return FETCH_FAILURE; 3605 return FETCH_FAILURE;
3624 } 3606 }
3625#else 3607#else
@@ -3649,8 +3631,8 @@ int InterpreterTranslate(arm_processor *cpu, int &bb_start, addr_t addr)
3649 3631
3650 ret = decode_arm_instr(inst, &idx); 3632 ret = decode_arm_instr(inst, &idx);
3651 if (ret == DECODE_FAILURE) { 3633 if (ret == DECODE_FAILURE) {
3652 DEBUG_LOG(ARM11, "[info] : Decode failure.\tPC : [0x%x]\tInstruction : [%x]\n", phys_addr, inst); 3634 LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : [%x]", phys_addr, inst);
3653 DEBUG_LOG(ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x\n", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]); 3635 LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, cpu->Reg[15]);
3654 CITRA_IGNORE_EXIT(-1); 3636 CITRA_IGNORE_EXIT(-1);
3655 } 3637 }
3656// DEBUG_LOG(ARM11, "PC : [0x%x] INST : %s\n", cpu->translate_pc, arm_instruction[idx].name); 3638// 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)
3694 } 3676 }
3695 } 3677 }
3696 for (int i = 0; i < array_size - 4; i ++) 3678 for (int i = 0; i < array_size - 4; i ++)
3697 DEBUG_LOG(ARM11, "[%d]:%d\n", i, InstLength[i]); 3679 LOG_DEBUG(Core_ARM11, "[%d]:%d", i, InstLength[i]);
3698} 3680}
3699 3681
3700int clz(unsigned int x) 3682int clz(unsigned int x)
@@ -3718,7 +3700,7 @@ static bool InAPrivilegedMode(arm_core_t *core)
3718} 3700}
3719 3701
3720/* r15 = r15 + 8 */ 3702/* r15 = r15 + 8 */
3721void InterpreterMainLoop(ARMul_State* state) 3703unsigned InterpreterMainLoop(ARMul_State* state)
3722{ 3704{
3723 #define CRn inst_cream->crn 3705 #define CRn inst_cream->crn
3724 #define OPCODE_2 inst_cream->opcode_2 3706 #define OPCODE_2 inst_cream->opcode_2
@@ -3741,22 +3723,28 @@ void InterpreterMainLoop(ARMul_State* state)
3741 //if (debug_function(core)) \ 3723 //if (debug_function(core)) \
3742 if (core->check_int_flag) \ 3724 if (core->check_int_flag) \
3743 goto END 3725 goto END
3744 //DEBUG_LOG(ARM11, "icounter is %llx line is %d pc is %x\n", cpu->icounter, __LINE__, cpu->Reg[15]) 3726 //LOG_TRACE(Core_ARM11, "icounter is %llx pc is %x\n", cpu->icounter, cpu->Reg[15])
3745 #else 3727 #else
3746 #define INC_ICOUNTER ; 3728 #define INC_ICOUNTER ;
3747 #endif 3729 #endif
3748 3730
3749 #define FETCH_INST if (inst_base->br != NON_BRANCH) \ 3731 #define FETCH_INST if (inst_base->br != NON_BRANCH) \
3750 goto PROFILING; \ 3732 goto DISPATCH; \
3751 inst_base = (arm_inst *)&inst_buf[ptr] 3733 inst_base = (arm_inst *)&inst_buf[ptr]
3752#define INC_PC(l) ptr += sizeof(arm_inst) + l 3734#define INC_PC(l) ptr += sizeof(arm_inst) + l
3753 3735
3754// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a 3736// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
3755// clunky switch statement. 3737// clunky switch statement.
3756#if defined __GNUC__ || defined __clang__ 3738#if defined __GNUC__ || defined __clang__
3757#define GOTO_NEXT_INST goto *InstLabel[inst_base->idx] 3739#define GOTO_NEXT_INST \
3740 if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
3741 num_instrs++; \
3742 goto *InstLabel[inst_base->idx]
3758#else 3743#else
3759#define GOTO_NEXT_INST switch(inst_base->idx) { \ 3744#define GOTO_NEXT_INST \
3745 if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
3746 num_instrs++; \
3747 switch(inst_base->idx) { \
3760 case 0: goto VMLA_INST; \ 3748 case 0: goto VMLA_INST; \
3761 case 1: goto VMLS_INST; \ 3749 case 1: goto VMLS_INST; \
3762 case 2: goto VNMLA_INST; \ 3750 case 2: goto VNMLA_INST; \
@@ -4028,20 +4016,15 @@ void InterpreterMainLoop(ARMul_State* state)
4028 unsigned int addr; 4016 unsigned int addr;
4029 unsigned int phys_addr; 4017 unsigned int phys_addr;
4030 unsigned int last_pc = 0; 4018 unsigned int last_pc = 0;
4019 unsigned int num_instrs = 0;
4031 fault_t fault; 4020 fault_t fault;
4032 static unsigned int last_physical_base = 0, last_logical_base = 0; 4021 static unsigned int last_physical_base = 0, last_logical_base = 0;
4033 int ptr; 4022 int ptr;
4023 bool single_step = (cpu->NumInstrsToExecute == 1);
4034 4024
4035 LOAD_NZCVT; 4025 LOAD_NZCVT;
4036 DISPATCH: 4026 DISPATCH:
4037 { 4027 {
4038 if (cpu->NumInstrsToExecute == 0)
4039 return;
4040
4041 cpu->NumInstrsToExecute--;
4042
4043 //NOTICE_LOG(ARM11, "instr!");
4044
4045 if (!cpu->NirqSig) { 4028 if (!cpu->NirqSig) {
4046 if (!(cpu->Cpsr & 0x80)) { 4029 if (!(cpu->Cpsr & 0x80)) {
4047 goto END; 4030 goto END;
@@ -4179,10 +4162,6 @@ void InterpreterMainLoop(ARMul_State* state)
4179 inst_base = (arm_inst *)&inst_buf[ptr]; 4162 inst_base = (arm_inst *)&inst_buf[ptr];
4180 GOTO_NEXT_INST; 4163 GOTO_NEXT_INST;
4181 } 4164 }
4182 PROFILING:
4183 {
4184 goto DISPATCH;
4185 }
4186 ADC_INST: 4165 ADC_INST:
4187 { 4166 {
4188 INC_ICOUNTER; 4167 INC_ICOUNTER;
@@ -4207,7 +4186,7 @@ void InterpreterMainLoop(ARMul_State* state)
4207 } 4186 }
4208 if (inst_cream->Rd == 15) { 4187 if (inst_cream->Rd == 15) {
4209 INC_PC(sizeof(adc_inst)); 4188 INC_PC(sizeof(adc_inst));
4210 goto PROFILING; 4189 goto DISPATCH;
4211 } 4190 }
4212 } 4191 }
4213 cpu->Reg[15] += GET_INST_SIZE(cpu); 4192 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4241,7 +4220,7 @@ void InterpreterMainLoop(ARMul_State* state)
4241 } 4220 }
4242 if (inst_cream->Rd == 15) { 4221 if (inst_cream->Rd == 15) {
4243 INC_PC(sizeof(add_inst)); 4222 INC_PC(sizeof(add_inst));
4244 goto PROFILING; 4223 goto DISPATCH;
4245 } 4224 }
4246 } 4225 }
4247 cpu->Reg[15] += GET_INST_SIZE(cpu); 4226 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4272,7 +4251,7 @@ void InterpreterMainLoop(ARMul_State* state)
4272 } 4251 }
4273 if (inst_cream->Rd == 15) { 4252 if (inst_cream->Rd == 15) {
4274 INC_PC(sizeof(and_inst)); 4253 INC_PC(sizeof(and_inst));
4275 goto PROFILING; 4254 goto DISPATCH;
4276 } 4255 }
4277 } 4256 }
4278 cpu->Reg[15] += GET_INST_SIZE(cpu); 4257 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4290,11 +4269,11 @@ void InterpreterMainLoop(ARMul_State* state)
4290 } 4269 }
4291 SET_PC; 4270 SET_PC;
4292 INC_PC(sizeof(bbl_inst)); 4271 INC_PC(sizeof(bbl_inst));
4293 goto PROFILING; 4272 goto DISPATCH;
4294 } 4273 }
4295 cpu->Reg[15] += GET_INST_SIZE(cpu); 4274 cpu->Reg[15] += GET_INST_SIZE(cpu);
4296 INC_PC(sizeof(bbl_inst)); 4275 INC_PC(sizeof(bbl_inst));
4297 goto PROFILING; 4276 goto DISPATCH;
4298 } 4277 }
4299 BIC_INST: 4278 BIC_INST:
4300 { 4279 {
@@ -4322,7 +4301,7 @@ void InterpreterMainLoop(ARMul_State* state)
4322 } 4301 }
4323 if (inst_cream->Rd == 15) { 4302 if (inst_cream->Rd == 15) {
4324 INC_PC(sizeof(bic_inst)); 4303 INC_PC(sizeof(bic_inst));
4325 goto PROFILING; 4304 goto DISPATCH;
4326 } 4305 }
4327 } 4306 }
4328 cpu->Reg[15] += GET_INST_SIZE(cpu); 4307 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4358,12 +4337,12 @@ void InterpreterMainLoop(ARMul_State* state)
4358 //DEBUG_MSG; 4337 //DEBUG_MSG;
4359 } 4338 }
4360 INC_PC(sizeof(blx_inst)); 4339 INC_PC(sizeof(blx_inst));
4361 goto PROFILING; 4340 goto DISPATCH;
4362 } 4341 }
4363 cpu->Reg[15] += GET_INST_SIZE(cpu); 4342 cpu->Reg[15] += GET_INST_SIZE(cpu);
4364// INC_PC(sizeof(bx_inst)); 4343// INC_PC(sizeof(bx_inst));
4365 INC_PC(sizeof(blx_inst)); 4344 INC_PC(sizeof(blx_inst));
4366 goto PROFILING; 4345 goto DISPATCH;
4367 } 4346 }
4368 BX_INST: 4347 BX_INST:
4369 { 4348 {
@@ -4371,17 +4350,17 @@ void InterpreterMainLoop(ARMul_State* state)
4371 bx_inst *inst_cream = (bx_inst *)inst_base->component; 4350 bx_inst *inst_cream = (bx_inst *)inst_base->component;
4372 if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { 4351 if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
4373 if (inst_cream->Rm == 15) 4352 if (inst_cream->Rm == 15)
4374 DEBUG_LOG(ARM11, "In %s, BX at pc %x: use of Rm = R15 is discouraged\n", __FUNCTION__, cpu->Reg[15]); 4353 LOG_WARNING(Core_ARM11, "BX at pc %x: use of Rm = R15 is discouraged", cpu->Reg[15]);
4375 cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; 4354 cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1;
4376 cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe; 4355 cpu->Reg[15] = cpu->Reg[inst_cream->Rm] & 0xfffffffe;
4377// cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1; 4356// cpu->TFlag = cpu->Reg[inst_cream->Rm] & 0x1;
4378 INC_PC(sizeof(bx_inst)); 4357 INC_PC(sizeof(bx_inst));
4379 goto PROFILING; 4358 goto DISPATCH;
4380 } 4359 }
4381 cpu->Reg[15] += GET_INST_SIZE(cpu); 4360 cpu->Reg[15] += GET_INST_SIZE(cpu);
4382// INC_PC(sizeof(bx_inst)); 4361// INC_PC(sizeof(bx_inst));
4383 INC_PC(sizeof(bx_inst)); 4362 INC_PC(sizeof(bx_inst));
4384 goto PROFILING; 4363 goto DISPATCH;
4385 } 4364 }
4386 BXJ_INST: 4365 BXJ_INST:
4387 CDP_INST: 4366 CDP_INST:
@@ -4393,12 +4372,13 @@ void InterpreterMainLoop(ARMul_State* state)
4393 #define CP_ACCESS_ALLOW 0 4372 #define CP_ACCESS_ALLOW 0
4394 if(CP_ACCESS_ALLOW){ 4373 if(CP_ACCESS_ALLOW){
4395 /* undefined instruction here */ 4374 /* undefined instruction here */
4396 return; 4375 cpu->NumInstrsToExecute = 0;
4376 return num_instrs;
4397 } 4377 }
4398 ERROR_LOG(ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]); 4378 LOG_ERROR(Core_ARM11, "CDP insn inst=0x%x, pc=0x%x\n", inst_cream->inst, cpu->Reg[15]);
4399 unsigned cpab = (cpu->CDP[inst_cream->cp_num]) (cpu, ARMul_FIRST, inst_cream->inst); 4379 unsigned cpab = (cpu->CDP[inst_cream->cp_num]) (cpu, ARMul_FIRST, inst_cream->inst);
4400 if(cpab != ARMul_DONE){ 4380 if(cpab != ARMul_DONE){
4401 ERROR_LOG(ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num); 4381 LOG_ERROR(Core_ARM11, "CDP insn wrong, inst=0x%x, cp_num=0x%x\n", inst_cream->inst, inst_cream->cp_num);
4402 //CITRA_IGNORE_EXIT(-1); 4382 //CITRA_IGNORE_EXIT(-1);
4403 } 4383 }
4404 } 4384 }
@@ -4522,7 +4502,7 @@ void InterpreterMainLoop(ARMul_State* state)
4522// RD = RM; 4502// RD = RM;
4523 if ((inst_cream->Rd == 15)) { 4503 if ((inst_cream->Rd == 15)) {
4524 INC_PC(sizeof(mov_inst)); 4504 INC_PC(sizeof(mov_inst));
4525 goto PROFILING; 4505 goto DISPATCH;
4526 } 4506 }
4527 } 4507 }
4528// DEBUG_LOG(ARM11, "cpy inst %x\n", cpu->Reg[15]); 4508// DEBUG_LOG(ARM11, "cpy inst %x\n", cpu->Reg[15]);
@@ -4558,7 +4538,7 @@ void InterpreterMainLoop(ARMul_State* state)
4558 } 4538 }
4559 if (inst_cream->Rd == 15) { 4539 if (inst_cream->Rd == 15) {
4560 INC_PC(sizeof(eor_inst)); 4540 INC_PC(sizeof(eor_inst));
4561 goto PROFILING; 4541 goto DISPATCH;
4562 } 4542 }
4563 } 4543 }
4564 cpu->Reg[15] += GET_INST_SIZE(cpu); 4544 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4717,7 +4697,7 @@ void InterpreterMainLoop(ARMul_State* state)
4717 } 4697 }
4718 if (BIT(inst, 15)) { 4698 if (BIT(inst, 15)) {
4719 INC_PC(sizeof(ldst_inst)); 4699 INC_PC(sizeof(ldst_inst));
4720 goto PROFILING; 4700 goto DISPATCH;
4721 } 4701 }
4722 } 4702 }
4723 cpu->Reg[15] += GET_INST_SIZE(cpu); 4703 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4764,7 +4744,7 @@ void InterpreterMainLoop(ARMul_State* state)
4764 cpu->TFlag = value & 0x1; 4744 cpu->TFlag = value & 0x1;
4765 cpu->Reg[15] &= 0xFFFFFFFE; 4745 cpu->Reg[15] &= 0xFFFFFFFE;
4766 INC_PC(sizeof(ldst_inst)); 4746 INC_PC(sizeof(ldst_inst));
4767 goto PROFILING; 4747 goto DISPATCH;
4768 } 4748 }
4769 //} 4749 //}
4770 cpu->Reg[15] += GET_INST_SIZE(cpu); 4750 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4794,7 +4774,7 @@ void InterpreterMainLoop(ARMul_State* state)
4794 cpu->TFlag = value & 0x1; 4774 cpu->TFlag = value & 0x1;
4795 cpu->Reg[15] &= 0xFFFFFFFE; 4775 cpu->Reg[15] &= 0xFFFFFFFE;
4796 INC_PC(sizeof(ldst_inst)); 4776 INC_PC(sizeof(ldst_inst));
4797 goto PROFILING; 4777 goto DISPATCH;
4798 } 4778 }
4799 } 4779 }
4800 cpu->Reg[15] += GET_INST_SIZE(cpu); 4780 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4825,7 +4805,7 @@ void InterpreterMainLoop(ARMul_State* state)
4825 & 0xffff; 4805 & 0xffff;
4826 RD = RN + operand2; 4806 RD = RN + operand2;
4827 if (inst_cream->Rn == 15 || inst_cream->Rm == 15) { 4807 if (inst_cream->Rn == 15 || inst_cream->Rm == 15) {
4828 DEBUG_LOG(ARM11, "in line %d\n", __LINE__); 4808 LOG_ERROR(Core_ARM11, "invalid operands for UXTAH");
4829 CITRA_IGNORE_EXIT(-1); 4809 CITRA_IGNORE_EXIT(-1);
4830 } 4810 }
4831 } 4811 }
@@ -4848,7 +4828,7 @@ void InterpreterMainLoop(ARMul_State* state)
4848 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; 4828 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
4849 if (BITS(inst_cream->inst, 12, 15) == 15) { 4829 if (BITS(inst_cream->inst, 12, 15) == 15) {
4850 INC_PC(sizeof(ldst_inst)); 4830 INC_PC(sizeof(ldst_inst));
4851 goto PROFILING; 4831 goto DISPATCH;
4852 } 4832 }
4853 } 4833 }
4854 cpu->Reg[15] += GET_INST_SIZE(cpu); 4834 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4869,7 +4849,7 @@ void InterpreterMainLoop(ARMul_State* state)
4869 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; 4849 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
4870 if (BITS(inst_cream->inst, 12, 15) == 15) { 4850 if (BITS(inst_cream->inst, 12, 15) == 15) {
4871 INC_PC(sizeof(ldst_inst)); 4851 INC_PC(sizeof(ldst_inst));
4872 goto PROFILING; 4852 goto DISPATCH;
4873 } 4853 }
4874 } 4854 }
4875 cpu->Reg[15] += GET_INST_SIZE(cpu); 4855 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4888,7 +4868,7 @@ void InterpreterMainLoop(ARMul_State* state)
4888 uint32_t rear_phys_addr; 4868 uint32_t rear_phys_addr;
4889 fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 1); 4869 fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 1);
4890 if(fault){ 4870 if(fault){
4891 ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n"); 4871 LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n");
4892 CITRA_IGNORE_EXIT(-1); 4872 CITRA_IGNORE_EXIT(-1);
4893 goto MMU_EXCEPTION; 4873 goto MMU_EXCEPTION;
4894 } 4874 }
@@ -4926,7 +4906,7 @@ void InterpreterMainLoop(ARMul_State* state)
4926 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; 4906 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
4927 if (BITS(inst_cream->inst, 12, 15) == 15) { 4907 if (BITS(inst_cream->inst, 12, 15) == 15) {
4928 INC_PC(sizeof(ldst_inst)); 4908 INC_PC(sizeof(ldst_inst));
4929 goto PROFILING; 4909 goto DISPATCH;
4930 } 4910 }
4931 } 4911 }
4932 cpu->Reg[15] += GET_INST_SIZE(cpu); 4912 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4953,7 +4933,7 @@ void InterpreterMainLoop(ARMul_State* state)
4953 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; 4933 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
4954 if (BITS(inst_cream->inst, 12, 15) == 15) { 4934 if (BITS(inst_cream->inst, 12, 15) == 15) {
4955 INC_PC(sizeof(ldst_inst)); 4935 INC_PC(sizeof(ldst_inst));
4956 goto PROFILING; 4936 goto DISPATCH;
4957 } 4937 }
4958 } 4938 }
4959 cpu->Reg[15] += GET_INST_SIZE(cpu); 4939 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -4980,7 +4960,7 @@ void InterpreterMainLoop(ARMul_State* state)
4980 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; 4960 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
4981 if (BITS(inst_cream->inst, 12, 15) == 15) { 4961 if (BITS(inst_cream->inst, 12, 15) == 15) {
4982 INC_PC(sizeof(ldst_inst)); 4962 INC_PC(sizeof(ldst_inst));
4983 goto PROFILING; 4963 goto DISPATCH;
4984 } 4964 }
4985 } 4965 }
4986 cpu->Reg[15] += GET_INST_SIZE(cpu); 4966 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5006,7 +4986,7 @@ void InterpreterMainLoop(ARMul_State* state)
5006 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; 4986 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
5007 if (BITS(inst_cream->inst, 12, 15) == 15) { 4987 if (BITS(inst_cream->inst, 12, 15) == 15) {
5008 INC_PC(sizeof(ldst_inst)); 4988 INC_PC(sizeof(ldst_inst));
5009 goto PROFILING; 4989 goto DISPATCH;
5010 } 4990 }
5011 } 4991 }
5012 cpu->Reg[15] += GET_INST_SIZE(cpu); 4992 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5031,7 +5011,7 @@ void InterpreterMainLoop(ARMul_State* state)
5031 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value; 5011 cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
5032 if (BITS(inst_cream->inst, 12, 15) == 15) { 5012 if (BITS(inst_cream->inst, 12, 15) == 15) {
5033 INC_PC(sizeof(ldst_inst)); 5013 INC_PC(sizeof(ldst_inst));
5034 goto PROFILING; 5014 goto DISPATCH;
5035 } 5015 }
5036 } 5016 }
5037 cpu->Reg[15] += GET_INST_SIZE(cpu); 5017 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5058,7 +5038,7 @@ void InterpreterMainLoop(ARMul_State* state)
5058 5038
5059 if (BITS(inst_cream->inst, 12, 15) == 15) { 5039 if (BITS(inst_cream->inst, 12, 15) == 15) {
5060 INC_PC(sizeof(ldst_inst)); 5040 INC_PC(sizeof(ldst_inst));
5061 goto PROFILING; 5041 goto DISPATCH;
5062 } 5042 }
5063 } 5043 }
5064 cpu->Reg[15] += GET_INST_SIZE(cpu); 5044 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5111,7 +5091,7 @@ void InterpreterMainLoop(ARMul_State* state)
5111 switch(OPCODE_2){ 5091 switch(OPCODE_2){
5112 case 0: /* invalidate all */ 5092 case 0: /* invalidate all */
5113 //invalidate_all_tlb(state); 5093 //invalidate_all_tlb(state);
5114 DEBUG_LOG(ARM11, "{TLB} [INSN] invalidate all\n"); 5094 LOG_DEBUG(Core_ARM11, "{TLB} [INSN] invalidate all");
5115 //remove_tlb(INSN_TLB); 5095 //remove_tlb(INSN_TLB);
5116 //erase_all(core, INSN_TLB); 5096 //erase_all(core, INSN_TLB);
5117 break; 5097 break;
@@ -5137,7 +5117,7 @@ void InterpreterMainLoop(ARMul_State* state)
5137 //invalidate_all_tlb(state); 5117 //invalidate_all_tlb(state);
5138 //remove_tlb(DATA_TLB); 5118 //remove_tlb(DATA_TLB);
5139 //erase_all(core, DATA_TLB); 5119 //erase_all(core, DATA_TLB);
5140 DEBUG_LOG(ARM11, "{TLB} [DATA] invalidate all\n"); 5120 LOG_DEBUG(Core_ARM11, "{TLB} [DATA] invalidate all");
5141 break; 5121 break;
5142 case 1: /* invalidate by MVA */ 5122 case 1: /* invalidate by MVA */
5143 //invalidate_by_mva(state, value); 5123 //invalidate_by_mva(state, value);
@@ -5169,13 +5149,13 @@ void InterpreterMainLoop(ARMul_State* state)
5169 //invalidate_by_mva(state, value); 5149 //invalidate_by_mva(state, value);
5170 //erase_by_mva(core, RD, DATA_TLB); 5150 //erase_by_mva(core, RD, DATA_TLB);
5171 //erase_by_mva(core, RD, INSN_TLB); 5151 //erase_by_mva(core, RD, INSN_TLB);
5172 DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by mva\n"); 5152 LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by mva");
5173 break; 5153 break;
5174 case 2: /* invalidate by asid */ 5154 case 2: /* invalidate by asid */
5175 //invalidate_by_asid(state, value); 5155 //invalidate_by_asid(state, value);
5176 //erase_by_asid(core, RD, DATA_TLB); 5156 //erase_by_asid(core, RD, DATA_TLB);
5177 //erase_by_asid(core, RD, INSN_TLB); 5157 //erase_by_asid(core, RD, INSN_TLB);
5178 DEBUG_LOG(ARM11, "{TLB} [UNIFILED] invalidate by asid\n"); 5158 LOG_DEBUG(Core_ARM11, "{TLB} [UNIFILED] invalidate by asid");
5179 break; 5159 break;
5180 default: 5160 default:
5181 break; 5161 break;
@@ -5197,7 +5177,7 @@ void InterpreterMainLoop(ARMul_State* state)
5197 } 5177 }
5198 5178
5199 } else { 5179 } else {
5200 DEBUG_LOG(ARM11, "mcr is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2); 5180 LOG_ERROR(Core_ARM11, "mcr CRn=%d, CRm=%d OP2=%d is not implemented", CRn, CRm, OPCODE_2);
5201 } 5181 }
5202 } 5182 }
5203 } 5183 }
@@ -5217,7 +5197,7 @@ void InterpreterMainLoop(ARMul_State* state)
5217 uint64_t rs = RS; 5197 uint64_t rs = RS;
5218 uint64_t rn = RN; 5198 uint64_t rn = RN;
5219 if (inst_cream->Rm == 15 || inst_cream->Rs == 15 || inst_cream->Rn == 15) { 5199 if (inst_cream->Rm == 15 || inst_cream->Rs == 15 || inst_cream->Rn == 15) {
5220 DEBUG_LOG(ARM11, "in __line__\n", __LINE__); 5200 LOG_ERROR(Core_ARM11, "invalid operands for MLA");
5221 CITRA_IGNORE_EXIT(-1); 5201 CITRA_IGNORE_EXIT(-1);
5222 } 5202 }
5223// RD = dst = RM * RS + RN; 5203// RD = dst = RM * RS + RN;
@@ -5228,7 +5208,7 @@ void InterpreterMainLoop(ARMul_State* state)
5228 } 5208 }
5229 if (inst_cream->Rd == 15) { 5209 if (inst_cream->Rd == 15) {
5230 INC_PC(sizeof(mla_inst)); 5210 INC_PC(sizeof(mla_inst));
5231 goto PROFILING; 5211 goto DISPATCH;
5232 } 5212 }
5233 } 5213 }
5234 cpu->Reg[15] += GET_INST_SIZE(cpu); 5214 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5260,7 +5240,7 @@ void InterpreterMainLoop(ARMul_State* state)
5260 } 5240 }
5261 if (inst_cream->Rd == 15) { 5241 if (inst_cream->Rd == 15) {
5262 INC_PC(sizeof(mov_inst)); 5242 INC_PC(sizeof(mov_inst));
5263 goto PROFILING; 5243 goto DISPATCH;
5264 } 5244 }
5265// return; 5245// return;
5266 } 5246 }
@@ -5331,7 +5311,7 @@ void InterpreterMainLoop(ARMul_State* state)
5331 } 5311 }
5332 } 5312 }
5333 else { 5313 else {
5334 DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2); 5314 LOG_ERROR(Core_ARM11, "mrc CRn=%d, CRm=%d, OP2=%d is not implemented", CRn, CRm, OPCODE_2);
5335 } 5315 }
5336 } 5316 }
5337 //DEBUG_LOG(ARM11, "mrc is not implementated. CRn is %d, CRm is %d, OPCODE_2 is %d\n", CRn, CRm, OPCODE_2); 5317 //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)
5422 } 5402 }
5423 if (inst_cream->Rd == 15) { 5403 if (inst_cream->Rd == 15) {
5424 INC_PC(sizeof(mul_inst)); 5404 INC_PC(sizeof(mul_inst));
5425 goto PROFILING; 5405 goto DISPATCH;
5426 } 5406 }
5427 } 5407 }
5428 cpu->Reg[15] += GET_INST_SIZE(cpu); 5408 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5451,7 +5431,7 @@ void InterpreterMainLoop(ARMul_State* state)
5451 } 5431 }
5452 if (inst_cream->Rd == 15) { 5432 if (inst_cream->Rd == 15) {
5453 INC_PC(sizeof(mvn_inst)); 5433 INC_PC(sizeof(mvn_inst));
5454 goto PROFILING; 5434 goto DISPATCH;
5455 } 5435 }
5456 } 5436 }
5457 cpu->Reg[15] += GET_INST_SIZE(cpu); 5437 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5483,7 +5463,7 @@ void InterpreterMainLoop(ARMul_State* state)
5483 } 5463 }
5484 if (inst_cream->Rd == 15) { 5464 if (inst_cream->Rd == 15) {
5485 INC_PC(sizeof(orr_inst)); 5465 INC_PC(sizeof(orr_inst));
5486 goto PROFILING; 5466 goto DISPATCH;
5487 } 5467 }
5488 } 5468 }
5489 cpu->Reg[15] += GET_INST_SIZE(cpu); 5469 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5522,7 +5502,7 @@ void InterpreterMainLoop(ARMul_State* state)
5522 (((RM >> 16) & 0xff) << 8) | 5502 (((RM >> 16) & 0xff) << 8) |
5523 ((RM >> 24) & 0xff); 5503 ((RM >> 24) & 0xff);
5524 if (inst_cream->Rm == 15) { 5504 if (inst_cream->Rm == 15) {
5525 DEBUG_LOG(ARM11, "in line %d\n", __LINE__); 5505 LOG_ERROR(Core_ARM11, "invalid operand for REV");
5526 CITRA_IGNORE_EXIT(-1); 5506 CITRA_IGNORE_EXIT(-1);
5527 } 5507 }
5528 } 5508 }
@@ -5575,7 +5555,7 @@ void InterpreterMainLoop(ARMul_State* state)
5575 } 5555 }
5576 if (inst_cream->Rd == 15) { 5556 if (inst_cream->Rd == 15) {
5577 INC_PC(sizeof(rsb_inst)); 5557 INC_PC(sizeof(rsb_inst));
5578 goto PROFILING; 5558 goto DISPATCH;
5579 } 5559 }
5580 } 5560 }
5581 cpu->Reg[15] += GET_INST_SIZE(cpu); 5561 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5612,7 +5592,7 @@ void InterpreterMainLoop(ARMul_State* state)
5612 } 5592 }
5613 if (inst_cream->Rd == 15) { 5593 if (inst_cream->Rd == 15) {
5614 INC_PC(sizeof(rsc_inst)); 5594 INC_PC(sizeof(rsc_inst));
5615 goto PROFILING; 5595 goto DISPATCH;
5616 } 5596 }
5617 } 5597 }
5618 cpu->Reg[15] += GET_INST_SIZE(cpu); 5598 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5653,7 +5633,7 @@ void InterpreterMainLoop(ARMul_State* state)
5653 } 5633 }
5654 if (inst_cream->Rd == 15) { 5634 if (inst_cream->Rd == 15) {
5655 INC_PC(sizeof(sbc_inst)); 5635 INC_PC(sizeof(sbc_inst));
5656 goto PROFILING; 5636 goto DISPATCH;
5657 } 5637 }
5658 } 5638 }
5659 cpu->Reg[15] += GET_INST_SIZE(cpu); 5639 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5975,7 +5955,7 @@ void InterpreterMainLoop(ARMul_State* state)
5975 sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component; 5955 sxtb_inst *inst_cream = (sxtb_inst *)inst_base->component;
5976 if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) { 5956 if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
5977 if (inst_cream->Rm == 15) { 5957 if (inst_cream->Rm == 15) {
5978 DEBUG_LOG(ARM11, "line is %d\n", __LINE__); 5958 LOG_ERROR(Core_ARM11, "invalid operand for SXTB");
5979 CITRA_IGNORE_EXIT(-1); 5959 CITRA_IGNORE_EXIT(-1);
5980 } 5960 }
5981 unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate); 5961 unsigned int operand2 = ROTATE_RIGHT_32(RM, 8 * inst_cream->rotate);
@@ -6066,7 +6046,7 @@ void InterpreterMainLoop(ARMul_State* state)
6066 } 6046 }
6067 cpu->Reg[15] += GET_INST_SIZE(cpu); 6047 cpu->Reg[15] += GET_INST_SIZE(cpu);
6068 //if (BITS(inst_cream->inst, 12, 15) == 15) 6048 //if (BITS(inst_cream->inst, 12, 15) == 15)
6069 // goto PROFILING; 6049 // goto DISPATCH;
6070 INC_PC(sizeof(ldst_inst)); 6050 INC_PC(sizeof(ldst_inst));
6071 FETCH_INST; 6051 FETCH_INST;
6072 GOTO_NEXT_INST; 6052 GOTO_NEXT_INST;
@@ -6081,7 +6061,7 @@ void InterpreterMainLoop(ARMul_State* state)
6081 uint32_t rear_phys_addr; 6061 uint32_t rear_phys_addr;
6082 fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 0); 6062 fault = check_address_validity(cpu, addr + 4, &rear_phys_addr, 0);
6083 if (fault){ 6063 if (fault){
6084 ERROR_LOG(ARM11, "mmu fault , should rollback the above get_addr\n"); 6064 LOG_ERROR(Core_ARM11, "mmu fault , should rollback the above get_addr\n");
6085 CITRA_IGNORE_EXIT(-1); 6065 CITRA_IGNORE_EXIT(-1);
6086 goto MMU_EXCEPTION; 6066 goto MMU_EXCEPTION;
6087 } 6067 }
@@ -6175,7 +6155,7 @@ void InterpreterMainLoop(ARMul_State* state)
6175 } 6155 }
6176 cpu->Reg[15] += GET_INST_SIZE(cpu); 6156 cpu->Reg[15] += GET_INST_SIZE(cpu);
6177 //if (BITS(inst_cream->inst, 12, 15) == 15) 6157 //if (BITS(inst_cream->inst, 12, 15) == 15)
6178 // goto PROFILING; 6158 // goto DISPATCH;
6179 INC_PC(sizeof(ldst_inst)); 6159 INC_PC(sizeof(ldst_inst));
6180 FETCH_INST; 6160 FETCH_INST;
6181 GOTO_NEXT_INST; 6161 GOTO_NEXT_INST;
@@ -6225,7 +6205,7 @@ void InterpreterMainLoop(ARMul_State* state)
6225 } 6205 }
6226 if (inst_cream->Rd == 15) { 6206 if (inst_cream->Rd == 15) {
6227 INC_PC(sizeof(sub_inst)); 6207 INC_PC(sizeof(sub_inst));
6228 goto PROFILING; 6208 goto DISPATCH;
6229 } 6209 }
6230 } 6210 }
6231 cpu->Reg[15] += GET_INST_SIZE(cpu); 6211 cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6449,7 +6429,7 @@ void InterpreterMainLoop(ARMul_State* state)
6449 cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm; 6429 cpu->Reg[15] = cpu->Reg[15] + 4 + inst_cream->imm;
6450 //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]); 6430 //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]);
6451 INC_PC(sizeof(b_2_thumb)); 6431 INC_PC(sizeof(b_2_thumb));
6452 goto PROFILING; 6432 goto DISPATCH;
6453 } 6433 }
6454 B_COND_THUMB: 6434 B_COND_THUMB:
6455 { 6435 {
@@ -6461,7 +6441,7 @@ void InterpreterMainLoop(ARMul_State* state)
6461 cpu->Reg[15] += 2; 6441 cpu->Reg[15] += 2;
6462 //DEBUG_LOG(ARM11, " B_COND_THUMB: imm=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[15]); 6442 //DEBUG_LOG(ARM11, " B_COND_THUMB: imm=0x%x, r15=0x%x\n", inst_cream->imm, cpu->Reg[15]);
6463 INC_PC(sizeof(b_cond_thumb)); 6443 INC_PC(sizeof(b_cond_thumb));
6464 goto PROFILING; 6444 goto DISPATCH;
6465 } 6445 }
6466 BL_1_THUMB: 6446 BL_1_THUMB:
6467 { 6447 {
@@ -6487,7 +6467,7 @@ void InterpreterMainLoop(ARMul_State* state)
6487 cpu->Reg[14] = tmp; 6467 cpu->Reg[14] = tmp;
6488 //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]); 6468 //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]);
6489 INC_PC(sizeof(bl_2_thumb)); 6469 INC_PC(sizeof(bl_2_thumb));
6490 goto PROFILING; 6470 goto DISPATCH;
6491 } 6471 }
6492 BLX_1_THUMB: 6472 BLX_1_THUMB:
6493 { 6473 {
@@ -6503,7 +6483,7 @@ void InterpreterMainLoop(ARMul_State* state)
6503 cpu->TFlag = 0; 6483 cpu->TFlag = 0;
6504 //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]); 6484 //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]);
6505 INC_PC(sizeof(blx_1_thumb)); 6485 INC_PC(sizeof(blx_1_thumb));
6506 goto PROFILING; 6486 goto DISPATCH;
6507 } 6487 }
6508 6488
6509 UQADD16_INST: 6489 UQADD16_INST:
@@ -6532,12 +6512,14 @@ void InterpreterMainLoop(ARMul_State* state)
6532 cpu->AbortAddr = addr; 6512 cpu->AbortAddr = addr;
6533 cpu->CP15[CP15(CP15_FAULT_STATUS)] = fault & 0xff; 6513 cpu->CP15[CP15(CP15_FAULT_STATUS)] = fault & 0xff;
6534 cpu->CP15[CP15(CP15_FAULT_ADDRESS)] = addr; 6514 cpu->CP15[CP15(CP15_FAULT_ADDRESS)] = addr;
6535 return; 6515 cpu->NumInstrsToExecute = 0;
6516 return num_instrs;
6536 } 6517 }
6537 END: 6518 END:
6538 { 6519 {
6539 SAVE_NZCVT; 6520 SAVE_NZCVT;
6540 return; 6521 cpu->NumInstrsToExecute = 0;
6522 return num_instrs;
6541 } 6523 }
6542 INIT_INST_LENGTH: 6524 INIT_INST_LENGTH:
6543 { 6525 {
@@ -6557,7 +6539,8 @@ void InterpreterMainLoop(ARMul_State* state)
6557 DEBUG_LOG(ARM11, "%llx\n", InstLabel[1]); 6539 DEBUG_LOG(ARM11, "%llx\n", InstLabel[1]);
6558 DEBUG_LOG(ARM11, "%lld\n", (char *)InstEndLabel[1] - (char *)InstLabel[1]); 6540 DEBUG_LOG(ARM11, "%lld\n", (char *)InstEndLabel[1] - (char *)InstLabel[1]);
6559#endif 6541#endif
6560 return; 6542 cpu->NumInstrsToExecute = 0;
6543 return num_instrs;
6561 } 6544 }
6562} 6545}
6563 6546
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.h b/src/core/arm/dyncom/arm_dyncom_interpreter.h
index d73f8f65f..3a2462f55 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.h
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.h
@@ -1,7 +1,7 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7void InterpreterMainLoop(ARMul_State* state); 7unsigned InterpreterMainLoop(ARMul_State* state);
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index ed4415082..e2aa5ce92 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/arm/interpreter/arm_interpreter.h" 5#include "core/arm/interpreter/arm_interpreter.h"
6 6
@@ -24,7 +24,7 @@ ARM_Interpreter::ARM_Interpreter() {
24 state->lateabtSig = LOW; 24 state->lateabtSig = LOW;
25 25
26 // Reset the core to initial state 26 // Reset the core to initial state
27 ARMul_CoProInit(state); 27 ARMul_CoProInit(state);
28 ARMul_Reset(state); 28 ARMul_Reset(state);
29 state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext 29 state->NextInstr = RESUME; // NOTE: This will be overwritten by LoadContext
30 state->Emulate = 3; 30 state->Emulate = 3;
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index ceb1be438..ed53d997c 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
@@ -18,7 +18,7 @@ public:
18 18
19 /** 19 /**
20 * Set the Program Counter to an address 20 * Set the Program Counter to an address
21 * @param addr Address to set PC to 21 * @param pc Address to set PC to
22 */ 22 */
23 void SetPC(u32 pc) override; 23 void SetPC(u32 pc) override;
24 24
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp
index 73223874e..1a589e39c 100644
--- a/src/core/arm/interpreter/armemu.cpp
+++ b/src/core/arm/interpreter/armemu.cpp
@@ -949,7 +949,7 @@ ARMul_Emulate26 (ARMul_State * state)
949 //printf("t decode %04lx -> %08lx\n", instr & 0xffff, armOp); 949 //printf("t decode %04lx -> %08lx\n", instr & 0xffff, armOp);
950 950
951 if (armOp == 0xDEADC0DE) { 951 if (armOp == 0xDEADC0DE) {
952 DEBUG("Failed to decode thumb opcode %04X at %08X\n", instr, pc); 952 LOG_ERROR(Core_ARM11, "Failed to decode thumb opcode %04X at %08X", instr, pc);
953 } 953 }
954 954
955 instr = armOp; 955 instr = armOp;
@@ -1166,7 +1166,7 @@ mainswitch:
1166 else if ((((int)BITS(21, 27)) == 0x3e) && ((int)BITS(4, 6) == 0x1)) { 1166 else if ((((int)BITS(21, 27)) == 0x3e) && ((int)BITS(4, 6) == 0x1)) {
1167 //(ARMword)(instr<<(31-(n))) >> ((31-(n))+(m)) 1167 //(ARMword)(instr<<(31-(n))) >> ((31-(n))+(m))
1168 unsigned msb ,tmp_rn, tmp_rd, dst; 1168 unsigned msb ,tmp_rn, tmp_rd, dst;
1169 msb = tmp_rd = tmp_rn = dst = 0; 1169 tmp_rd = tmp_rn = dst = 0;
1170 Rd = BITS(12, 15); 1170 Rd = BITS(12, 15);
1171 Rn = BITS(0, 3); 1171 Rn = BITS(0, 3);
1172 lsb = BITS(7, 11); 1172 lsb = BITS(7, 11);
@@ -1737,7 +1737,7 @@ mainswitch:
1737 //chy 2006-02-15 if in user mode, can not set cpsr 0:23 1737 //chy 2006-02-15 if in user mode, can not set cpsr 0:23
1738 //from p165 of ARMARM book 1738 //from p165 of ARMARM book
1739 state->Cpsr = GETSPSR (state->Bank); 1739 state->Cpsr = GETSPSR (state->Bank);
1740 //ARMul_CPSRAltered (state); 1740 ARMul_CPSRAltered (state);
1741#else 1741#else
1742 rhs = DPRegRHS; 1742 rhs = DPRegRHS;
1743 temp = LHS & rhs; 1743 temp = LHS & rhs;
@@ -1877,7 +1877,7 @@ mainswitch:
1877 /* TEQP reg */ 1877 /* TEQP reg */
1878#ifdef MODE32 1878#ifdef MODE32
1879 state->Cpsr = GETSPSR (state->Bank); 1879 state->Cpsr = GETSPSR (state->Bank);
1880 //ARMul_CPSRAltered (state); 1880 ARMul_CPSRAltered (state);
1881#else 1881#else
1882 rhs = DPRegRHS; 1882 rhs = DPRegRHS;
1883 temp = LHS ^ rhs; 1883 temp = LHS ^ rhs;
@@ -1993,7 +1993,7 @@ mainswitch:
1993 /* CMPP reg. */ 1993 /* CMPP reg. */
1994#ifdef MODE32 1994#ifdef MODE32
1995 state->Cpsr = GETSPSR (state->Bank); 1995 state->Cpsr = GETSPSR (state->Bank);
1996 //ARMul_CPSRAltered (state); 1996 ARMul_CPSRAltered (state);
1997#else 1997#else
1998 rhs = DPRegRHS; 1998 rhs = DPRegRHS;
1999 temp = LHS - rhs; 1999 temp = LHS - rhs;
@@ -2112,7 +2112,7 @@ mainswitch:
2112 if (DESTReg == 15) { 2112 if (DESTReg == 15) {
2113#ifdef MODE32 2113#ifdef MODE32
2114 state->Cpsr = GETSPSR (state->Bank); 2114 state->Cpsr = GETSPSR (state->Bank);
2115 //ARMul_CPSRAltered (state); 2115 ARMul_CPSRAltered (state);
2116#else 2116#else
2117 rhs = DPRegRHS; 2117 rhs = DPRegRHS;
2118 temp = LHS + rhs; 2118 temp = LHS + rhs;
@@ -2200,17 +2200,57 @@ mainswitch:
2200 Handle_Store_Double (state, instr); 2200 Handle_Store_Double (state, instr);
2201 break; 2201 break;
2202 } 2202 }
2203 if (BITS(4, 11) == 0xF9) { //strexd
2204 u32 l = LHSReg;
2205
2206 bool enter = false;
2207
2208 if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr)&&
2209 state->currentexvald == (u32)ARMul_ReadWord(state, state->currentexaddr + 4))
2210 enter = true;
2211
2212
2213 //todo bug this and STREXD and LDREXD http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0360e/CHDGJGGC.html
2214
2215
2216 if (enter) {
2217 ARMul_StoreWordN(state, LHS, state->Reg[RHSReg]);
2218 ARMul_StoreWordN(state,LHS + 4 , state->Reg[RHSReg + 1]);
2219 state->Reg[DESTReg] = 0;
2220 } else {
2221 state->Reg[DESTReg] = 1;
2222 }
2223
2224 break;
2225 }
2203#endif 2226#endif
2204 dest = DPRegRHS; 2227 dest = DPRegRHS;
2205 WRITEDEST (dest); 2228 WRITEDEST (dest);
2206 break; 2229 break;
2207 2230
2208 case 0x1b: /* MOVS reg */ 2231 case 0x1B: /* MOVS reg */
2209#ifdef MODET 2232#ifdef MODET
2233 /* ldrexd ichfly */
2234 if (BITS(0, 11) == 0xF9F) { //strexd
2235 lhs = LHS;
2236
2237 state->currentexaddr = lhs;
2238 state->currentexval = (u32)ARMul_ReadWord(state, lhs);
2239 state->currentexvald = (u32)ARMul_ReadWord(state, lhs + 4);
2240
2241 state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs);
2242 state->Reg[DESTReg] = ARMul_LoadWordN(state, lhs + 4);
2243 break;
2244 }
2245
2210 if ((BITS (4, 11) & 0xF9) == 0x9) 2246 if ((BITS (4, 11) & 0xF9) == 0x9)
2211 /* LDR register offset, write-back, up, pre indexed. */ 2247 /* LDR register offset, write-back, up, pre indexed. */
2212 LHPREUPWB (); 2248 LHPREUPWB ();
2213 /* Continue with remaining instruction decoding. */ 2249 /* Continue with remaining instruction decoding. */
2250
2251
2252
2253
2214#endif 2254#endif
2215 dest = DPSRegRHS; 2255 dest = DPSRegRHS;
2216 WRITESDEST (dest); 2256 WRITESDEST (dest);
@@ -2297,12 +2337,12 @@ mainswitch:
2297 if (state->currentexval == (u32)ARMul_LoadHalfWord(state, state->currentexaddr))enter = true; 2337 if (state->currentexval == (u32)ARMul_LoadHalfWord(state, state->currentexaddr))enter = true;
2298 2338
2299 2339
2300 ARMul_StoreHalfWord(state, lhs, RHS);
2301 //StoreWord(state, lhs, RHS) 2340 //StoreWord(state, lhs, RHS)
2302 if (state->Aborted) { 2341 if (state->Aborted) {
2303 TAKEABORT; 2342 TAKEABORT;
2304 } 2343 }
2305 if (enter) { 2344 if (enter) {
2345 ARMul_StoreHalfWord(state, lhs, RHS);
2306 state->Reg[DESTReg] = 0; 2346 state->Reg[DESTReg] = 0;
2307 } else { 2347 } else {
2308 state->Reg[DESTReg] = 1; 2348 state->Reg[DESTReg] = 1;
@@ -2520,7 +2560,7 @@ mainswitch:
2520 /* TSTP immed. */ 2560 /* TSTP immed. */
2521#ifdef MODE32 2561#ifdef MODE32
2522 state->Cpsr = GETSPSR (state->Bank); 2562 state->Cpsr = GETSPSR (state->Bank);
2523 //ARMul_CPSRAltered (state); 2563 ARMul_CPSRAltered (state);
2524#else 2564#else
2525 temp = LHS & DPImmRHS; 2565 temp = LHS & DPImmRHS;
2526 SETR15PSR (temp); 2566 SETR15PSR (temp);
@@ -2547,7 +2587,7 @@ mainswitch:
2547 /* TEQP immed. */ 2587 /* TEQP immed. */
2548#ifdef MODE32 2588#ifdef MODE32
2549 state->Cpsr = GETSPSR (state->Bank); 2589 state->Cpsr = GETSPSR (state->Bank);
2550 //ARMul_CPSRAltered (state); 2590 ARMul_CPSRAltered (state);
2551#else 2591#else
2552 temp = LHS ^ DPImmRHS; 2592 temp = LHS ^ DPImmRHS;
2553 SETR15PSR (temp); 2593 SETR15PSR (temp);
@@ -2568,7 +2608,7 @@ mainswitch:
2568 /* CMPP immed. */ 2608 /* CMPP immed. */
2569#ifdef MODE32 2609#ifdef MODE32
2570 state->Cpsr = GETSPSR (state->Bank); 2610 state->Cpsr = GETSPSR (state->Bank);
2571 //ARMul_CPSRAltered (state); 2611 ARMul_CPSRAltered (state);
2572#else 2612#else
2573 temp = LHS - DPImmRHS; 2613 temp = LHS - DPImmRHS;
2574 SETR15PSR (temp); 2614 SETR15PSR (temp);
@@ -2604,7 +2644,7 @@ mainswitch:
2604 /* CMNP immed. */ 2644 /* CMNP immed. */
2605#ifdef MODE32 2645#ifdef MODE32
2606 state->Cpsr = GETSPSR (state->Bank); 2646 state->Cpsr = GETSPSR (state->Bank);
2607 //ARMul_CPSRAltered (state); 2647 ARMul_CPSRAltered (state);
2608#else 2648#else
2609 temp = LHS + DPImmRHS; 2649 temp = LHS + DPImmRHS;
2610 SETR15PSR (temp); 2650 SETR15PSR (temp);
@@ -3055,17 +3095,14 @@ mainswitch:
3055 3095
3056 case 0x68: /* Store Word, No WriteBack, Post Inc, Reg. */ 3096 case 0x68: /* Store Word, No WriteBack, Post Inc, Reg. */
3057 //ichfly PKHBT PKHTB todo check this 3097 //ichfly PKHBT PKHTB todo check this
3058 if ((instr & 0x70) == 0x10) //pkhbt 3098 if ((instr & 0x70) == 0x10) { //pkhbt
3059 {
3060 u8 idest = BITS(12, 15); 3099 u8 idest = BITS(12, 15);
3061 u8 rfis = BITS(16, 19); 3100 u8 rfis = BITS(16, 19);
3062 u8 rlast = BITS(0, 3); 3101 u8 rlast = BITS(0, 3);
3063 u8 ishi = BITS(7,11); 3102 u8 ishi = BITS(7,11);
3064 state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); 3103 state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000);
3065 break; 3104 break;
3066 } 3105 } else if ((instr & 0x70) == 0x50) { //pkhtb
3067 else if ((instr & 0x70) == 0x50)//pkhtb
3068 {
3069 u8 idest = BITS(12, 15); 3106 u8 idest = BITS(12, 15);
3070 u8 rfis = BITS(16, 19); 3107 u8 rfis = BITS(16, 19);
3071 u8 rlast = BITS(0, 3); 3108 u8 rlast = BITS(0, 3);
@@ -3073,8 +3110,7 @@ mainswitch:
3073 if (ishi == 0)ishi = 0x20; 3110 if (ishi == 0)ishi = 0x20;
3074 state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000); 3111 state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000);
3075 break; 3112 break;
3076 } 3113 } else if (BIT (4)) {
3077 else if (BIT (4)) {
3078#ifdef MODE32 3114#ifdef MODE32
3079 if (state->is_v6 3115 if (state->is_v6
3080 && handle_v6_insn (state, instr)) 3116 && handle_v6_insn (state, instr))
@@ -3437,7 +3473,7 @@ mainswitch:
3437 3473
3438 case 0x7f: /* Load Byte, WriteBack, Pre Inc, Reg. */ 3474 case 0x7f: /* Load Byte, WriteBack, Pre Inc, Reg. */
3439 if (BIT (4)) { 3475 if (BIT (4)) {
3440 DEBUG("got unhandled special breakpoint\n"); 3476 LOG_DEBUG(Core_ARM11, "got unhandled special breakpoint");
3441 return 1; 3477 return 1;
3442 } 3478 }
3443 UNDEF_LSRBaseEQOffWb; 3479 UNDEF_LSRBaseEQOffWb;
@@ -3686,13 +3722,11 @@ mainswitch:
3686 3722
3687 /* Co-Processor Data Transfers. */ 3723 /* Co-Processor Data Transfers. */
3688 case 0xc4: 3724 case 0xc4:
3689 if ((instr & 0x0FF00FF0) == 0xC400B10) //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0 3725 if ((instr & 0x0FF00FF0) == 0xC400B10) { //vmov BIT(0-3), BIT(12-15), BIT(16-20), vmov d0, r0, r0
3690 {
3691 state->ExtReg[BITS(0, 3) << 1] = state->Reg[BITS(12, 15)]; 3726 state->ExtReg[BITS(0, 3) << 1] = state->Reg[BITS(12, 15)];
3692 state->ExtReg[(BITS(0, 3) << 1) + 1] = state->Reg[BITS(16, 20)]; 3727 state->ExtReg[(BITS(0, 3) << 1) + 1] = state->Reg[BITS(16, 20)];
3693 break; 3728 break;
3694 } 3729 } else if (state->is_v5) {
3695 else if (state->is_v5) {
3696 /* Reading from R15 is UNPREDICTABLE. */ 3730 /* Reading from R15 is UNPREDICTABLE. */
3697 if (BITS (12, 15) == 15 || BITS (16, 19) == 15) 3731 if (BITS (12, 15) == 15 || BITS (16, 19) == 15)
3698 ARMul_UndefInstr (state, instr); 3732 ARMul_UndefInstr (state, instr);
@@ -3712,22 +3746,18 @@ mainswitch:
3712 break; 3746 break;
3713 3747
3714 case 0xc5: 3748 case 0xc5:
3715 if ((instr & 0x00000FF0) == 0xB10) //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0 3749 if ((instr & 0x00000FF0) == 0xB10) { //vmov BIT(12-15), BIT(16-20), BIT(0-3) vmov r0, r0, d0
3716 {
3717 state->Reg[BITS(12, 15)] = state->ExtReg[BITS(0, 3) << 1]; 3750 state->Reg[BITS(12, 15)] = state->ExtReg[BITS(0, 3) << 1];
3718 state->Reg[BITS(16, 19)] = state->ExtReg[(BITS(0, 3) << 1) + 1]; 3751 state->Reg[BITS(16, 19)] = state->ExtReg[(BITS(0, 3) << 1) + 1];
3719 break; 3752 break;
3720 } 3753 } else if (state->is_v5) {
3721 else if (state->is_v5) {
3722 /* Writes to R15 are UNPREDICATABLE. */ 3754 /* Writes to R15 are UNPREDICATABLE. */
3723 if (DESTReg == 15 || LHSReg == 15) 3755 if (DESTReg == 15 || LHSReg == 15)
3724 ARMul_UndefInstr (state, instr); 3756 ARMul_UndefInstr (state, instr);
3725 /* Is access to the coprocessor allowed ? */ 3757 /* Is access to the coprocessor allowed ? */
3726 else if (!CP_ACCESS_ALLOWED(state, CPNum)) 3758 else if (!CP_ACCESS_ALLOWED(state, CPNum)) {
3727 {
3728 ARMul_UndefInstr(state, instr); 3759 ARMul_UndefInstr(state, instr);
3729 } 3760 } else {
3730 else {
3731 /* MRRC, ARMv5TE and up */ 3761 /* MRRC, ARMv5TE and up */
3732 ARMul_MRRC (state, instr, &DEST, &(state->Reg[LHSReg])); 3762 ARMul_MRRC (state, instr, &DEST, &(state->Reg[LHSReg]));
3733 break; 3763 break;
@@ -4565,7 +4595,7 @@ out:
4565#ifdef MODE32 4595#ifdef MODE32
4566 if (state->Bank > 0) { 4596 if (state->Bank > 0) {
4567 state->Cpsr = state->Spsr[state->Bank]; 4597 state->Cpsr = state->Spsr[state->Bank];
4568 //ARMul_CPSRAltered (state); 4598 ARMul_CPSRAltered (state);
4569 } 4599 }
4570#ifdef MODET 4600#ifdef MODET
4571 if (TFLAG) 4601 if (TFLAG)
@@ -5256,7 +5286,7 @@ L_ldm_s_makeabort:
5256 //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode 5286 //chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
5257 if (state->Mode != USER26MODE && state->Mode != USER32MODE ) { 5287 if (state->Mode != USER26MODE && state->Mode != USER32MODE ) {
5258 state->Cpsr = GETSPSR (state->Bank); 5288 state->Cpsr = GETSPSR (state->Bank);
5259 //ARMul_CPSRAltered (state); 5289 ARMul_CPSRAltered (state);
5260 } 5290 }
5261 5291
5262 WriteR15 (state, PC); 5292 WriteR15 (state, PC);
@@ -5641,30 +5671,9 @@ L_stm_s_takeabort:
5641 5671
5642 static int 5672 static int
5643 handle_v6_insn (ARMul_State * state, ARMword instr) { 5673 handle_v6_insn (ARMul_State * state, ARMword instr) {
5644 switch (BITS (20, 27)) { 5674 ARMword lhs, temp;
5645 //ichfly
5646 case 0x66: //UQSUB8
5647 if ((instr & 0x0FF00FF0) == 0x06600FF0) {
5648 u32 rd = (instr >> 12) & 0xF;
5649 u32 rm = (instr >> 16) & 0xF;
5650 u32 rn = (instr >> 0) & 0xF;
5651 u32 subfrom = state->Reg[rm];
5652 u32 tosub = state->Reg[rn];
5653 5675
5654 u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub)); 5676 switch (BITS (20, 27)) {
5655 if (b1 > (u8)(subfrom)) b1 = 0;
5656 u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8));
5657 if (b2 > (u8)(subfrom >> 8)) b2 = 0;
5658 u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16));
5659 if (b3 > (u8)(subfrom >> 16)) b3 = 0;
5660 u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24));
5661 if (b4 > (u8)(subfrom >> 24)) b4 = 0;
5662 state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24);
5663 return 1;
5664 } else {
5665 printf("UQSUB8 decoding fail %08X",instr);
5666 }
5667#if 0
5668 case 0x03: 5677 case 0x03:
5669 printf ("Unhandled v6 insn: ldr\n"); 5678 printf ("Unhandled v6 insn: ldr\n");
5670 break; 5679 break;
@@ -5678,9 +5687,43 @@ L_stm_s_takeabort:
5678 printf ("Unhandled v6 insn: smi\n"); 5687 printf ("Unhandled v6 insn: smi\n");
5679 break; 5688 break;
5680 case 0x18: 5689 case 0x18:
5690 if (BITS(4, 7) == 0x9) {
5691 /* strex */
5692 u32 l = LHSReg;
5693 u32 r = RHSReg;
5694 lhs = LHS;
5695
5696 bool enter = false;
5697
5698 if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true;
5699 //StoreWord(state, lhs, RHS)
5700 if (state->Aborted) {
5701 TAKEABORT;
5702 }
5703
5704 if (enter) {
5705 ARMul_StoreWordS(state, lhs, RHS);
5706 state->Reg[DESTReg] = 0;
5707 }
5708 else {
5709 state->Reg[DESTReg] = 1;
5710 }
5711
5712 return 1;
5713 }
5681 printf ("Unhandled v6 insn: strex\n"); 5714 printf ("Unhandled v6 insn: strex\n");
5682 break; 5715 break;
5683 case 0x19: 5716 case 0x19:
5717 /* ldrex */
5718 if (BITS(4, 7) == 0x9) {
5719 lhs = LHS;
5720
5721 state->currentexaddr = lhs;
5722 state->currentexval = ARMul_ReadWord(state, lhs);
5723
5724 LoadWord(state, instr, lhs);
5725 return 1;
5726 }
5684 printf ("Unhandled v6 insn: ldrex\n"); 5727 printf ("Unhandled v6 insn: ldrex\n");
5685 break; 5728 break;
5686 case 0x1a: 5729 case 0x1a:
@@ -5690,9 +5733,52 @@ L_stm_s_takeabort:
5690 printf ("Unhandled v6 insn: ldrexd\n"); 5733 printf ("Unhandled v6 insn: ldrexd\n");
5691 break; 5734 break;
5692 case 0x1c: 5735 case 0x1c:
5736 if (BITS(4, 7) == 0x9) {
5737 /* strexb */
5738 lhs = LHS;
5739
5740 bool enter = false;
5741
5742 if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true;
5743
5744 BUSUSEDINCPCN;
5745 if (state->Aborted) {
5746 TAKEABORT;
5747 }
5748
5749
5750 if (enter) {
5751 ARMul_StoreByte(state, lhs, RHS);
5752 state->Reg[DESTReg] = 0;
5753 }
5754 else {
5755 state->Reg[DESTReg] = 1;
5756 }
5757
5758 //printf("In %s, strexb not implemented\n", __FUNCTION__);
5759 UNDEF_LSRBPC;
5760 /* WRITESDEST (dest); */
5761 return 1;
5762 }
5693 printf ("Unhandled v6 insn: strexb\n"); 5763 printf ("Unhandled v6 insn: strexb\n");
5694 break; 5764 break;
5695 case 0x1d: 5765 case 0x1d:
5766 if ((BITS(4, 7)) == 0x9) {
5767 /* ldrexb */
5768 temp = LHS;
5769 LoadByte(state, instr, temp, LUNSIGNED);
5770
5771 state->currentexaddr = temp;
5772 state->currentexval = (u32)ARMul_ReadByte(state, temp);
5773
5774 //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]);
5775 //printf("ldrexb\n");
5776 //printf("instr is %x rm is %d\n", instr, BITS(16, 19));
5777 //exit(-1);
5778
5779 //printf("In %s, ldrexb not implemented\n", __FUNCTION__);
5780 return 1;
5781 }
5696 printf ("Unhandled v6 insn: ldrexb\n"); 5782 printf ("Unhandled v6 insn: ldrexb\n");
5697 break; 5783 break;
5698 case 0x1e: 5784 case 0x1e:
@@ -5713,10 +5799,8 @@ L_stm_s_takeabort:
5713 case 0x3f: 5799 case 0x3f:
5714 printf ("Unhandled v6 insn: rbit\n"); 5800 printf ("Unhandled v6 insn: rbit\n");
5715 break; 5801 break;
5716#endif
5717 case 0x61: 5802 case 0x61:
5718 if ((instr & 0xFF0) == 0xf70)//ssub16 5803 if ((instr & 0xFF0) == 0xf70) { //ssub16
5719 {
5720 u8 tar = BITS(12, 15); 5804 u8 tar = BITS(12, 15);
5721 u8 src1 = BITS(16, 19); 5805 u8 src1 = BITS(16, 19);
5722 u8 src2 = BITS(0, 3); 5806 u8 src2 = BITS(0, 3);
@@ -5724,23 +5808,20 @@ L_stm_s_takeabort:
5724 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5808 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5725 s16 b1 = (state->Reg[src2] & 0xFFFF); 5809 s16 b1 = (state->Reg[src2] & 0xFFFF);
5726 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5810 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
5727 state->Reg[tar] = (a1 - a2)&0xFFFF | (((b1 - b2)&0xFFFF)<< 0x10); 5811 state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2) & 0xFFFF) << 0x10);
5728 return 1; 5812 return 1;
5729 } 5813 } else if ((instr & 0xFF0) == 0xf10) { //sadd16
5730 else if ((instr & 0xFF0) == 0xf10)//sadd16 5814 const u8 rd_idx = BITS(12, 15);
5731 { 5815 const u8 rm_idx = BITS(0, 3);
5732 u8 tar = BITS(12, 15); 5816 const u8 rn_idx = BITS(16, 19);
5733 u8 src1 = BITS(16, 19); 5817 const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
5734 u8 src2 = BITS(0, 3); 5818 const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF);
5735 s16 a1 = (state->Reg[src1] & 0xFFFF); 5819 const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
5736 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5820 const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF);
5737 s16 b1 = (state->Reg[src2] & 0xFFFF); 5821
5738 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5822 state->Reg[rd_idx] = ((rn_lo + rm_lo) & 0xFFFF) | (((rn_hi + rm_hi) & 0xFFFF) << 16);
5739 state->Reg[tar] = (a1 + a2)&0xFFFF | (((b1 + b2)&0xFFFF)<< 0x10);
5740 return 1; 5823 return 1;
5741 } 5824 } else if ((instr & 0xFF0) == 0xf50) { //ssax
5742 else if ((instr & 0xFF0) == 0xf50)//ssax
5743 {
5744 u8 tar = BITS(12, 15); 5825 u8 tar = BITS(12, 15);
5745 u8 src1 = BITS(16, 19); 5826 u8 src1 = BITS(16, 19);
5746 u8 src2 = BITS(0, 3); 5827 u8 src2 = BITS(0, 3);
@@ -5748,11 +5829,9 @@ L_stm_s_takeabort:
5748 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5829 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5749 s16 b1 = (state->Reg[src2] & 0xFFFF); 5830 s16 b1 = (state->Reg[src2] & 0xFFFF);
5750 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5831 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
5751 state->Reg[tar] = (a1 - b2) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); 5832 state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10);
5752 return 1; 5833 return 1;
5753 } 5834 } else if ((instr & 0xFF0) == 0xf30) { //sasx
5754 else if ((instr & 0xFF0) == 0xf30)//sasx
5755 {
5756 u8 tar = BITS(12, 15); 5835 u8 tar = BITS(12, 15);
5757 u8 src1 = BITS(16, 19); 5836 u8 src1 = BITS(16, 19);
5758 u8 src2 = BITS(0, 3); 5837 u8 src2 = BITS(0, 3);
@@ -5760,127 +5839,445 @@ L_stm_s_takeabort:
5760 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5839 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5761 s16 b1 = (state->Reg[src2] & 0xFFFF); 5840 s16 b1 = (state->Reg[src2] & 0xFFFF);
5762 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5841 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
5763 state->Reg[tar] = (a2 - b1) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); 5842 state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10);
5764 return 1; 5843 return 1;
5765 } 5844 } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n");
5766 else printf ("Unhandled v6 insn: sadd/ssub\n");
5767 break; 5845 break;
5768 case 0x62: 5846 case 0x62: // QSUB16 and QADD16
5769 if ((instr & 0xFF0) == 0xf70)//QSUB16 5847 if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10) {
5770 { 5848 const u8 rd_idx = BITS(12, 15);
5771 u8 tar = BITS(12, 15); 5849 const u8 rn_idx = BITS(16, 19);
5772 u8 src1 = BITS(16, 19); 5850 const u8 rm_idx = BITS(0, 3);
5773 u8 src2 = BITS(0, 3); 5851 const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF);
5774 s16 a1 = (state->Reg[src1] & 0xFFFF); 5852 const s16 rm_hi = ((state->Reg[rm_idx] >> 0x10) & 0xFFFF);
5775 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5853 const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF);
5776 s16 b1 = (state->Reg[src2] & 0xFFFF); 5854 const s16 rn_hi = ((state->Reg[rn_idx] >> 0x10) & 0xFFFF);
5777 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5855
5778 s32 res1 = (a1 - b1); 5856 s32 lo_result;
5779 s32 res2 = (a2 - b2); 5857 s32 hi_result;
5780 if (res1 > 0x7FFF) res1 = 0x7FFF; 5858
5781 if (res2 > 0x7FFF) res2 = 0x7FFF; 5859 // QSUB16
5782 if (res1 < 0x7FFF) res1 = -0x8000; 5860 if ((instr & 0xFF0) == 0xf70) {
5783 if (res2 < 0x7FFF) res2 = -0x8000; 5861 lo_result = (rn_lo - rm_lo);
5784 state->Reg[tar] = (res1 & 0xFFFF) | ((res2 & 0xFFFF) << 0x10); 5862 hi_result = (rn_hi - rm_hi);
5785 return 1; 5863 }
5786 } 5864 else { // QADD16
5787 else if ((instr & 0xFF0) == 0xf10)//QADD16 5865 lo_result = (rn_lo + rm_lo);
5788 { 5866 hi_result = (rn_hi + rm_hi);
5789 u8 tar = BITS(12, 15); 5867 }
5790 u8 src1 = BITS(16, 19); 5868
5791 u8 src2 = BITS(0, 3); 5869 if (lo_result > 0x7FFF)
5792 s16 a1 = (state->Reg[src1] & 0xFFFF); 5870 lo_result = 0x7FFF;
5793 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5871 else if (lo_result < -0x8000)
5794 s16 b1 = (state->Reg[src2] & 0xFFFF); 5872 lo_result = -0x8000;
5795 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5873
5796 s32 res1 = (a1 + b1); 5874 if (hi_result > 0x7FFF)
5797 s32 res2 = (a2 + b2); 5875 hi_result = 0x7FFF;
5798 if (res1 > 0x7FFF) res1 = 0x7FFF; 5876 else if (hi_result < -0x8000)
5799 if (res2 > 0x7FFF) res2 = 0x7FFF; 5877 hi_result = -0x8000;
5800 if (res1 < 0x7FFF) res1 = -0x8000; 5878
5801 if (res2 < 0x7FFF) res2 = -0x8000; 5879 state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16);
5802 state->Reg[tar] = ((res1) & 0xFFFF) | (((res2) & 0xFFFF) << 0x10);
5803 return 1; 5880 return 1;
5881 } else {
5882 printf("Unhandled v6 insn: %08x", BITS(20, 27));
5804 } 5883 }
5805 else printf ("Unhandled v6 insn: qadd/qsub\n");
5806 break; 5884 break;
5807#if 0
5808 case 0x63: 5885 case 0x63:
5809 printf ("Unhandled v6 insn: shadd/shsub\n"); 5886 printf ("Unhandled v6 insn: shadd/shsub\n");
5810 break; 5887 break;
5811 case 0x65: 5888 case 0x65:
5812 printf ("Unhandled v6 insn: uadd/usub\n"); 5889 {
5890 u32 rd = (instr >> 12) & 0xF;
5891 u32 rn = (instr >> 16) & 0xF;
5892 u32 rm = (instr >> 0) & 0xF;
5893 u32 from = state->Reg[rn];
5894 u32 to = state->Reg[rm];
5895
5896 if ((instr & 0xFF0) == 0xF10 || (instr & 0xFF0) == 0xF70) { // UADD16/USUB16
5897 u32 h1, h2;
5898 state->Cpsr &= 0xfff0ffff;
5899 if ((instr & 0x0F0) == 0x070) { // USUB16
5900 h1 = ((u16)from - (u16)to);
5901 h2 = ((u16)(from >> 16) - (u16)(to >> 16));
5902 if (!(h1 & 0xffff0000)) state->Cpsr |= (3 << 16);
5903 if (!(h2 & 0xffff0000)) state->Cpsr |= (3 << 18);
5904 }
5905 else { // UADD16
5906 h1 = ((u16)from + (u16)to);
5907 h2 = ((u16)(from >> 16) + (u16)(to >> 16));
5908 if (h1 & 0xffff0000) state->Cpsr |= (3 << 16);
5909 if (h2 & 0xffff0000) state->Cpsr |= (3 << 18);
5910 }
5911 state->Reg[rd] = (u32)((h1 & 0xffff) | ((h2 & 0xffff) << 16));
5912 return 1;
5913 }
5914 else
5915 if ((instr & 0xFF0) == 0xF90 || (instr & 0xFF0) == 0xFF0) { // UADD8/USUB8
5916 u32 b1, b2, b3, b4;
5917 state->Cpsr &= 0xfff0ffff;
5918 if ((instr & 0x0F0) == 0x0F0) { // USUB8
5919 b1 = ((u8)from - (u8)to);
5920 b2 = ((u8)(from >> 8) - (u8)(to >> 8));
5921 b3 = ((u8)(from >> 16) - (u8)(to >> 16));
5922 b4 = ((u8)(from >> 24) - (u8)(to >> 24));
5923 if (!(b1 & 0xffffff00)) state->Cpsr |= (1 << 16);
5924 if (!(b2 & 0xffffff00)) state->Cpsr |= (1 << 17);
5925 if (!(b3 & 0xffffff00)) state->Cpsr |= (1 << 18);
5926 if (!(b4 & 0xffffff00)) state->Cpsr |= (1 << 19);
5927 }
5928 else { // UADD8
5929 b1 = ((u8)from + (u8)to);
5930 b2 = ((u8)(from >> 8) + (u8)(to >> 8));
5931 b3 = ((u8)(from >> 16) + (u8)(to >> 16));
5932 b4 = ((u8)(from >> 24) + (u8)(to >> 24));
5933 if (b1 & 0xffffff00) state->Cpsr |= (1 << 16);
5934 if (b2 & 0xffffff00) state->Cpsr |= (1 << 17);
5935 if (b3 & 0xffffff00) state->Cpsr |= (1 << 18);
5936 if (b4 & 0xffffff00) state->Cpsr |= (1 << 19);
5937 }
5938 state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24);
5939 return 1;
5940 }
5941 }
5942 printf("Unhandled v6 insn: uasx/usax\n");
5813 break; 5943 break;
5814 case 0x66: 5944 case 0x66:
5815 printf ("Unhandled v6 insn: uqadd/uqsub\n"); 5945 if ((instr & 0x0FF00FF0) == 0x06600FF0) { //uqsub8
5946 u32 rd = (instr >> 12) & 0xF;
5947 u32 rm = (instr >> 16) & 0xF;
5948 u32 rn = (instr >> 0) & 0xF;
5949 u32 subfrom = state->Reg[rm];
5950 u32 tosub = state->Reg[rn];
5951
5952 u8 b1 = (u8)((u8)(subfrom)-(u8)(tosub));
5953 if (b1 > (u8)(subfrom)) b1 = 0;
5954 u8 b2 = (u8)((u8)(subfrom >> 8) - (u8)(tosub >> 8));
5955 if (b2 > (u8)(subfrom >> 8)) b2 = 0;
5956 u8 b3 = (u8)((u8)(subfrom >> 16) - (u8)(tosub >> 16));
5957 if (b3 > (u8)(subfrom >> 16)) b3 = 0;
5958 u8 b4 = (u8)((u8)(subfrom >> 24) - (u8)(tosub >> 24));
5959 if (b4 > (u8)(subfrom >> 24)) b4 = 0;
5960 state->Reg[rd] = (u32)(b1 | b2 << 8 | b3 << 16 | b4 << 24);
5961 return 1;
5962 } else {
5963 printf ("Unhandled v6 insn: uqsub16\n");
5964 }
5816 break; 5965 break;
5817 case 0x67: 5966 case 0x67:
5818 printf ("Unhandled v6 insn: uhadd/uhsub\n"); 5967 printf ("Unhandled v6 insn: uhadd/uhsub\n");
5819 break; 5968 break;
5820 case 0x68: 5969 case 0x68:
5821 printf ("Unhandled v6 insn: pkh/sxtab/selsxtb\n"); 5970 {
5822 break; 5971 u32 rd = (instr >> 12) & 0xF;
5823#endif 5972 u32 rn = (instr >> 16) & 0xF;
5824 case 0x6c: 5973 u32 rm = (instr >> 0) & 0xF;
5825 if ((instr & 0xf03f0) == 0xf0070) //uxtb16 5974 u32 from = state->Reg[rn];
5826 { 5975 u32 to = state->Reg[rm];
5827 u8 src1 = BITS(0, 3); 5976 u32 cpsr = state->Cpsr;
5828 u8 tar = BITS(12, 15); 5977 if ((instr & 0xFF0) == 0xFB0) { // SEL
5829 u32 base = state->Reg[src1]; 5978 u32 result;
5830 u32 shamt = BITS(9,10)* 8; 5979 if (cpsr & (1 << 16))
5831 u32 in = ((base << (32 - shamt)) | (base >> shamt)); 5980 result = from & 0xff;
5832 state->Reg[tar] = in & 0x00FF00FF; 5981 else
5982 result = to & 0xff;
5983 if (cpsr & (1 << 17))
5984 result |= from & 0x0000ff00;
5985 else
5986 result |= to & 0x0000ff00;
5987 if (cpsr & (1 << 18))
5988 result |= from & 0x00ff0000;
5989 else
5990 result |= to & 0x00ff0000;
5991 if (cpsr & (1 << 19))
5992 result |= from & 0xff000000;
5993 else
5994 result |= to & 0xff000000;
5995 state->Reg[rd] = result;
5833 return 1; 5996 return 1;
5834 } 5997 }
5835 else 5998 }
5836 printf ("Unhandled v6 insn: uxtb16/uxtab16\n"); 5999 printf("Unhandled v6 insn: pkh/sxtab/selsxtb\n");
5837 break; 6000 break;
6001 case 0x6a: {
6002 ARMword Rm;
6003 int ror = -1;
6004
6005 switch (BITS(4, 11)) {
6006 case 0x07:
6007 ror = 0;
6008 break;
6009 case 0x47:
6010 ror = 8;
6011 break;
6012 case 0x87:
6013 ror = 16;
6014 break;
6015 case 0xc7:
6016 ror = 24;
6017 break;
6018
6019 case 0x01:
6020 case 0xf3:
6021 //ichfly
6022 //SSAT16
6023 {
6024 u8 tar = BITS(12, 15);
6025 u8 src = BITS(0, 3);
6026 u8 val = BITS(16, 19) + 1;
6027 s16 a1 = (state->Reg[src]);
6028 s16 a2 = (state->Reg[src] >> 0x10);
6029 s16 min = (s16)(0x8000 >> (16 - val));
6030 s16 max = 0x7FFF >> (16 - val);
6031 if (min > a1) a1 = min;
6032 if (max < a1) a1 = max;
6033 if (min > a2) a2 = min;
6034 if (max < a2) a2 = max;
6035 u32 temp2 = ((u32)(a2)) << 0x10;
6036 state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
6037 }
6038
6039 return 1;
6040 default:
6041 break;
6042 }
6043
6044 if (ror == -1) {
6045 if (BITS(4, 6) == 0x7) {
6046 printf("Unhandled v6 insn: ssat\n");
6047 return 0;
6048 }
6049 break;
6050 }
6051
6052 Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF);
6053 if (Rm & 0x80)
6054 Rm |= 0xffffff00;
6055
6056 if (BITS(16, 19) == 0xf)
6057 /* SXTB */
6058 state->Reg[BITS(12, 15)] = Rm;
6059 else
6060 /* SXTAB */
6061 state->Reg[BITS(12, 15)] += Rm;
6062
6063 return 1;
6064 }
6065 case 0x6b: {
6066 ARMword Rm;
6067 int ror = -1;
6068
6069 switch (BITS(4, 11)) {
6070 case 0x07:
6071 ror = 0;
6072 break;
6073 case 0x47:
6074 ror = 8;
6075 break;
6076 case 0x87:
6077 ror = 16;
6078 break;
6079 case 0xc7:
6080 ror = 24;
6081 break;
6082
6083 case 0xf3:
6084 DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24);
6085 return 1;
6086 case 0xfb:
6087 DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8);
6088 return 1;
6089 default:
6090 break;
6091 }
6092
6093 if (ror == -1)
6094 break;
6095
6096 Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF);
6097 if (Rm & 0x8000)
6098 Rm |= 0xffff0000;
6099
6100 if (BITS(16, 19) == 0xf)
6101 /* SXTH */
6102 state->Reg[BITS(12, 15)] = Rm;
6103 else
6104 /* SXTAH */
6105 state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm;
6106
6107 return 1;
6108 }
6109 case 0x6c: // UXTB16 and UXTAB16
6110 {
6111 const u8 rm_idx = BITS(0, 3);
6112 const u8 rn_idx = BITS(16, 19);
6113 const u8 rd_idx = BITS(12, 15);
6114 const u32 rm_val = state->Reg[rm_idx];
6115 const u32 rn_val = state->Reg[rn_idx];
6116 const u32 rotation = BITS(10, 11) * 8;
6117 const u32 rotated_rm = ((rm_val << (32 - rotation)) | (rm_val >> rotation));
6118
6119 // UXTB16
6120 if ((instr & 0xf03f0) == 0xf0070) {
6121 state->Reg[rd_idx] = rotated_rm & 0x00FF00FF;
6122 }
6123 else { // UXTAB16
6124 const u8 lo_rotated = (rotated_rm & 0xFF);
6125 const u16 lo_result = (rn_val & 0xFFFF) + (u16)lo_rotated;
6126
6127 const u8 hi_rotated = (rotated_rm >> 16) & 0xFF;
6128 const u16 hi_result = (rn_val >> 16) + (u16)hi_rotated;
6129
6130 state->Reg[rd_idx] = ((hi_result << 16) | (lo_result & 0xFFFF));
6131 }
6132
6133 return 1;
6134 }
6135 break;
6136 case 0x6e: {
6137 ARMword Rm;
6138 int ror = -1;
6139
6140 switch (BITS(4, 11)) {
6141 case 0x07:
6142 ror = 0;
6143 break;
6144 case 0x47:
6145 ror = 8;
6146 break;
6147 case 0x87:
6148 ror = 16;
6149 break;
6150 case 0xc7:
6151 ror = 24;
6152 break;
6153
6154 case 0x01:
6155 case 0xf3:
6156 //ichfly
6157 //USAT16
6158 {
6159 u8 tar = BITS(12, 15);
6160 u8 src = BITS(0, 3);
6161 u8 val = BITS(16, 19);
6162 s16 a1 = (state->Reg[src]);
6163 s16 a2 = (state->Reg[src] >> 0x10);
6164 s16 max = 0xFFFF >> (16 - val);
6165 if (max < a1) a1 = max;
6166 if (max < a2) a2 = max;
6167 u32 temp2 = ((u32)(a2)) << 0x10;
6168 state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
6169 }
6170 return 1;
6171 default:
6172 break;
6173 }
6174
6175 if (ror == -1) {
6176 if (BITS(4, 6) == 0x7) {
6177 printf("Unhandled v6 insn: usat\n");
6178 return 0;
6179 }
6180 break;
6181 }
6182
6183 Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF);
6184
6185 if (BITS(16, 19) == 0xf)
6186 /* UXTB */
6187 state->Reg[BITS(12, 15)] = Rm;
6188 else
6189 /* UXTAB */
6190 state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm;
6191
6192 return 1;
6193 }
6194
6195 case 0x6f: {
6196 ARMword Rm;
6197 int ror = -1;
6198
6199 switch (BITS(4, 11)) {
6200 case 0x07:
6201 ror = 0;
6202 break;
6203 case 0x47:
6204 ror = 8;
6205 break;
6206 case 0x87:
6207 ror = 16;
6208 break;
6209 case 0xc7:
6210 ror = 24;
6211 break;
6212
6213 case 0xfb:
6214 printf("Unhandled v6 insn: revsh\n");
6215 return 0;
6216 default:
6217 break;
6218 }
6219
6220 if (ror == -1)
6221 break;
6222
6223 Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF);
6224
6225 /* UXT */
6226 /* state->Reg[BITS (12, 15)] = Rm; */
6227 /* dyf add */
6228 if (BITS(16, 19) == 0xf) {
6229 state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF;
6230 }
6231 else {
6232 /* UXTAH */
6233 /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */
6234 // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)]
6235 // , Rm, BITS(10, 11));
6236 // printf("icounter is %lld\n", state->NumInstrs);
6237 state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm;
6238 // printf("rd is %x\n", state->Reg[BITS (12, 15)]);
6239 // exit(-1);
6240 }
6241
6242 return 1;
6243 }
5838 case 0x70: 6244 case 0x70:
5839 if ((instr & 0xf0d0) == 0xf010)//smuad //ichfly 6245 // ichfly
5840 { 6246 // SMUAD, SMUSD, SMLAD
5841 u8 tar = BITS(16, 19); 6247 if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 || (instr & 0xd0) == 0x10) {
5842 u8 src1 = BITS(0, 3); 6248 const u8 rd_idx = BITS(16, 19);
5843 u8 src2 = BITS(8, 11); 6249 const u8 rn_idx = BITS(0, 3);
5844 u8 swap = BIT(5); 6250 const u8 rm_idx = BITS(8, 11);
5845 s16 a1 = (state->Reg[src1] & 0xFFFF); 6251 const bool do_swap = (BIT(5) == 1);
5846 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 6252
5847 s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); 6253 u32 rm_val = state->Reg[rm_idx];
5848 s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); 6254 const u32 rn_val = state->Reg[rn_idx];
5849 state->Reg[tar] = a1*a2 + b1*b2; 6255
5850 return 1; 6256 if (do_swap)
5851 6257 rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16));
5852 } 6258
5853 else if ((instr & 0xf0d0) == 0xf050)//smusd 6259 const s16 rm_lo = (rm_val & 0xFFFF);
5854 { 6260 const s16 rm_hi = ((rm_val >> 16) & 0xFFFF);
5855 u8 tar = BITS(16, 19); 6261 const s16 rn_lo = (rn_val & 0xFFFF);
5856 u8 src1 = BITS(0, 3); 6262 const s16 rn_hi = ((rn_val >> 16) & 0xFFFF);
5857 u8 src2 = BITS(8, 11); 6263
5858 u8 swap = BIT(5); 6264 // SMUAD
5859 s16 a1 = (state->Reg[src1] & 0xFFFF); 6265 if ((instr & 0xf0d0) == 0xf010) {
5860 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 6266 state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi);
5861 s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); 6267 }
5862 s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); 6268 // SMUSD
5863 state->Reg[tar] = a1*a2 - b1*b2; 6269 else if ((instr & 0xf0d0) == 0xf050) {
5864 return 1; 6270 state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi);
5865 } 6271 }
5866 else if ((instr & 0xd0) == 0x10)//smlad 6272 // SMLAD
5867 { 6273 else {
5868 u8 tar = BITS(16, 19); 6274 const u8 ra_idx = BITS(12, 15);
5869 u8 src1 = BITS(0, 3); 6275 state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx];
5870 u8 src2 = BITS(8, 11); 6276 }
5871 u8 src3 = BITS(12, 15);
5872 u8 swap = BIT(5);
5873
5874 u32 a3 = state->Reg[src3];
5875
5876 s16 a1 = (state->Reg[src1] & 0xFFFF);
5877 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5878 s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF);
5879 s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF);
5880 state->Reg[tar] = a1*a2 + b1*b2 + a3;
5881 return 1; 6277 return 1;
6278 } else {
6279 printf ("Unhandled v6 insn: smlsd\n");
5882 } 6280 }
5883 else printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n");
5884 break; 6281 break;
5885 case 0x74: 6282 case 0x74:
5886 printf ("Unhandled v6 insn: smlald/smlsld\n"); 6283 printf ("Unhandled v6 insn: smlald/smlsld\n");
@@ -5891,332 +6288,18 @@ L_stm_s_takeabort:
5891 case 0x78: 6288 case 0x78:
5892 printf ("Unhandled v6 insn: usad/usada8\n"); 6289 printf ("Unhandled v6 insn: usad/usada8\n");
5893 break; 6290 break;
5894#if 0
5895 case 0x7a: 6291 case 0x7a:
5896 printf ("Unhandled v6 insn: usbfx\n"); 6292 printf ("Unhandled v6 insn: usbfx\n");
5897 break; 6293 break;
5898 case 0x7c: 6294 case 0x7c:
5899 printf ("Unhandled v6 insn: bfc/bfi\n"); 6295 printf ("Unhandled v6 insn: bfc/bfi\n");
5900 break; 6296 break;
5901#endif
5902
5903
5904 /* add new instr for arm v6. */
5905 ARMword lhs, temp;
5906 case 0x18: { /* ORR reg */
5907 /* dyf add armv6 instr strex 2010.9.17 */
5908 if (BITS (4, 7) == 0x9) {
5909 u32 l = LHSReg;
5910 u32 r = RHSReg;
5911 lhs = LHS;
5912
5913 bool enter = false;
5914
5915 if (state->currentexval == (u32)ARMul_ReadWord(state, state->currentexaddr))enter = true;
5916 ARMul_StoreWordS(state, lhs, RHS);
5917 //StoreWord(state, lhs, RHS)
5918 if (state->Aborted) {
5919 TAKEABORT;
5920 }
5921
5922 if (enter) {
5923 state->Reg[DESTReg] = 0;
5924 } else {
5925 state->Reg[DESTReg] = 1;
5926 }
5927
5928 return 1;
5929 }
5930 break;
5931 }
5932
5933 case 0x19: { /* orrs reg */
5934 /* dyf add armv6 instr ldrex */
5935 if (BITS (4, 7) == 0x9) {
5936 lhs = LHS;
5937
5938 state->currentexaddr = lhs;
5939 state->currentexval = ARMul_ReadWord(state, lhs);
5940
5941 LoadWord (state, instr, lhs);
5942 return 1;
5943 }
5944 break;
5945 }
5946
5947 case 0x1c: { /* BIC reg */
5948 /* dyf add for STREXB */
5949 if (BITS (4, 7) == 0x9) {
5950 lhs = LHS;
5951
5952 bool enter = false;
5953
5954 if (state->currentexval == (u32)ARMul_ReadByte(state, state->currentexaddr))enter = true;
5955
5956 ARMul_StoreByte (state, lhs, RHS);
5957 BUSUSEDINCPCN;
5958 if (state->Aborted) {
5959 TAKEABORT;
5960 }
5961
5962
5963 if (enter) {
5964 state->Reg[DESTReg] = 0;
5965 } else {
5966 state->Reg[DESTReg] = 1;
5967 }
5968
5969 //printf("In %s, strexb not implemented\n", __FUNCTION__);
5970 UNDEF_LSRBPC;
5971 /* WRITESDEST (dest); */
5972 return 1;
5973 }
5974 break;
5975 }
5976
5977 case 0x1d: { /* BICS reg */
5978 if ((BITS (4, 7)) == 0x9) {
5979 /* ldrexb */
5980 temp = LHS;
5981 LoadByte (state, instr, temp, LUNSIGNED);
5982
5983 state->currentexaddr = temp;
5984 state->currentexval = (u32)ARMul_ReadByte(state, temp);
5985
5986 //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]);
5987 //printf("ldrexb\n");
5988 //printf("instr is %x rm is %d\n", instr, BITS(16, 19));
5989 //exit(-1);
5990
5991 //printf("In %s, ldrexb not implemented\n", __FUNCTION__);
5992 return 1;
5993 }
5994 break;
5995 }
5996 /* add end */
5997
5998 case 0x6a: {
5999 ARMword Rm;
6000 int ror = -1;
6001
6002 switch (BITS (4, 11)) {
6003 case 0x07:
6004 ror = 0;
6005 break;
6006 case 0x47:
6007 ror = 8;
6008 break;
6009 case 0x87:
6010 ror = 16;
6011 break;
6012 case 0xc7:
6013 ror = 24;
6014 break;
6015
6016 case 0x01:
6017 case 0xf3:
6018 //ichfly
6019 //SSAT16
6020 {
6021 u8 tar = BITS(12,15);
6022 u8 src = BITS(0, 3);
6023 u8 val = BITS(16, 19) + 1;
6024 s16 a1 = (state->Reg[src]);
6025 s16 a2 = (state->Reg[src] >> 0x10);
6026 s16 min = (s16)(0x8000) >> (16 - val);
6027 s16 max = 0x7FFF >> (16 - val);
6028 if (min > a1) a1 = min;
6029 if (max < a1) a1 = max;
6030 if (min > a2) a2 = min;
6031 if (max < a2) a2 = max;
6032 u32 temp2 = ((u32)(a2)) << 0x10;
6033 state->Reg[tar] = (a1&0xFFFF) | (temp2);
6034 }
6035
6036 return 1;
6037 default:
6038 break;
6039 }
6040
6041 if (ror == -1) {
6042 if (BITS (4, 6) == 0x7) {
6043 printf ("Unhandled v6 insn: ssat\n");
6044 return 0;
6045 }
6046 break;
6047 }
6048
6049 Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
6050 if (Rm & 0x80)
6051 Rm |= 0xffffff00;
6052
6053 if (BITS (16, 19) == 0xf)
6054 /* SXTB */
6055 state->Reg[BITS (12, 15)] = Rm;
6056 else
6057 /* SXTAB */
6058 state->Reg[BITS (12, 15)] += Rm;
6059 }
6060 return 1;
6061
6062 case 0x6b: {
6063 ARMword Rm;
6064 int ror = -1;
6065
6066 switch (BITS (4, 11)) {
6067 case 0x07:
6068 ror = 0;
6069 break;
6070 case 0x47:
6071 ror = 8;
6072 break;
6073 case 0x87:
6074 ror = 16;
6075 break;
6076 case 0xc7:
6077 ror = 24;
6078 break;
6079
6080 case 0xf3:
6081 DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24);
6082 return 1;
6083 case 0xfb:
6084 DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8);
6085 return 1;
6086 default:
6087 break;
6088 }
6089
6090 if (ror == -1)
6091 break;
6092
6093 Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
6094 if (Rm & 0x8000)
6095 Rm |= 0xffff0000;
6096
6097 if (BITS (16, 19) == 0xf)
6098 /* SXTH */
6099 state->Reg[BITS (12, 15)] = Rm;
6100 else
6101 /* SXTAH */
6102 state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
6103 }
6104 return 1;
6105
6106 case 0x6e: {
6107 ARMword Rm;
6108 int ror = -1;
6109
6110 switch (BITS (4, 11)) {
6111 case 0x07:
6112 ror = 0;
6113 break;
6114 case 0x47:
6115 ror = 8;
6116 break;
6117 case 0x87:
6118 ror = 16;
6119 break;
6120 case 0xc7:
6121 ror = 24;
6122 break;
6123
6124 case 0x01:
6125 case 0xf3:
6126 //ichfly
6127 //USAT16
6128 {
6129 u8 tar = BITS(12, 15);
6130 u8 src = BITS(0, 3);
6131 u8 val = BITS(16, 19);
6132 s16 a1 = (state->Reg[src]);
6133 s16 a2 = (state->Reg[src] >> 0x10);
6134 s16 max = 0xFFFF >> (16 - val);
6135 if (max < a1) a1 = max;
6136 if (max < a2) a2 = max;
6137 u32 temp2 = ((u32)(a2)) << 0x10;
6138 state->Reg[tar] = (a1 & 0xFFFF) | (temp2);
6139 }
6140 return 1;
6141 default:
6142 break;
6143 }
6144
6145 if (ror == -1) {
6146 if (BITS (4, 6) == 0x7) {
6147 printf ("Unhandled v6 insn: usat\n");
6148 return 0;
6149 }
6150 break;
6151 }
6152
6153 Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFF);
6154
6155 if (BITS (16, 19) == 0xf)
6156 /* UXTB */
6157 state->Reg[BITS (12, 15)] = Rm;
6158 else
6159 /* UXTAB */
6160 state->Reg[BITS (12, 15)] = state->Reg[BITS (16, 19)] + Rm;
6161 }
6162 return 1;
6163
6164 case 0x6f: {
6165 ARMword Rm;
6166 int ror = -1;
6167
6168 switch (BITS (4, 11)) {
6169 case 0x07:
6170 ror = 0;
6171 break;
6172 case 0x47:
6173 ror = 8;
6174 break;
6175 case 0x87:
6176 ror = 16;
6177 break;
6178 case 0xc7:
6179 ror = 24;
6180 break;
6181
6182 case 0xfb:
6183 printf ("Unhandled v6 insn: revsh\n");
6184 return 0;
6185 default:
6186 break;
6187 }
6188
6189 if (ror == -1)
6190 break;
6191
6192 Rm = ((state->Reg[BITS (0, 3)] >> ror) & 0xFFFF);
6193
6194 /* UXT */
6195 /* state->Reg[BITS (12, 15)] = Rm; */
6196 /* dyf add */
6197 if (BITS (16, 19) == 0xf) {
6198 state->Reg[BITS (12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF;
6199 } else {
6200 /* UXTAH */
6201 /* state->Reg[BITS (12, 15)] = state->Reg [BITS (16, 19)] + Rm; */
6202// printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)]
6203// , Rm, BITS(10, 11));
6204// printf("icounter is %lld\n", state->NumInstrs);
6205 state->Reg[BITS (12, 15)] = (state->Reg[BITS (16, 19)] >> (8 * (BITS(10, 11)))) + Rm;
6206// printf("rd is %x\n", state->Reg[BITS (12, 15)]);
6207// exit(-1);
6208 }
6209 }
6210 return 1;
6211
6212#if 0
6213 case 0x84: 6297 case 0x84:
6214 printf ("Unhandled v6 insn: srs\n"); 6298 printf ("Unhandled v6 insn: srs\n");
6215 break; 6299 break;
6216#endif
6217 default: 6300 default:
6218 break; 6301 break;
6219 } 6302 }
6220 printf("Unhandled v6 insn: UNKNOWN: %08x %08X\n", instr, BITS(20, 27)); 6303 printf("Unhandled v6 insn: UNKNOWN: %08x %08X\n", instr, BITS(20, 27));
6221 return 0; 6304 return 0;
6222 } 6305 } \ No newline at end of file
diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp
index 2568b93ef..30519f216 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)
665 //if (!CP_ACCESS_ALLOWED (state, CPNum)) { 665 //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
666 if (!state->MCR[CPNum]) { 666 if (!state->MCR[CPNum]) {
667 //chy 2004-07-19 should fix in the future ????!!!! 667 //chy 2004-07-19 should fix in the future ????!!!!
668 DEBUG("SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x\n",CPNum, source); 668 LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x",CPNum, source);
669 ARMul_UndefInstr (state, instr); 669 ARMul_UndefInstr (state, instr);
670 return; 670 return;
671 } 671 }
@@ -690,7 +690,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
690 } 690 }
691 691
692 if (cpab == ARMul_CANT) { 692 if (cpab == ARMul_CANT) {
693 DEBUG("SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x\n", instr, CPNum, source); //ichfly todo 693 LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x", instr, CPNum, source); //ichfly todo
694 //ARMul_Abort (state, ARMul_UndefinedInstrV); 694 //ARMul_Abort (state, ARMul_UndefinedInstrV);
695 } else { 695 } else {
696 BUSUSEDINCPCN; 696 BUSUSEDINCPCN;
@@ -762,7 +762,7 @@ ARMword ARMul_MRC (ARMul_State * state, ARMword instr)
762 //if (!CP_ACCESS_ALLOWED (state, CPNum)) { 762 //if (!CP_ACCESS_ALLOWED (state, CPNum)) {
763 if (!state->MRC[CPNum]) { 763 if (!state->MRC[CPNum]) {
764 //chy 2004-07-19 should fix in the future????!!!! 764 //chy 2004-07-19 should fix in the future????!!!!
765 DEBUG("SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x\n", CPNum, instr); 765 LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x", CPNum, instr);
766 ARMul_UndefInstr (state, instr); 766 ARMul_UndefInstr (state, instr);
767 return -1; 767 return -1;
768 } 768 }
@@ -865,7 +865,7 @@ void
865ARMul_UndefInstr (ARMul_State * state, ARMword instr) 865ARMul_UndefInstr (ARMul_State * state, ARMword instr)
866{ 866{
867 std::string disasm = ARM_Disasm::Disassemble(state->pc, instr); 867 std::string disasm = ARM_Disasm::Disassemble(state->pc, instr);
868 ERROR_LOG(ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr); 868 LOG_ERROR(Core_ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
869 ARMul_Abort (state, ARMul_UndefinedInstrV); 869 ARMul_Abort (state, ARMul_UndefinedInstrV);
870} 870}
871 871
diff --git a/src/core/arm/interpreter/thumbemu.cpp b/src/core/arm/interpreter/thumbemu.cpp
index f7f11f714..9cf80672d 100644
--- a/src/core/arm/interpreter/thumbemu.cpp
+++ b/src/core/arm/interpreter/thumbemu.cpp
@@ -467,7 +467,7 @@ ARMul_ThumbDecode (
467 (state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC; 467 (state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC;
468 state->Reg[14] = (tmp | 1); 468 state->Reg[14] = (tmp | 1);
469 CLEART; 469 CLEART;
470 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); 470 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);
471 valid = t_branch; 471 valid = t_branch;
472 FLUSHPIPE; 472 FLUSHPIPE;
473 } 473 }
diff --git a/src/core/arm/skyeye_common/armcpu.h b/src/core/arm/skyeye_common/armcpu.h
index 3a029f0e7..2b756c5bc 100644
--- a/src/core/arm/skyeye_common/armcpu.h
+++ b/src/core/arm/skyeye_common/armcpu.h
@@ -24,8 +24,6 @@
24#include <stddef.h> 24#include <stddef.h>
25#include <stdio.h> 25#include <stdio.h>
26 26
27#include "common/thread.h"
28
29#include "core/arm/skyeye_common/armdefs.h" 27#include "core/arm/skyeye_common/armdefs.h"
30 28
31typedef struct ARM_CPU_State_s { 29typedef struct ARM_CPU_State_s {
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h
index 8e71948c6..28a4a0db4 100644
--- a/src/core/arm/skyeye_common/armdefs.h
+++ b/src/core/arm/skyeye_common/armdefs.h
@@ -18,38 +18,26 @@
18#ifndef _ARMDEFS_H_ 18#ifndef _ARMDEFS_H_
19#define _ARMDEFS_H_ 19#define _ARMDEFS_H_
20 20
21#include <stdio.h> 21#include <cerrno>
22#include <stdlib.h> 22#include <csignal>
23#include <errno.h> 23#include <cstdio>
24 24#include <cstdlib>
25#include "common/platform.h" 25#include <cstring>
26 26#include <fcntl.h>
27//teawater add for arm2x86 2005.02.14------------------------------------------- 27#include <sys/stat.h>
28// koodailar remove it for mingw 2005.12.18---------------- 28#include <sys/types.h>
29//anthonylee modify it for portable 2007.01.30
30//#include "portable/mman.h"
31 29
32#include "arm_regformat.h" 30#include "arm_regformat.h"
31#include "common/common_types.h"
33#include "common/platform.h" 32#include "common/platform.h"
33#include "core/arm/skyeye_common/armmmu.h"
34#include "core/arm/skyeye_common/skyeye_defs.h" 34#include "core/arm/skyeye_common/skyeye_defs.h"
35 35
36//AJ2D--------------------------------------------------------------------------
37
38//teawater add for arm2x86 2005.07.03-------------------------------------------
39
40#include <sys/types.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#if EMU_PLATFORM == PLATFORM_LINUX 36#if EMU_PLATFORM == PLATFORM_LINUX
37#include <sys/time.h>
45#include <unistd.h> 38#include <unistd.h>
46#endif 39#endif
47#include <errno.h>
48#include <sys/stat.h>
49#include <fcntl.h>
50 40
51//#include <memory_space.h>
52//AJ2D--------------------------------------------------------------------------
53#if 0 41#if 0
54#if 0 42#if 0
55#define DIFF_STATE 1 43#define DIFF_STATE 1
@@ -70,25 +58,8 @@
70#define LOWHIGH 1 58#define LOWHIGH 1
71#define HIGHLOW 2 59#define HIGHLOW 2
72 60
73//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
74#include <signal.h>
75
76#include "common/platform.h"
77
78#if EMU_PLATFORM == PLATFORM_LINUX
79#include <sys/time.h>
80#endif
81
82//#define DBCT_TEST_SPEED 61//#define DBCT_TEST_SPEED
83#define DBCT_TEST_SPEED_SEC 10 62#define DBCT_TEST_SPEED_SEC 10
84//AJ2D--------------------------------------------------------------------------
85
86//teawater add compile switch for DBCT GDB RSP function 2005.10.21--------------
87//#define DBCT_GDBRSP
88//AJ2D--------------------------------------------------------------------------
89
90//#include <skyeye_defs.h>
91//#include <skyeye_types.h>
92 63
93#define ARM_BYTE_TYPE 0 64#define ARM_BYTE_TYPE 0
94#define ARM_HALFWORD_TYPE 1 65#define ARM_HALFWORD_TYPE 1
@@ -103,71 +74,34 @@
103typedef char *VoidStar; 74typedef char *VoidStar;
104#endif 75#endif
105 76
106typedef unsigned long long ARMdword; /* must be 64 bits wide */ 77typedef u64 ARMdword; // must be 64 bits wide
107typedef unsigned int ARMword; /* must be 32 bits wide */ 78typedef u32 ARMword; // must be 32 bits wide
108typedef unsigned char ARMbyte; /* must be 8 bits wide */ 79typedef u16 ARMhword; // must be 16 bits wide
109typedef unsigned short ARMhword; /* must be 16 bits wide */ 80typedef u8 ARMbyte; // must be 8 bits wide
110typedef struct ARMul_State ARMul_State; 81typedef struct ARMul_State ARMul_State;
111typedef struct ARMul_io ARMul_io; 82typedef struct ARMul_io ARMul_io;
112typedef struct ARMul_Energy ARMul_Energy; 83typedef struct ARMul_Energy ARMul_Energy;
113 84
114//teawater add for arm2x86 2005.06.24-------------------------------------------
115#include <stdint.h>
116//AJ2D--------------------------------------------------------------------------
117/*
118//chy 2005-05-11
119#ifndef __CYGWIN__
120//teawater add for arm2x86 2005.02.14-------------------------------------------
121typedef unsigned char uint8_t;
122typedef unsigned short uint16_t;
123typedef unsigned int u32;
124#if defined (__x86_64__)
125typedef unsigned long uint64_t;
126#else
127typedef unsigned long long uint64_t;
128#endif
129////AJ2D--------------------------------------------------------------------------
130#endif
131*/
132 85
133#include "core/arm/skyeye_common/armmmu.h" 86typedef unsigned ARMul_CPInits(ARMul_State* state);
134//#include "lcd/skyeye_lcd.h" 87typedef unsigned ARMul_CPExits(ARMul_State* state);
135 88typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
136 89typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
137//#include "skyeye.h" 90typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
138//#include "skyeye_device.h" 91typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
139//#include "net/skyeye_net.h" 92typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
140//#include "skyeye_config.h" 93typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
141 94typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr);
142 95typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value);
143typedef unsigned ARMul_CPInits (ARMul_State * state); 96typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value);
144typedef unsigned ARMul_CPExits (ARMul_State * state);
145typedef unsigned ARMul_LDCs (ARMul_State * state, unsigned type,
146 ARMword instr, ARMword value);
147typedef unsigned ARMul_STCs (ARMul_State * state, unsigned type,
148 ARMword instr, ARMword * value);
149typedef unsigned ARMul_MRCs (ARMul_State * state, unsigned type,
150 ARMword instr, ARMword * value);
151typedef unsigned ARMul_MCRs (ARMul_State * state, unsigned type,
152 ARMword instr, ARMword value);
153typedef unsigned ARMul_MRRCs (ARMul_State * state, unsigned type,
154 ARMword instr, ARMword * value1, ARMword * value2);
155typedef unsigned ARMul_MCRRs (ARMul_State * state, unsigned type,
156 ARMword instr, ARMword value1, ARMword value2);
157typedef unsigned ARMul_CDPs (ARMul_State * state, unsigned type,
158 ARMword instr);
159typedef unsigned ARMul_CPReads (ARMul_State * state, unsigned reg,
160 ARMword * value);
161typedef unsigned ARMul_CPWrites (ARMul_State * state, unsigned reg,
162 ARMword value);
163 97
164 98
165//added by ksh,2004-3-5 99//added by ksh,2004-3-5
166struct ARMul_io 100struct ARMul_io
167{ 101{
168 ARMword *instr; //to display the current interrupt state 102 ARMword *instr; // to display the current interrupt state
169 ARMword *net_flag; //to judge if network is enabled 103 ARMword *net_flag; // to judge if network is enabled
170 ARMword *net_int; //netcard interrupt 104 ARMword *net_int; // netcard interrupt
171 105
172 //ywc,2004-04-01 106 //ywc,2004-04-01
173 ARMword *ts_int; 107 ARMword *ts_int;
@@ -180,17 +114,17 @@ struct ARMul_io
180/* added by ksh,2004-11-26,some energy profiling */ 114/* added by ksh,2004-11-26,some energy profiling */
181struct ARMul_Energy 115struct ARMul_Energy
182{ 116{
183 int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */ 117 int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
184 int enable_func_energy; /* <tktan> BUG200105181702 */ 118 int enable_func_energy; /* <tktan> BUG200105181702 */
185 char *func_energy; 119 char *func_energy;
186 int func_display; /* <tktan> BUG200103311509 : for function call display */ 120 int func_display; /* <tktan> BUG200103311509 : for function call display */
187 int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */ 121 int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */
188 char *start_func; /* <tktan> BUG200104191428 */ 122 char *start_func; /* <tktan> BUG200104191428 */
189 123
190 FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */ 124 FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
191 long long tcycle, pcycle; 125 long long tcycle, pcycle;
192 float t_energy; 126 float t_energy;
193 void *cur_task; /* <tktan> BUG200103291737 */ 127 void *cur_task; /* <tktan> BUG200103291737 */
194 long long t_mem_cycle, t_idle_cycle, t_uart_cycle; 128 long long t_mem_cycle, t_idle_cycle, t_uart_cycle;
195 long long p_mem_cycle, p_idle_cycle, p_uart_cycle; 129 long long p_mem_cycle, p_idle_cycle, p_uart_cycle;
196 long long p_io_update_tcycle; 130 long long p_io_update_tcycle;
@@ -203,13 +137,12 @@ struct ARMul_Energy
203 137
204typedef struct mem_bank 138typedef struct mem_bank
205{ 139{
206 ARMword (*read_byte) (ARMul_State * state, ARMword addr); 140 ARMword (*read_byte) (ARMul_State* state, ARMword addr);
207 void (*write_byte) (ARMul_State * state, ARMword addr, ARMword data); 141 void (*write_byte) (ARMul_State* state, ARMword addr, ARMword data);
208 ARMword (*read_halfword) (ARMul_State * state, ARMword addr); 142 ARMword (*read_halfword) (ARMul_State* state, ARMword addr);
209 void (*write_halfword) (ARMul_State * state, ARMword addr, 143 void (*write_halfword) (ARMul_State* state, ARMword addr, ARMword data);
210 ARMword data); 144 ARMword (*read_word) (ARMul_State* state, ARMword addr);
211 ARMword (*read_word) (ARMul_State * state, ARMword addr); 145 void (*write_word) (ARMul_State* state, ARMword addr, ARMword data);
212 void (*write_word) (ARMul_State * state, ARMword addr, ARMword data);
213 unsigned int addr, len; 146 unsigned int addr, len;
214 char filename[MAX_STR]; 147 char filename[MAX_STR];
215 unsigned type; //chy 2003-09-21: maybe io,ram,rom 148 unsigned type; //chy 2003-09-21: maybe io,ram,rom
@@ -224,24 +157,24 @@ typedef struct
224#define VFP_REG_NUM 64 157#define VFP_REG_NUM 64
225struct ARMul_State 158struct ARMul_State
226{ 159{
227 ARMword Emulate; /* to start and stop emulation */ 160 ARMword Emulate; /* to start and stop emulation */
228 unsigned EndCondition; /* reason for stopping */ 161 unsigned EndCondition; /* reason for stopping */
229 unsigned ErrorCode; /* type of illegal instruction */ 162 unsigned ErrorCode; /* type of illegal instruction */
230 163
231 /* Order of the following register should not be modified */ 164 /* Order of the following register should not be modified */
232 ARMword Reg[16]; /* the current register file */ 165 ARMword Reg[16]; /* the current register file */
233 ARMword Cpsr; /* the current psr */ 166 ARMword Cpsr; /* the current psr */
234 ARMword Spsr_copy; 167 ARMword Spsr_copy;
235 ARMword phys_pc; 168 ARMword phys_pc;
236 ARMword Reg_usr[2]; 169 ARMword Reg_usr[2];
237 ARMword Reg_svc[2]; /* R13_SVC R14_SVC */ 170 ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
238 ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */ 171 ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */
239 ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */ 172 ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */
240 ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */ 173 ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */
241 ARMword Reg_firq[7]; /* R8---R14 FIRQ */ 174 ARMword Reg_firq[7]; /* R8---R14 FIRQ */
242 ARMword Spsr[7]; /* the exception psr's */ 175 ARMword Spsr[7]; /* the exception psr's */
243 ARMword Mode; /* the current mode */ 176 ARMword Mode; /* the current mode */
244 ARMword Bank; /* the current register bank */ 177 ARMword Bank; /* the current register bank */
245 ARMword exclusive_tag; 178 ARMword exclusive_tag;
246 ARMword exclusive_state; 179 ARMword exclusive_state;
247 ARMword exclusive_result; 180 ARMword exclusive_result;
@@ -281,38 +214,39 @@ struct ARMul_State
281 214
282 ARMword currentexaddr; 215 ARMword currentexaddr;
283 ARMword currentexval; 216 ARMword currentexval;
217 ARMword currentexvald;
284 ARMword servaddr; 218 ARMword servaddr;
285 219
286 unsigned NextInstr; 220 unsigned NextInstr;
287 unsigned VectorCatch; /* caught exception mask */ 221 unsigned VectorCatch; /* caught exception mask */
288 unsigned CallDebug; /* set to call the debugger */ 222 unsigned CallDebug; /* set to call the debugger */
289 unsigned CanWatch; /* set by memory interface if its willing to suffer the 223 unsigned CanWatch; /* set by memory interface if its willing to suffer the
290 overhead of checking for watchpoints on each memory 224 overhead of checking for watchpoints on each memory
291 access */ 225 access */
292 unsigned int StopHandle; 226 unsigned int StopHandle;
293 227
294 char *CommandLine; /* Command Line from ARMsd */ 228 char *CommandLine; /* Command Line from ARMsd */
295 229
296 ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */ 230 ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
297 ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */ 231 ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
298 ARMul_LDCs *LDC[16]; /* LDC instruction */ 232 ARMul_LDCs *LDC[16]; /* LDC instruction */
299 ARMul_STCs *STC[16]; /* STC instruction */ 233 ARMul_STCs *STC[16]; /* STC instruction */
300 ARMul_MRCs *MRC[16]; /* MRC instruction */ 234 ARMul_MRCs *MRC[16]; /* MRC instruction */
301 ARMul_MCRs *MCR[16]; /* MCR instruction */ 235 ARMul_MCRs *MCR[16]; /* MCR instruction */
302 ARMul_MRRCs *MRRC[16]; /* MRRC instruction */ 236 ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
303 ARMul_MCRRs *MCRR[16]; /* MCRR instruction */ 237 ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
304 ARMul_CDPs *CDP[16]; /* CDP instruction */ 238 ARMul_CDPs *CDP[16]; /* CDP instruction */
305 ARMul_CPReads *CPRead[16]; /* Read CP register */ 239 ARMul_CPReads *CPRead[16]; /* Read CP register */
306 ARMul_CPWrites *CPWrite[16]; /* Write CP register */ 240 ARMul_CPWrites *CPWrite[16]; /* Write CP register */
307 unsigned char *CPData[16]; /* Coprocessor data */ 241 unsigned char *CPData[16]; /* Coprocessor data */
308 unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */ 242 unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */
309 243
310 unsigned EventSet; /* the number of events in the queue */ 244 unsigned EventSet; /* the number of events in the queue */
311 unsigned int Now; /* time to the nearest cycle */ 245 unsigned int Now; /* time to the nearest cycle */
312 struct EventNode **EventPtr; /* the event list */ 246 struct EventNode **EventPtr; /* the event list */
313 247
314 unsigned Debug; /* show instructions as they are executed */ 248 unsigned Debug; /* show instructions as they are executed */
315 unsigned NresetSig; /* reset the processor */ 249 unsigned NresetSig; /* reset the processor */
316 unsigned NfiqSig; 250 unsigned NfiqSig;
317 unsigned NirqSig; 251 unsigned NirqSig;
318 252
@@ -356,12 +290,12 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
356*/ 290*/
357 unsigned lateabtSig; 291 unsigned lateabtSig;
358 292
359 ARMword Vector; /* synthesize aborts in cycle modes */ 293 ARMword Vector; /* synthesize aborts in cycle modes */
360 ARMword Aborted; /* sticky flag for aborts */ 294 ARMword Aborted; /* sticky flag for aborts */
361 ARMword Reseted; /* sticky flag for Reset */ 295 ARMword Reseted; /* sticky flag for Reset */
362 ARMword Inted, LastInted; /* sticky flags for interrupts */ 296 ARMword Inted, LastInted; /* sticky flags for interrupts */
363 ARMword Base; /* extra hand for base writeback */ 297 ARMword Base; /* extra hand for base writeback */
364 ARMword AbortAddr; /* to keep track of Prefetch aborts */ 298 ARMword AbortAddr; /* to keep track of Prefetch aborts */
365 299
366 const struct Dbg_HostosInterface *hostif; 300 const struct Dbg_HostosInterface *hostif;
367 301
@@ -378,7 +312,7 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
378 //chy: 2003-08-11, for different arm core type 312 //chy: 2003-08-11, for different arm core type
379 unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */ 313 unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */
380 unsigned is_v5; /* Are we emulating a v5 architecture ? */ 314 unsigned is_v5; /* Are we emulating a v5 architecture ? */
381 unsigned is_v5e; /* Are we emulating a v5e architecture ? */ 315 unsigned is_v5e; /* Are we emulating a v5e architecture ? */
382 unsigned is_v6; /* Are we emulating a v6 architecture ? */ 316 unsigned is_v6; /* Are we emulating a v6 architecture ? */
383 unsigned is_v7; /* Are we emulating a v7 architecture ? */ 317 unsigned is_v7; /* Are we emulating a v7 architecture ? */
384 unsigned is_XScale; /* Are we emulating an XScale architecture ? */ 318 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)
387 //chy 2005-09-19 321 //chy 2005-09-19
388 unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */ 322 unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */
389 //chy: seems only used in xscale's CP14 323 //chy: seems only used in xscale's CP14
390 unsigned int LastTime; /* Value of last call to ARMul_Time() */ 324 unsigned int LastTime; /* Value of last call to ARMul_Time() */
391 ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */ 325 ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */
392 326
393 327
394//added by ksh:for handle different machs io 2004-3-5 328 //added by ksh:for handle different machs io 2004-3-5
395 ARMul_io mach_io; 329 ARMul_io mach_io;
396 330
397/*added by ksh,2004-11-26,some energy profiling*/ 331 /*added by ksh,2004-11-26,some energy profiling*/
398 ARMul_Energy energy; 332 ARMul_Energy energy;
399 333
400//teawater add for next_dis 2004.10.27----------------------- 334 //teawater add for next_dis 2004.10.27-----------------------
401 int disassemble; 335 int disassemble;
402//AJ2D------------------------------------------
403 336
404//teawater add for arm2x86 2005.02.15------------------------------------------- 337
338 //teawater add for arm2x86 2005.02.15-------------------------------------------
405 u32 trap; 339 u32 trap;
406 u32 tea_break_addr; 340 u32 tea_break_addr;
407 u32 tea_break_ok; 341 u32 tea_break_ok;
408 int tea_pc; 342 int tea_pc;
409//AJ2D--------------------------------------------------------------------------
410//teawater add for arm2x86 2005.07.03-------------------------------------------
411
412 /*
413 * 2007-01-24 removed the term-io functions by Anthony Lee,
414 * moved to "device/uart/skyeye_uart_stdio.c".
415 */
416 343
417//AJ2D-------------------------------------------------------------------------- 344 //teawater add for arm2x86 2005.07.05-------------------------------------------
418//teawater add for arm2x86 2005.07.05-------------------------------------------
419 //arm_arm A2-18 345 //arm_arm A2-18
420 int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model 346 int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model
421//AJ2D-------------------------------------------------------------------------- 347
422//teawater change for return if running tb dirty 2005.07.09--------------------- 348 //teawater change for return if running tb dirty 2005.07.09---------------------
423 void *tb_now; 349 void *tb_now;
424//AJ2D--------------------------------------------------------------------------
425 350
426//teawater add for record reg value to ./reg.txt 2005.07.10--------------------- 351
352 //teawater add for record reg value to ./reg.txt 2005.07.10---------------------
427 FILE *tea_reg_fd; 353 FILE *tea_reg_fd;
428//AJ2D--------------------------------------------------------------------------
429 354
430/*added by ksh in 2005-10-1*/ 355
356 /*added by ksh in 2005-10-1*/
431 cpu_config_t *cpu; 357 cpu_config_t *cpu;
432 //mem_config_t *mem_bank; 358 //mem_config_t *mem_bank;
433 359
434/* added LPC remap function */ 360 /* added LPC remap function */
435 int vector_remap_flag; 361 int vector_remap_flag;
436 u32 vector_remap_addr; 362 u32 vector_remap_addr;
437 u32 vector_remap_size; 363 u32 vector_remap_size;
@@ -486,17 +412,14 @@ typedef ARMul_State arm_core_t;
486#define ARM_Debug_Prop 0x10 412#define ARM_Debug_Prop 0x10
487#define ARM_Isync_Prop ARM_Debug_Prop 413#define ARM_Isync_Prop ARM_Debug_Prop
488#define ARM_Lock_Prop 0x20 414#define ARM_Lock_Prop 0x20
489//chy 2003-08-11
490#define ARM_v4_Prop 0x40 415#define ARM_v4_Prop 0x40
491#define ARM_v5_Prop 0x80 416#define ARM_v5_Prop 0x80
492/*jeff.du 2010-08-05 */
493#define ARM_v6_Prop 0xc0 417#define ARM_v6_Prop 0xc0
494 418
495#define ARM_v5e_Prop 0x100 419#define ARM_v5e_Prop 0x100
496#define ARM_XScale_Prop 0x200 420#define ARM_XScale_Prop 0x200
497#define ARM_ep9312_Prop 0x400 421#define ARM_ep9312_Prop 0x400
498#define ARM_iWMMXt_Prop 0x800 422#define ARM_iWMMXt_Prop 0x800
499//chy 2005-09-19
500#define ARM_PXA27X_Prop 0x1000 423#define ARM_PXA27X_Prop 0x1000
501#define ARM_v7_Prop 0x2000 424#define ARM_v7_Prop 0x2000
502 425
@@ -591,47 +514,44 @@ typedef ARMul_State arm_core_t;
591#ifdef __cplusplus 514#ifdef __cplusplus
592extern "C" { 515extern "C" {
593#endif 516#endif
594extern void ARMul_EmulateInit (void); 517extern void ARMul_EmulateInit();
595extern void ARMul_Reset (ARMul_State * state); 518extern void ARMul_Reset(ARMul_State* state);
596#ifdef __cplusplus 519#ifdef __cplusplus
597 } 520 }
598#endif 521#endif
599extern ARMul_State *ARMul_NewState (ARMul_State * state); 522extern ARMul_State *ARMul_NewState(ARMul_State* state);
600extern ARMword ARMul_DoProg (ARMul_State * state); 523extern ARMword ARMul_DoProg(ARMul_State* state);
601extern ARMword ARMul_DoInstr (ARMul_State * state); 524extern ARMword ARMul_DoInstr(ARMul_State* state);
602/***************************************************************************\ 525/***************************************************************************\
603* Definitons of things for event handling * 526* Definitons of things for event handling *
604\***************************************************************************/ 527\***************************************************************************/
605 528
606extern void ARMul_ScheduleEvent (ARMul_State * state, unsigned int delay, 529extern void ARMul_ScheduleEvent(ARMul_State* state, unsigned int delay, unsigned(*func) ());
607 unsigned (*func) ()); 530extern void ARMul_EnvokeEvent(ARMul_State* state);
608extern void ARMul_EnvokeEvent (ARMul_State * state); 531extern unsigned int ARMul_Time(ARMul_State* state);
609extern unsigned int ARMul_Time (ARMul_State * state);
610 532
611/***************************************************************************\ 533/***************************************************************************\
612* Useful support routines * 534* Useful support routines *
613\***************************************************************************/ 535\***************************************************************************/
614 536
615extern ARMword ARMul_GetReg (ARMul_State * state, unsigned mode, 537extern ARMword ARMul_GetReg (ARMul_State* state, unsigned mode, unsigned reg);
616 unsigned reg); 538extern void ARMul_SetReg (ARMul_State* state, unsigned mode, unsigned reg, ARMword value);
617extern void ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg, 539extern ARMword ARMul_GetPC(ARMul_State* state);
618 ARMword value); 540extern ARMword ARMul_GetNextPC(ARMul_State* state);
619extern ARMword ARMul_GetPC (ARMul_State * state); 541extern void ARMul_SetPC(ARMul_State* state, ARMword value);
620extern ARMword ARMul_GetNextPC (ARMul_State * state); 542extern ARMword ARMul_GetR15(ARMul_State* state);
621extern void ARMul_SetPC (ARMul_State * state, ARMword value); 543extern void ARMul_SetR15(ARMul_State* state, ARMword value);
622extern ARMword ARMul_GetR15 (ARMul_State * state); 544
623extern void ARMul_SetR15 (ARMul_State * state, ARMword value); 545extern ARMword ARMul_GetCPSR(ARMul_State* state);
624 546extern void ARMul_SetCPSR(ARMul_State* state, ARMword value);
625extern ARMword ARMul_GetCPSR (ARMul_State * state); 547extern ARMword ARMul_GetSPSR(ARMul_State* state, ARMword mode);
626extern void ARMul_SetCPSR (ARMul_State * state, ARMword value); 548extern void ARMul_SetSPSR(ARMul_State* state, ARMword mode, ARMword value);
627extern ARMword ARMul_GetSPSR (ARMul_State * state, ARMword mode);
628extern void ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value);
629 549
630/***************************************************************************\ 550/***************************************************************************\
631* Definitons of things to handle aborts * 551* Definitons of things to handle aborts *
632\***************************************************************************/ 552\***************************************************************************/
633 553
634extern void ARMul_Abort (ARMul_State * state, ARMword address); 554extern void ARMul_Abort(ARMul_State* state, ARMword address);
635#ifdef MODET 555#ifdef MODET
636#define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */ 556#define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */
637#define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \ 557#define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \
@@ -649,54 +569,40 @@ extern void ARMul_Abort (ARMul_State * state, ARMword address);
649* Definitons of things in the memory interface * 569* Definitons of things in the memory interface *
650\***************************************************************************/ 570\***************************************************************************/
651 571
652extern unsigned ARMul_MemoryInit (ARMul_State * state, 572extern unsigned ARMul_MemoryInit(ARMul_State* state, unsigned int initmemsize);
653 unsigned int initmemsize); 573extern void ARMul_MemoryExit(ARMul_State* state);
654extern void ARMul_MemoryExit (ARMul_State * state);
655 574
656extern ARMword ARMul_LoadInstrS (ARMul_State * state, ARMword address, 575extern ARMword ARMul_LoadInstrS(ARMul_State* state, ARMword address, ARMword isize);
657 ARMword isize); 576extern ARMword ARMul_LoadInstrN(ARMul_State* state, ARMword address, ARMword isize);
658extern ARMword ARMul_LoadInstrN (ARMul_State * state, ARMword address,
659 ARMword isize);
660#ifdef __cplusplus 577#ifdef __cplusplus
661extern "C" { 578extern "C" {
662#endif 579#endif
663extern ARMword ARMul_ReLoadInstr (ARMul_State * state, ARMword address, 580extern ARMword ARMul_ReLoadInstr(ARMul_State* state, ARMword address, ARMword isize);
664 ARMword isize);
665#ifdef __cplusplus 581#ifdef __cplusplus
666 } 582 }
667#endif 583#endif
668extern ARMword ARMul_LoadWordS (ARMul_State * state, ARMword address); 584extern ARMword ARMul_LoadWordS(ARMul_State* state, ARMword address);
669extern ARMword ARMul_LoadWordN (ARMul_State * state, ARMword address); 585extern ARMword ARMul_LoadWordN(ARMul_State* state, ARMword address);
670extern ARMword ARMul_LoadHalfWord (ARMul_State * state, ARMword address); 586extern ARMword ARMul_LoadHalfWord(ARMul_State* state, ARMword address);
671extern ARMword ARMul_LoadByte (ARMul_State * state, ARMword address); 587extern ARMword ARMul_LoadByte(ARMul_State* state, ARMword address);
672 588
673extern void ARMul_StoreWordS (ARMul_State * state, ARMword address, 589extern void ARMul_StoreWordS(ARMul_State* state, ARMword address, ARMword data);
674 ARMword data); 590extern void ARMul_StoreWordN(ARMul_State* state, ARMword address, ARMword data);
675extern void ARMul_StoreWordN (ARMul_State * state, ARMword address, 591extern void ARMul_StoreHalfWord(ARMul_State* state, ARMword address, ARMword data);
676 ARMword data); 592extern void ARMul_StoreByte(ARMul_State* state, ARMword address, ARMword data);
677extern void ARMul_StoreHalfWord (ARMul_State * state, ARMword address, 593
678 ARMword data); 594extern ARMword ARMul_SwapWord(ARMul_State* state, ARMword address, ARMword data);
679extern void ARMul_StoreByte (ARMul_State * state, ARMword address, 595extern ARMword ARMul_SwapByte(ARMul_State* state, ARMword address, ARMword data);
680 ARMword data); 596
681 597extern void ARMul_Icycles(ARMul_State* state, unsigned number, ARMword address);
682extern ARMword ARMul_SwapWord (ARMul_State * state, ARMword address, 598extern void ARMul_Ccycles(ARMul_State* state, unsigned number, ARMword address);
683 ARMword data); 599
684extern ARMword ARMul_SwapByte (ARMul_State * state, ARMword address, 600extern ARMword ARMul_ReadWord(ARMul_State* state, ARMword address);
685 ARMword data); 601extern ARMword ARMul_ReadByte(ARMul_State* state, ARMword address);
686 602extern void ARMul_WriteWord(ARMul_State* state, ARMword address, ARMword data);
687extern void ARMul_Icycles (ARMul_State * state, unsigned number, 603extern void ARMul_WriteByte(ARMul_State* state, ARMword address, ARMword data);
688 ARMword address); 604
689extern void ARMul_Ccycles (ARMul_State * state, unsigned number, 605extern ARMword ARMul_MemAccess(ARMul_State* state, ARMword, ARMword,
690 ARMword address);
691
692extern ARMword ARMul_ReadWord (ARMul_State * state, ARMword address);
693extern ARMword ARMul_ReadByte (ARMul_State * state, ARMword address);
694extern void ARMul_WriteWord (ARMul_State * state, ARMword address,
695 ARMword data);
696extern void ARMul_WriteByte (ARMul_State * state, ARMword address,
697 ARMword data);
698
699extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
700 ARMword, ARMword, ARMword, ARMword, ARMword, 606 ARMword, ARMword, ARMword, ARMword, ARMword,
701 ARMword, ARMword, ARMword); 607 ARMword, ARMword, ARMword);
702 608
@@ -739,82 +645,58 @@ extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
739#define ARMul_CP15_DBCON_E1 0x000c 645#define ARMul_CP15_DBCON_E1 0x000c
740#define ARMul_CP15_DBCON_E0 0x0003 646#define ARMul_CP15_DBCON_E0 0x0003
741 647
742extern unsigned ARMul_CoProInit (ARMul_State * state); 648extern unsigned ARMul_CoProInit(ARMul_State* state);
743extern void ARMul_CoProExit (ARMul_State * state); 649extern void ARMul_CoProExit(ARMul_State* state);
744extern void ARMul_CoProAttach (ARMul_State * state, unsigned number, 650extern void ARMul_CoProAttach (ARMul_State* state, unsigned number,
745 ARMul_CPInits * init, ARMul_CPExits * exit, 651 ARMul_CPInits* init, ARMul_CPExits* exit,
746 ARMul_LDCs * ldc, ARMul_STCs * stc, 652 ARMul_LDCs* ldc, ARMul_STCs* stc,
747 ARMul_MRCs * mrc, ARMul_MCRs * mcr, 653 ARMul_MRCs* mrc, ARMul_MCRs* mcr,
748 ARMul_MRRCs * mrrc, ARMul_MCRRs * mcrr, 654 ARMul_MRRCs* mrrc, ARMul_MCRRs* mcrr,
749 ARMul_CDPs * cdp, 655 ARMul_CDPs* cdp,
750 ARMul_CPReads * read, ARMul_CPWrites * write); 656 ARMul_CPReads* read, ARMul_CPWrites* write);
751extern void ARMul_CoProDetach (ARMul_State * state, unsigned number); 657extern void ARMul_CoProDetach(ARMul_State* state, unsigned number);
752 658
753/***************************************************************************\ 659/***************************************************************************\
754* Definitons of things in the host environment * 660* Definitons of things in the host environment *
755\***************************************************************************/ 661\***************************************************************************/
756 662
757extern unsigned ARMul_OSInit (ARMul_State * state); 663extern unsigned ARMul_OSInit(ARMul_State* state);
758extern void ARMul_OSExit (ARMul_State * state); 664extern void ARMul_OSExit(ARMul_State* state);
759 665
760#ifdef __cplusplus 666#ifdef __cplusplus
761 extern "C" { 667 extern "C" {
762#endif 668#endif
763 669
764extern unsigned ARMul_OSHandleSWI (ARMul_State * state, ARMword number); 670extern unsigned ARMul_OSHandleSWI(ARMul_State* state, ARMword number);
765#ifdef __cplusplus 671#ifdef __cplusplus
766} 672}
767#endif 673#endif
768 674
769 675
770extern ARMword ARMul_OSLastErrorP (ARMul_State * state); 676extern ARMword ARMul_OSLastErrorP(ARMul_State* state);
771 677
772extern ARMword ARMul_Debug (ARMul_State * state, ARMword pc, ARMword instr); 678extern ARMword ARMul_Debug(ARMul_State* state, ARMword pc, ARMword instr);
773extern unsigned ARMul_OSException (ARMul_State * state, ARMword vector, 679extern unsigned ARMul_OSException(ARMul_State* state, ARMword vector, ARMword pc);
774 ARMword pc);
775extern int rdi_log; 680extern int rdi_log;
776 681
777/***************************************************************************\ 682enum ConditionCode {
778* Host-dependent stuff * 683 EQ = 0,
779\***************************************************************************/ 684 NE = 1,
780 685 CS = 2,
781#ifdef macintosh 686 CC = 3,
782pascal void SpinCursor (short increment); /* copied from CursorCtl.h */ 687 MI = 4,
783# define HOURGLASS SpinCursor( 1 ) 688 PL = 5,
784# define HOURGLASS_RATE 1023 /* 2^n - 1 */ 689 VS = 6,
785#endif 690 VC = 7,
786 691 HI = 8,
787//teawater add for arm2x86 2005.02.14------------------------------------------- 692 LS = 9,
788/*ywc 2005-03-31*/ 693 GE = 10,
789/* 694 LT = 11,
790#include "arm2x86.h" 695 GT = 12,
791#include "arm2x86_dp.h" 696 LE = 13,
792#include "arm2x86_movl.h" 697 AL = 14,
793#include "arm2x86_psr.h" 698 NV = 15,
794#include "arm2x86_shift.h" 699};
795#include "arm2x86_mem.h"
796#include "arm2x86_mul.h"
797#include "arm2x86_test.h"
798#include "arm2x86_other.h"
799#include "list.h"
800#include "tb.h"
801*/
802#define EQ 0
803#define NE 1
804#define CS 2
805#define CC 3
806#define MI 4
807#define PL 5
808#define VS 6
809#define VC 7
810#define HI 8
811#define LS 9
812#define GE 10
813#define LT 11
814#define GT 12
815#define LE 13
816#define AL 14
817#define NV 15
818 700
819#ifndef NFLAG 701#ifndef NFLAG
820#define NFLAG state->NFlag 702#define NFLAG state->NFlag
@@ -849,32 +731,16 @@ pascal void SpinCursor (short increment); /* copied from CursorCtl.h */
849#define ZBIT_SHIFT 30 731#define ZBIT_SHIFT 30
850#define CBIT_SHIFT 29 732#define CBIT_SHIFT 29
851#define VBIT_SHIFT 28 733#define VBIT_SHIFT 28
852#ifdef DBCT 734
853//teawater change for local tb branch directly jump 2005.10.18------------------
854#include "dbct/list.h"
855#include "dbct/arm2x86.h"
856#include "dbct/arm2x86_dp.h"
857#include "dbct/arm2x86_movl.h"
858#include "dbct/arm2x86_psr.h"
859#include "dbct/arm2x86_shift.h"
860#include "dbct/arm2x86_mem.h"
861#include "dbct/arm2x86_mul.h"
862#include "dbct/arm2x86_test.h"
863#include "dbct/arm2x86_other.h"
864#include "dbct/arm2x86_coproc.h"
865#include "dbct/tb.h"
866#endif
867//AJ2D--------------------------------------------------------------------------
868//AJ2D--------------------------------------------------------------------------
869#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,",\ 735#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,",\
870 state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \ 736 state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \
871 state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \ 737 state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \
872 state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \ 738 state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \
873 state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \ 739 state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \
874 state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\ 740 state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
875 state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\ 741 state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\
876 state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\ 742 state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
877 state->temp,state->loaded,state->decoded);} 743 state->temp,state->loaded,state->decoded);}
878 744
879#define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\ 745#define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\
880RUs %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\ 746RUs %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",\
912 778
913#define SA1110 0x6901b110 779#define SA1110 0x6901b110
914#define SA1100 0x4401a100 780#define SA1100 0x4401a100
915#define PXA250 0x69052100 781#define PXA250 0x69052100
916#define PXA270 0x69054110 782#define PXA270 0x69054110
917//#define PXA250 0x69052903 783//#define PXA250 0x69052903
918// 0x69052903; //PXA250 B1 from intel 278522-001.pdf 784// 0x69052903; //PXA250 B1 from intel 278522-001.pdf
919 785
920 786
921extern void ARMul_UndefInstr (ARMul_State *, ARMword); 787extern void ARMul_UndefInstr(ARMul_State*, ARMword);
922extern void ARMul_FixCPSR (ARMul_State *, ARMword, ARMword); 788extern void ARMul_FixCPSR(ARMul_State*, ARMword, ARMword);
923extern void ARMul_FixSPSR (ARMul_State *, ARMword, ARMword); 789extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword);
924extern void ARMul_ConsolePrint (ARMul_State *, const char *, ...); 790extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...);
925extern void ARMul_SelectProcessor (ARMul_State *, unsigned); 791extern void ARMul_SelectProcessor(ARMul_State*, unsigned);
926 792
927#define DIFF_LOG 0 793#define DIFF_LOG 0
928#define SAVE_LOG 0 794#define SAVE_LOG 0
diff --git a/src/core/arm/skyeye_common/armemu.h b/src/core/arm/skyeye_common/armemu.h
index c0f0270fe..7f7c0e682 100644
--- a/src/core/arm/skyeye_common/armemu.h
+++ b/src/core/arm/skyeye_common/armemu.h
@@ -23,26 +23,6 @@
23 23
24//extern ARMword isize; 24//extern ARMword isize;
25 25
26#define DEBUG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
27
28/* Condition code values. */
29#define EQ 0
30#define NE 1
31#define CS 2
32#define CC 3
33#define MI 4
34#define PL 5
35#define VS 6
36#define VC 7
37#define HI 8
38#define LS 9
39#define GE 10
40#define LT 11
41#define GT 12
42#define LE 13
43#define AL 14
44#define NV 15
45
46/* Shift Opcodes. */ 26/* Shift Opcodes. */
47#define LSL 0 27#define LSL 0
48#define LSR 1 28#define LSR 1
@@ -503,7 +483,7 @@ tdstate;
503 * out-of-updated with the newer ISA. 483 * out-of-updated with the newer ISA.
504 * -- Michael.Kang 484 * -- Michael.Kang
505 ********************************************************************************/ 485 ********************************************************************************/
506#define UNDEF_WARNING WARN_LOG(ARM11, "undefined or unpredicted behavior for arm instruction.\n"); 486#define UNDEF_WARNING LOG_WARNING(Core_ARM11, "undefined or unpredicted behavior for arm instruction.");
507 487
508/* Macros to scrutinize instructions. */ 488/* Macros to scrutinize instructions. */
509#define UNDEF_Test UNDEF_WARNING 489#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 07d0c1f44..6c33d8b78 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
522 if (tm == VFP_QNAN) 522 if (tm == VFP_QNAN)
523 vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; 523 vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
524 goto pack_nan; 524 goto pack_nan;
525 } 525 } else if (tm & VFP_ZERO)
526 else if (tm & VFP_ZERO)
527 vdd.exponent = 0; 526 vdd.exponent = 0;
528 else 527 else
529 vdd.exponent = vsm.exponent + (1023 - 127); 528 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
615 exceptions |= FPSCR_IDC; 614 exceptions |= FPSCR_IDC;
616 615
617 if (tm & VFP_NAN) 616 if (tm & VFP_NAN)
618 vsm.sign = 0; 617 vsm.sign = 1;
619 618
620 if (vsm.exponent >= 127 + 32) { 619 if (vsm.exponent >= 127 + 32) {
621 d = vsm.sign ? 0 : 0xffffffff; 620 d = vsm.sign ? 0 : 0xffffffff;
622 exceptions = FPSCR_IOC; 621 exceptions = FPSCR_IOC;
623 } else if (vsm.exponent >= 127 - 1) { 622 } else if (vsm.exponent >= 127) {
624 int shift = 127 + 31 - vsm.exponent; 623 int shift = 127 + 31 - vsm.exponent;
625 u32 rem, incr = 0; 624 u32 rem, incr = 0;
626 625
@@ -705,7 +704,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
705 if (vsm.sign) 704 if (vsm.sign)
706 d = ~d; 705 d = ~d;
707 exceptions |= FPSCR_IOC; 706 exceptions |= FPSCR_IOC;
708 } else if (vsm.exponent >= 127 - 1) { 707 } else if (vsm.exponent >= 127) {
709 int shift = 127 + 31 - vsm.exponent; 708 int shift = 127 + 31 - vsm.exponent;
710 u32 rem, incr = 0; 709 u32 rem, incr = 0;
711 710
@@ -1149,7 +1148,10 @@ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
1149 /* 1148 /*
1150 * Subtraction is addition with one sign inverted. 1149 * Subtraction is addition with one sign inverted.
1151 */ 1150 */
1152 return vfp_single_fadd(state, sd, sn, vfp_single_packed_negate(m), fpscr); 1151 if (m != 0x7FC00000) // Only negate if m isn't NaN.
1152 m = vfp_single_packed_negate(m);
1153
1154 return vfp_single_fadd(state, sd, sn, m, fpscr);
1153} 1155}
1154 1156
1155/* 1157/*
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 25c78d33c..64de0cbba 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -16,10 +16,10 @@
16 16
17namespace Core { 17namespace Core {
18 18
19u64 g_last_ticks = 0; ///< Last CPU ticks 19static u64 last_ticks = 0; ///< Last CPU ticks
20ARM_Disasm* g_disasm = nullptr; ///< ARM disassembler 20static ARM_Disasm* disasm = nullptr; ///< ARM disassembler
21ARM_Interface* g_app_core = nullptr; ///< ARM11 application core 21ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
22ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core 22ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
23 23
24/// Run the core CPU loop 24/// Run the core CPU loop
25void RunLoop(int tight_loop) { 25void RunLoop(int tight_loop) {
@@ -47,9 +47,9 @@ void Stop() {
47 47
48/// Initialize the core 48/// Initialize the core
49int Init() { 49int Init() {
50 NOTICE_LOG(MASTER_LOG, "initialized OK"); 50 LOG_DEBUG(Core, "initialized OK");
51 51
52 g_disasm = new ARM_Disasm(); 52 disasm = new ARM_Disasm();
53 g_sys_core = new ARM_Interpreter(); 53 g_sys_core = new ARM_Interpreter();
54 54
55 switch (Settings::values.cpu_core) { 55 switch (Settings::values.cpu_core) {
@@ -62,17 +62,17 @@ int Init() {
62 break; 62 break;
63 } 63 }
64 64
65 g_last_ticks = Core::g_app_core->GetTicks(); 65 last_ticks = Core::g_app_core->GetTicks();
66 66
67 return 0; 67 return 0;
68} 68}
69 69
70void Shutdown() { 70void Shutdown() {
71 delete g_disasm; 71 delete disasm;
72 delete g_app_core; 72 delete g_app_core;
73 delete g_sys_core; 73 delete g_sys_core;
74 74
75 NOTICE_LOG(MASTER_LOG, "shutdown OK"); 75 LOG_DEBUG(Core, "shutdown OK");
76} 76}
77 77
78} // namespace 78} // namespace
diff --git a/src/core/core.h b/src/core/core.h
index 872dc0cd1..850bb0ab4 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -26,13 +26,13 @@ void Start();
26 26
27/** 27/**
28 * Run the core CPU loop 28 * Run the core CPU loop
29 * This function loops for 100 instructions in the CPU before trying to update hardware. This is a 29 * This function runs the core for the specified number of CPU instructions before trying to update
30 * little bit faster than SingleStep, and should be pretty much equivalent. The number of 30 * hardware. This is much faster than SingleStep (and should be equivalent), as the CPU is not
31 * instructions chosen is fairly arbitrary, however a large number will more drastically affect the 31 * required to do a full dispatch with each instruction. NOTE: the number of instructions requested
32 * frequency of GSP interrupts and likely break things. The point of this is to just loop in the CPU 32 * is not guaranteed to run, as this will be interrupted preemptively if a hardware update is
33 * for more than 1 instruction to reduce overhead and make it a little bit faster... 33 * requested (e.g. on a thread switch).
34 */ 34 */
35void RunLoop(int tight_loop=100); 35void RunLoop(int tight_loop=1000);
36 36
37/// Step the CPU one instruction 37/// Step the CPU one instruction
38void SingleStep(); 38void SingleStep();
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 0116cb376..1a0b2724a 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -41,7 +41,7 @@ struct BaseEvent
41 s64 time; 41 s64 time;
42 u64 userdata; 42 u64 userdata;
43 int type; 43 int type;
44 // Event *next; 44 // Event *next;
45}; 45};
46 46
47typedef LinkedListItem<BaseEvent> Event; 47typedef LinkedListItem<BaseEvent> Event;
@@ -67,7 +67,7 @@ s64 idledCycles;
67static std::recursive_mutex externalEventSection; 67static std::recursive_mutex externalEventSection;
68 68
69// Warning: not included in save state. 69// Warning: not included in save state.
70void(*advanceCallback)(int cyclesExecuted) = NULL; 70void(*advanceCallback)(int cyclesExecuted) = nullptr;
71 71
72void SetClockFrequencyMHz(int cpuMhz) 72void SetClockFrequencyMHz(int cpuMhz)
73{ 73{
@@ -124,7 +124,7 @@ int RegisterEvent(const char *name, TimedCallback callback)
124 124
125void AntiCrashCallback(u64 userdata, int cyclesLate) 125void AntiCrashCallback(u64 userdata, int cyclesLate)
126{ 126{
127 ERROR_LOG(TIME, "Savestate broken: an unregistered event was called."); 127 LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called.");
128 Core::Halt("invalid timing events"); 128 Core::Halt("invalid timing events");
129} 129}
130 130
@@ -176,7 +176,7 @@ void Shutdown()
176 176
177u64 GetTicks() 177u64 GetTicks()
178{ 178{
179 ERROR_LOG(TIME, "Unimplemented function!"); 179 LOG_ERROR(Core, "Unimplemented function!");
180 return 0; 180 return 0;
181 //return (u64)globalTimer + slicelength - currentMIPS->downcount; 181 //return (u64)globalTimer + slicelength - currentMIPS->downcount;
182} 182}
@@ -231,7 +231,7 @@ void ClearPendingEvents()
231 231
232void AddEventToQueue(Event* ne) 232void AddEventToQueue(Event* ne)
233{ 233{
234 Event* prev = NULL; 234 Event* prev = nullptr;
235 Event** pNext = &first; 235 Event** pNext = &first;
236 for (;;) 236 for (;;)
237 { 237 {
@@ -249,7 +249,7 @@ void AddEventToQueue(Event* ne)
249 249
250// This must be run ONLY from within the cpu thread 250// This must be run ONLY from within the cpu thread
251// cyclesIntoFuture may be VERY inaccurate if called from anything else 251// cyclesIntoFuture may be VERY inaccurate if called from anything else
252// than Advance 252// than Advance
253void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) 253void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
254{ 254{
255 Event *ne = GetNewEvent(); 255 Event *ne = GetNewEvent();
@@ -327,7 +327,7 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
327 } 327 }
328 if (!tsFirst) 328 if (!tsFirst)
329 { 329 {
330 tsLast = NULL; 330 tsLast = nullptr;
331 return result; 331 return result;
332 } 332 }
333 333
@@ -433,7 +433,7 @@ void RemoveThreadsafeEvent(int event_type)
433 } 433 }
434 if (!tsFirst) 434 if (!tsFirst)
435 { 435 {
436 tsLast = NULL; 436 tsLast = nullptr;
437 return; 437 return;
438 } 438 }
439 Event *prev = tsFirst; 439 Event *prev = tsFirst;
@@ -469,8 +469,8 @@ void ProcessFifoWaitEvents()
469 { 469 {
470 if (first->time <= globalTimer) 470 if (first->time <= globalTimer)
471 { 471 {
472 // LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", 472 //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
473 // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); 473 // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
474 Event* evt = first; 474 Event* evt = first;
475 first = first->next; 475 first = first->next;
476 event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); 476 event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
@@ -495,7 +495,7 @@ void MoveEvents()
495 AddEventToQueue(tsFirst); 495 AddEventToQueue(tsFirst);
496 tsFirst = next; 496 tsFirst = next;
497 } 497 }
498 tsLast = NULL; 498 tsLast = nullptr;
499 499
500 // Move free events to threadsafe pool 500 // Move free events to threadsafe pool
501 while (allocatedTsEvents > 0 && eventPool) 501 while (allocatedTsEvents > 0 && eventPool)
@@ -510,29 +510,29 @@ void MoveEvents()
510 510
511void Advance() 511void Advance()
512{ 512{
513 ERROR_LOG(TIME, "Unimplemented function!"); 513 LOG_ERROR(Core, "Unimplemented function!");
514 //int cyclesExecuted = slicelength - currentMIPS->downcount; 514 //int cyclesExecuted = slicelength - currentMIPS->downcount;
515 //globalTimer += cyclesExecuted; 515 //globalTimer += cyclesExecuted;
516 //currentMIPS->downcount = slicelength; 516 //currentMIPS->downcount = slicelength;
517 517
518 //if (Common::AtomicLoadAcquire(hasTsEvents)) 518 //if (Common::AtomicLoadAcquire(hasTsEvents))
519 // MoveEvents(); 519 // MoveEvents();
520 //ProcessFifoWaitEvents(); 520 //ProcessFifoWaitEvents();
521 521
522 //if (!first) 522 //if (!first)
523 //{ 523 //{
524 // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); 524 // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
525 // currentMIPS->downcount += 10000; 525 // currentMIPS->downcount += 10000;
526 //} 526 //}
527 //else 527 //else
528 //{ 528 //{
529 // slicelength = (int)(first->time - globalTimer); 529 // slicelength = (int)(first->time - globalTimer);
530 // if (slicelength > MAX_SLICE_LENGTH) 530 // if (slicelength > MAX_SLICE_LENGTH)
531 // slicelength = MAX_SLICE_LENGTH; 531 // slicelength = MAX_SLICE_LENGTH;
532 // currentMIPS->downcount = slicelength; 532 // currentMIPS->downcount = slicelength;
533 //} 533 //}
534 //if (advanceCallback) 534 //if (advanceCallback)
535 // advanceCallback(cyclesExecuted); 535 // advanceCallback(cyclesExecuted);
536} 536}
537 537
538void LogPendingEvents() 538void LogPendingEvents()
@@ -547,23 +547,23 @@ void LogPendingEvents()
547 547
548void Idle(int maxIdle) 548void Idle(int maxIdle)
549{ 549{
550 ERROR_LOG(TIME, "Unimplemented function!"); 550 LOG_ERROR(Core, "Unimplemented function!");
551 //int cyclesDown = currentMIPS->downcount; 551 //int cyclesDown = currentMIPS->downcount;
552 //if (maxIdle != 0 && cyclesDown > maxIdle) 552 //if (maxIdle != 0 && cyclesDown > maxIdle)
553 // cyclesDown = maxIdle; 553 // cyclesDown = maxIdle;
554 554
555 //if (first && cyclesDown > 0) 555 //if (first && cyclesDown > 0)
556 //{ 556 //{
557 // int cyclesExecuted = slicelength - currentMIPS->downcount; 557 // int cyclesExecuted = slicelength - currentMIPS->downcount;
558 // int cyclesNextEvent = (int) (first->time - globalTimer); 558 // int cyclesNextEvent = (int) (first->time - globalTimer);
559 559
560 // if (cyclesNextEvent < cyclesExecuted + cyclesDown) 560 // if (cyclesNextEvent < cyclesExecuted + cyclesDown)
561 // { 561 // {
562 // cyclesDown = cyclesNextEvent - cyclesExecuted; 562 // cyclesDown = cyclesNextEvent - cyclesExecuted;
563 // // Now, now... no time machines, please. 563 // // Now, now... no time machines, please.
564 // if (cyclesDown < 0) 564 // if (cyclesDown < 0)
565 // cyclesDown = 0; 565 // cyclesDown = 0;
566 // } 566 // }
567 //} 567 //}
568 568
569 //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f)); 569 //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)
571 //idledCycles += cyclesDown; 571 //idledCycles += cyclesDown;
572 //currentMIPS->downcount -= cyclesDown; 572 //currentMIPS->downcount -= cyclesDown;
573 //if (currentMIPS->downcount == 0) 573 //if (currentMIPS->downcount == 0)
574 // currentMIPS->downcount = -1; 574 // currentMIPS->downcount = -1;
575} 575}
576 576
577std::string GetScheduledEventsSummary() 577std::string GetScheduledEventsSummary()
@@ -614,7 +614,7 @@ void DoState(PointerWrap &p)
614 // These (should) be filled in later by the modules. 614 // These (should) be filled in later by the modules.
615 event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); 615 event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
616 616
617 p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)NULL); 617 p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr);
618 p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast); 618 p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
619 619
620 p.Do(g_clock_rate_arm11); 620 p.Do(g_clock_rate_arm11);
@@ -623,4 +623,4 @@ void DoState(PointerWrap &p)
623 p.Do(idledCycles); 623 p.Do(idledCycles);
624} 624}
625 625
626} // namespace 626} // namespace
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 09fdf7a90..b197cf40c 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -106,4 +106,4 @@ void SetClockFrequencyMHz(int cpuMhz);
106int GetClockFrequencyMHz(); 106int GetClockFrequencyMHz();
107extern int slicelength; 107extern int slicelength;
108 108
109}; // namespace 109} // namespace
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive.h
deleted file mode 100644
index aeabf09ac..000000000
--- a/src/core/file_sys/archive.h
+++ /dev/null
@@ -1,104 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "common/common_types.h"
10#include "common/bit_field.h"
11
12#include "core/file_sys/file.h"
13#include "core/file_sys/directory.h"
14
15#include "core/hle/kernel/kernel.h"
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18// FileSys namespace
19
20namespace FileSys {
21
22union Mode {
23 u32 hex;
24 BitField<0, 1, u32> read_flag;
25 BitField<1, 1, u32> write_flag;
26 BitField<2, 1, u32> create_flag;
27};
28
29class Archive : NonCopyable {
30public:
31 /// Supported archive types
32 enum class IdCode : u32 {
33 RomFS = 0x00000003,
34 SaveData = 0x00000004,
35 ExtSaveData = 0x00000006,
36 SharedExtSaveData = 0x00000007,
37 SystemSaveData = 0x00000008,
38 SDMC = 0x00000009,
39 SDMCWriteOnly = 0x0000000A,
40 };
41
42 Archive() { }
43 virtual ~Archive() { }
44
45 /**
46 * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
47 * @return IdCode of the archive
48 */
49 virtual IdCode GetIdCode() const = 0;
50
51 /**
52 * Open a file specified by its path, using the specified mode
53 * @param path Path relative to the archive
54 * @param mode Mode to open the file with
55 * @return Opened file, or nullptr
56 */
57 virtual std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const = 0;
58
59 /**
60 * Create a directory specified by its path
61 * @param path Path relative to the archive
62 * @return Whether the directory could be created
63 */
64 virtual bool CreateDirectory(const std::string& path) const = 0;
65
66 /**
67 * Open a directory specified by its path
68 * @param path Path relative to the archive
69 * @return Opened directory, or nullptr
70 */
71 virtual std::unique_ptr<Directory> OpenDirectory(const std::string& path) const = 0;
72
73 /**
74 * Read data from the archive
75 * @param offset Offset in bytes to start reading data from
76 * @param length Length in bytes of data to read from archive
77 * @param buffer Buffer to read data into
78 * @return Number of bytes read
79 */
80 virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0;
81
82 /**
83 * Write data to the archive
84 * @param offset Offset in bytes to start writing data to
85 * @param length Length in bytes of data to write to archive
86 * @param buffer Buffer to write data from
87 * @param flush The flush parameters (0 == do not flush)
88 * @return Number of bytes written
89 */
90 virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0;
91
92 /**
93 * Get the size of the archive in bytes
94 * @return Size of the archive in bytes
95 */
96 virtual size_t GetSize() const = 0;
97
98 /**
99 * Set the size of the archive in bytes
100 */
101 virtual void SetSize(const u64 size) = 0;
102};
103
104} // 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 000000000..18c314884
--- /dev/null
+++ b/src/core/file_sys/archive_backend.h
@@ -0,0 +1,225 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9#include "common/common_types.h"
10#include "common/string_util.h"
11#include "common/bit_field.h"
12
13#include "core/file_sys/file_backend.h"
14#include "core/file_sys/directory_backend.h"
15
16#include "core/mem_map.h"
17#include "core/hle/kernel/kernel.h"
18
19////////////////////////////////////////////////////////////////////////////////////////////////////
20// FileSys namespace
21
22namespace FileSys {
23
24// Path string type
25enum LowPathType : u32 {
26 Invalid = 0,
27 Empty = 1,
28 Binary = 2,
29 Char = 3,
30 Wchar = 4
31};
32
33union Mode {
34 u32 hex;
35 BitField<0, 1, u32> read_flag;
36 BitField<1, 1, u32> write_flag;
37 BitField<2, 1, u32> create_flag;
38};
39
40class Path {
41public:
42
43 Path():
44 type(Invalid)
45 {
46 }
47
48 Path(LowPathType type, u32 size, u32 pointer):
49 type(type)
50 {
51 switch (type) {
52 case Binary:
53 {
54 u8* data = Memory::GetPointer(pointer);
55 binary = std::vector<u8>(data, data + size);
56 break;
57 }
58 case Char:
59 {
60 const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
61 string = std::string(data, size - 1); // Data is always null-terminated.
62 break;
63 }
64 case Wchar:
65 {
66 const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
67 u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
68 break;
69 }
70 default:
71 break;
72 }
73 }
74
75 LowPathType GetType() const {
76 return type;
77 }
78
79 /**
80 * Gets the string representation of the path for debugging
81 * @return String representation of the path for debugging
82 */
83 const std::string DebugStr() const {
84 switch (GetType()) {
85 case Invalid:
86 return "[Invalid]";
87 case Empty:
88 return "[Empty]";
89 case Binary:
90 {
91 std::stringstream res;
92 res << "[Binary: ";
93 for (unsigned byte : binary)
94 res << std::hex << std::setw(2) << std::setfill('0') << byte;
95 res << ']';
96 return res.str();
97 }
98 case Char:
99 return "[Char: " + AsString() + ']';
100 case Wchar:
101 return "[Wchar: " + AsString() + ']';
102 default:
103 // TODO(yuriks): Add assert
104 LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
105 return {};
106 }
107 }
108
109 const std::string AsString() const {
110 switch (GetType()) {
111 case Char:
112 return string;
113 case Wchar:
114 return Common::UTF16ToUTF8(u16str);
115 case Empty:
116 return {};
117 default:
118 // TODO(yuriks): Add assert
119 LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
120 return {};
121 }
122 }
123
124 const std::u16string AsU16Str() const {
125 switch (GetType()) {
126 case Char:
127 return Common::UTF8ToUTF16(string);
128 case Wchar:
129 return u16str;
130 case Empty:
131 return {};
132 default:
133 // TODO(yuriks): Add assert
134 LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
135 return {};
136 }
137 }
138
139 const std::vector<u8> AsBinary() const {
140 switch (GetType()) {
141 case Binary:
142 return binary;
143 case Char:
144 return std::vector<u8>(string.begin(), string.end());
145 case Wchar:
146 return std::vector<u8>(u16str.begin(), u16str.end());
147 case Empty:
148 return {};
149 default:
150 // TODO(yuriks): Add assert
151 LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!");
152 return {};
153 }
154 }
155
156private:
157 LowPathType type;
158 std::vector<u8> binary;
159 std::string string;
160 std::u16string u16str;
161};
162
163class ArchiveBackend : NonCopyable {
164public:
165 virtual ~ArchiveBackend() { }
166
167 /**
168 * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
169 */
170 virtual std::string GetName() const = 0;
171
172 /**
173 * Open a file specified by its path, using the specified mode
174 * @param path Path relative to the archive
175 * @param mode Mode to open the file with
176 * @return Opened file, or nullptr
177 */
178 virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0;
179
180 /**
181 * Delete a file specified by its path
182 * @param path Path relative to the archive
183 * @return Whether the file could be deleted
184 */
185 virtual bool DeleteFile(const FileSys::Path& path) const = 0;
186
187 /**
188 * Rename a File specified by its path
189 * @param src_path Source path relative to the archive
190 * @param dest_path Destination path relative to the archive
191 * @return Whether rename succeeded
192 */
193 virtual bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0;
194
195 /**
196 * Delete a directory specified by its path
197 * @param path Path relative to the archive
198 * @return Whether the directory could be deleted
199 */
200 virtual bool DeleteDirectory(const FileSys::Path& path) const = 0;
201
202 /**
203 * Create a directory specified by its path
204 * @param path Path relative to the archive
205 * @return Whether the directory could be created
206 */
207 virtual bool CreateDirectory(const Path& path) const = 0;
208
209 /**
210 * Rename a Directory specified by its path
211 * @param src_path Source path relative to the archive
212 * @param dest_path Destination path relative to the archive
213 * @return Whether rename succeeded
214 */
215 virtual bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const = 0;
216
217 /**
218 * Open a directory specified by its path
219 * @param path Path relative to the archive
220 * @return Opened directory, or nullptr
221 */
222 virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
223};
224
225} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
index cc759faa8..0709b62a1 100644
--- a/src/core/file_sys/archive_romfs.cpp
+++ b/src/core/file_sys/archive_romfs.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6
5#include "common/common_types.h" 7#include "common/common_types.h"
6 8
7#include "core/file_sys/archive_romfs.h" 9#include "core/file_sys/archive_romfs.h"
@@ -16,81 +18,67 @@ namespace FileSys {
16Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { 18Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) {
17 // Load the RomFS from the app 19 // Load the RomFS from the app
18 if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) { 20 if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) {
19 WARN_LOG(FILESYS, "Unable to read RomFS!"); 21 LOG_ERROR(Service_FS, "Unable to read RomFS!");
20 } 22 }
21} 23}
22 24
23Archive_RomFS::~Archive_RomFS() {
24}
25
26/** 25/**
27 * Open a file specified by its path, using the specified mode 26 * Open a file specified by its path, using the specified mode
28 * @param path Path relative to the archive 27 * @param path Path relative to the archive
29 * @param mode Mode to open the file with 28 * @param mode Mode to open the file with
30 * @return Opened file, or nullptr 29 * @return Opened file, or nullptr
31 */ 30 */
32std::unique_ptr<File> Archive_RomFS::OpenFile(const std::string& path, const Mode mode) const { 31std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const {
33 return std::unique_ptr<File>(new File_RomFS); 32 return std::make_unique<File_RomFS>(this);
34} 33}
35 34
36/** 35/**
37 * Create a directory specified by its path 36 * Delete a file specified by its path
38 * @param path Path relative to the archive 37 * @param path Path relative to the archive
39 * @return Whether the directory could be created 38 * @return Whether the file could be deleted
40 */ 39 */
41bool Archive_RomFS::CreateDirectory(const std::string& path) const { 40bool Archive_RomFS::DeleteFile(const FileSys::Path& path) const {
42 ERROR_LOG(FILESYS, "Attempted to create a directory in ROMFS."); 41 LOG_WARNING(Service_FS, "Attempted to delete a file from ROMFS.");
43 return false; 42 return false;
44}; 43}
45 44
46/** 45bool Archive_RomFS::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
47 * Open a directory specified by its path 46 LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
48 * @param path Path relative to the archive 47 return false;
49 * @return Opened directory, or nullptr
50 */
51std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const std::string& path) const {
52 return std::unique_ptr<Directory>(new Directory_RomFS);
53} 48}
54 49
55/** 50/**
56 * Read data from the archive 51 * Delete a directory specified by its path
57 * @param offset Offset in bytes to start reading data from 52 * @param path Path relative to the archive
58 * @param length Length in bytes of data to read from archive 53 * @return Whether the directory could be deleted
59 * @param buffer Buffer to read data into
60 * @return Number of bytes read
61 */ 54 */
62size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { 55bool Archive_RomFS::DeleteDirectory(const FileSys::Path& path) const {
63 DEBUG_LOG(FILESYS, "called offset=%llu, length=%d", offset, length); 56 LOG_WARNING(Service_FS, "Attempted to delete a directory from ROMFS.");
64 memcpy(buffer, &raw_data[(u32)offset], length); 57 return false;
65 return length;
66} 58}
67 59
68/** 60/**
69 * Write data to the archive 61 * Create a directory specified by its path
70 * @param offset Offset in bytes to start writing data to 62 * @param path Path relative to the archive
71 * @param length Length in bytes of data to write to archive 63 * @return Whether the directory could be created
72 * @param buffer Buffer to write data from
73 * @param flush The flush parameters (0 == do not flush)
74 * @return Number of bytes written
75 */ 64 */
76size_t Archive_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) { 65bool Archive_RomFS::CreateDirectory(const Path& path) const {
77 ERROR_LOG(FILESYS, "Attempted to write to ROMFS."); 66 LOG_WARNING(Service_FS, "Attempted to create a directory in ROMFS.");
78 return 0; 67 return false;
79} 68}
80 69
81/** 70bool Archive_RomFS::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
82 * Get the size of the archive in bytes 71 LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
83 * @return Size of the archive in bytes 72 return false;
84 */
85size_t Archive_RomFS::GetSize() const {
86 return sizeof(u8) * raw_data.size();
87} 73}
88 74
89/** 75/**
90 * Set the size of the archive in bytes 76 * Open a directory specified by its path
77 * @param path Path relative to the archive
78 * @return Opened directory, or nullptr
91 */ 79 */
92void Archive_RomFS::SetSize(const u64 size) { 80std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const {
93 ERROR_LOG(FILESYS, "Attempted to set the size of ROMFS"); 81 return std::make_unique<Directory_RomFS>();
94} 82}
95 83
96} // namespace FileSys 84} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
index ae2344e82..5b1ee6332 100644
--- a/src/core/file_sys/archive_romfs.h
+++ b/src/core/file_sys/archive_romfs.h
@@ -8,7 +8,7 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11#include "core/file_sys/archive.h" 11#include "core/file_sys/archive_backend.h"
12#include "core/loader/loader.h" 12#include "core/loader/loader.h"
13 13
14//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -17,16 +17,11 @@
17namespace FileSys { 17namespace FileSys {
18 18
19/// File system interface to the RomFS archive 19/// File system interface to the RomFS archive
20class Archive_RomFS final : public Archive { 20class Archive_RomFS final : public ArchiveBackend {
21public: 21public:
22 Archive_RomFS(const Loader::AppLoader& app_loader); 22 Archive_RomFS(const Loader::AppLoader& app_loader);
23 ~Archive_RomFS() override;
24 23
25 /** 24 std::string GetName() const override { return "RomFS"; }
26 * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
27 * @return IdCode of the archive
28 */
29 IdCode GetIdCode() const override { return IdCode::RomFS; };
30 25
31 /** 26 /**
32 * Open a file specified by its path, using the specified mode 27 * Open a file specified by its path, using the specified mode
@@ -34,53 +29,55 @@ public:
34 * @param mode Mode to open the file with 29 * @param mode Mode to open the file with
35 * @return Opened file, or nullptr 30 * @return Opened file, or nullptr
36 */ 31 */
37 std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override; 32 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
38 33
39 /** 34 /**
40 * Create a directory specified by its path 35 * Delete a file specified by its path
41 * @param path Path relative to the archive 36 * @param path Path relative to the archive
42 * @return Whether the directory could be created 37 * @return Whether the file could be deleted
43 */ 38 */
44 bool CreateDirectory(const std::string& path) const override; 39 bool DeleteFile(const FileSys::Path& path) const override;
45 40
46 /** 41 /**
47 * Open a directory specified by its path 42 * Rename a File specified by its path
48 * @param path Path relative to the archive 43 * @param src_path Source path relative to the archive
49 * @return Opened directory, or nullptr 44 * @param dest_path Destination path relative to the archive
45 * @return Whether rename succeeded
50 */ 46 */
51 std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override; 47 bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
52 48
53 /** 49 /**
54 * Read data from the archive 50 * Delete a directory specified by its path
55 * @param offset Offset in bytes to start reading data from 51 * @param path Path relative to the archive
56 * @param length Length in bytes of data to read from archive 52 * @return Whether the directory could be deleted
57 * @param buffer Buffer to read data into
58 * @return Number of bytes read
59 */ 53 */
60 size_t Read(const u64 offset, const u32 length, u8* buffer) const override; 54 bool DeleteDirectory(const FileSys::Path& path) const override;
61 55
62 /** 56 /**
63 * Write data to the archive 57 * Create a directory specified by its path
64 * @param offset Offset in bytes to start writing data to 58 * @param path Path relative to the archive
65 * @param length Length in bytes of data to write to archive 59 * @return Whether the directory could be created
66 * @param buffer Buffer to write data from
67 * @param flush The flush parameters (0 == do not flush)
68 * @return Number of bytes written
69 */ 60 */
70 size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override; 61 bool CreateDirectory(const Path& path) const override;
71 62
72 /** 63 /**
73 * Get the size of the archive in bytes 64 * Rename a Directory specified by its path
74 * @return Size of the archive in bytes 65 * @param src_path Source path relative to the archive
66 * @param dest_path Destination path relative to the archive
67 * @return Whether rename succeeded
75 */ 68 */
76 size_t GetSize() const override; 69 bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
77 70
78 /** 71 /**
79 * Set the size of the archive in bytes 72 * Open a directory specified by its path
73 * @param path Path relative to the archive
74 * @return Opened directory, or nullptr
80 */ 75 */
81 void SetSize(const u64 size) override; 76 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
82 77
83private: 78private:
79 friend class File_RomFS;
80
84 std::vector<u8> raw_data; 81 std::vector<u8> raw_data;
85}; 82};
86 83
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
new file mode 100644
index 000000000..2414564e4
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -0,0 +1,33 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/archive_savedata.h"
11#include "core/file_sys/disk_archive.h"
12#include "core/settings.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id)
20 : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) {
21 LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
22}
23
24bool Archive_SaveData::Initialize() {
25 if (!FileUtil::CreateFullPath(mount_point)) {
26 LOG_ERROR(Service_FS, "Unable to create SaveData path.");
27 return false;
28 }
29
30 return true;
31}
32
33} // 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 000000000..b3e561130
--- /dev/null
+++ b/src/core/file_sys/archive_savedata.h
@@ -0,0 +1,32 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/disk_archive.h"
10#include "core/loader/loader.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// FileSys namespace
14
15namespace FileSys {
16
17/// File system interface to the SaveData archive
18class Archive_SaveData final : public DiskArchive {
19public:
20 Archive_SaveData(const std::string& mount_point, u64 program_id);
21
22 /**
23 * Initialize the archive.
24 * @return CreateSaveDataResult AlreadyExists if the SaveData folder already exists,
25 * Success if it was created properly and Failure if there was any error
26 */
27 bool Initialize();
28
29 std::string GetName() const override { return "SaveData"; }
30};
31
32} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 66931e93e..dccdf7f67 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -8,8 +8,7 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9 9
10#include "core/file_sys/archive_sdmc.h" 10#include "core/file_sys/archive_sdmc.h"
11#include "core/file_sys/directory_sdmc.h" 11#include "core/file_sys/disk_archive.h"
12#include "core/file_sys/file_sdmc.h"
13#include "core/settings.h" 12#include "core/settings.h"
14 13
15//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -17,113 +16,22 @@
17 16
18namespace FileSys { 17namespace FileSys {
19 18
20Archive_SDMC::Archive_SDMC(const std::string& mount_point) { 19Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) {
21 this->mount_point = mount_point; 20 LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str());
22 DEBUG_LOG(FILESYS, "Directory %s set as SDMC.", mount_point.c_str());
23} 21}
24 22
25Archive_SDMC::~Archive_SDMC() {
26}
27
28/**
29 * Initialize the archive.
30 * @return true if it initialized successfully
31 */
32bool Archive_SDMC::Initialize() { 23bool Archive_SDMC::Initialize() {
33 if (!Settings::values.use_virtual_sd) { 24 if (!Settings::values.use_virtual_sd) {
34 WARN_LOG(FILESYS, "SDMC disabled by config."); 25 LOG_WARNING(Service_FS, "SDMC disabled by config.");
35 return false; 26 return false;
36 } 27 }
37 28
38 if (!FileUtil::CreateFullPath(mount_point)) { 29 if (!FileUtil::CreateFullPath(mount_point)) {
39 WARN_LOG(FILESYS, "Unable to create SDMC path."); 30 LOG_ERROR(Service_FS, "Unable to create SDMC path.");
40 return false; 31 return false;
41 } 32 }
42 33
43 return true; 34 return true;
44} 35}
45 36
46/**
47 * Open a file specified by its path, using the specified mode
48 * @param path Path relative to the archive
49 * @param mode Mode to open the file with
50 * @return Opened file, or nullptr
51 */
52std::unique_ptr<File> Archive_SDMC::OpenFile(const std::string& path, const Mode mode) const {
53 DEBUG_LOG(FILESYS, "called path=%s mode=%d", path.c_str(), mode);
54 File_SDMC* file = new File_SDMC(this, path, mode);
55 if (!file->Open())
56 return nullptr;
57 return std::unique_ptr<File>(file);
58}
59
60/**
61 * Create a directory specified by its path
62 * @param path Path relative to the archive
63 * @return Whether the directory could be created
64 */
65bool Archive_SDMC::CreateDirectory(const std::string& path) const {
66 return FileUtil::CreateDir(GetMountPoint() + path);
67}
68
69/**
70 * Open a directory specified by its path
71 * @param path Path relative to the archive
72 * @return Opened directory, or nullptr
73 */
74std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const std::string& path) const {
75 DEBUG_LOG(FILESYS, "called path=%s", path.c_str());
76 Directory_SDMC* directory = new Directory_SDMC(this, path);
77 return std::unique_ptr<Directory>(directory);
78}
79
80/**
81 * Read data from the archive
82 * @param offset Offset in bytes to start reading archive from
83 * @param length Length in bytes to read data from archive
84 * @param buffer Buffer to read data into
85 * @return Number of bytes read
86 */
87size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
88 ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
89 return -1;
90}
91
92/**
93 * Write data to the archive
94 * @param offset Offset in bytes to start writing data to
95 * @param length Length in bytes of data to write to archive
96 * @param buffer Buffer to write data from
97 * @param flush The flush parameters (0 == do not flush)
98 * @return Number of bytes written
99 */
100size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
101 ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
102 return -1;
103}
104
105/**
106 * Get the size of the archive in bytes
107 * @return Size of the archive in bytes
108 */
109size_t Archive_SDMC::GetSize() const {
110 ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
111 return 0;
112}
113
114/**
115 * Set the size of the archive in bytes
116 */
117void Archive_SDMC::SetSize(const u64 size) {
118 ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
119}
120
121/**
122 * Getter for the path used for this Archive
123 * @return Mount point of that passthrough archive
124 */
125std::string Archive_SDMC::GetMountPoint() const {
126 return mount_point;
127}
128
129} // namespace FileSys 37} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 0e059b635..c84c6948e 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -6,7 +6,7 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9#include "core/file_sys/archive.h" 9#include "core/file_sys/disk_archive.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12//////////////////////////////////////////////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,10 +15,9 @@
15namespace FileSys { 15namespace FileSys {
16 16
17/// File system interface to the SDMC archive 17/// File system interface to the SDMC archive
18class Archive_SDMC final : public Archive { 18class Archive_SDMC final : public DiskArchive {
19public: 19public:
20 Archive_SDMC(const std::string& mount_point); 20 Archive_SDMC(const std::string& mount_point);
21 ~Archive_SDMC() override;
22 21
23 /** 22 /**
24 * Initialize the archive. 23 * Initialize the archive.
@@ -26,72 +25,7 @@ public:
26 */ 25 */
27 bool Initialize(); 26 bool Initialize();
28 27
29 /** 28 std::string GetName() const override { return "SDMC"; }
30 * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
31 * @return IdCode of the archive
32 */
33 IdCode GetIdCode() const override { return IdCode::SDMC; };
34
35 /**
36 * Open a file specified by its path, using the specified mode
37 * @param path Path relative to the archive
38 * @param mode Mode to open the file with
39 * @return Opened file, or nullptr
40 */
41 std::unique_ptr<File> OpenFile(const std::string& path, const Mode mode) const override;
42
43 /**
44 * Create a directory specified by its path
45 * @param path Path relative to the archive
46 * @return Whether the directory could be created
47 */
48 bool CreateDirectory(const std::string& path) const override;
49
50 /**
51 * Open a directory specified by its path
52 * @param path Path relative to the archive
53 * @return Opened directory, or nullptr
54 */
55 std::unique_ptr<Directory> OpenDirectory(const std::string& path) const override;
56
57 /**
58 * Read data from the archive
59 * @param offset Offset in bytes to start reading archive from
60 * @param length Length in bytes to read data from archive
61 * @param buffer Buffer to read data into
62 * @return Number of bytes read
63 */
64 size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
65
66 /**
67 * Write data to the archive
68 * @param offset Offset in bytes to start writing data to
69 * @param length Length in bytes of data to write to archive
70 * @param buffer Buffer to write data from
71 * @param flush The flush parameters (0 == do not flush)
72 * @return Number of bytes written
73 */
74 size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
75
76 /**
77 * Get the size of the archive in bytes
78 * @return Size of the archive in bytes
79 */
80 size_t GetSize() const override;
81
82 /**
83 * Set the size of the archive in bytes
84 */
85 void SetSize(const u64 size) override;
86
87 /**
88 * Getter for the path used for this Archive
89 * @return Mount point of that passthrough archive
90 */
91 std::string GetMountPoint() const;
92
93private:
94 std::string mount_point;
95}; 29};
96 30
97} // namespace FileSys 31} // namespace FileSys
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory_backend.h
index e10431337..188746a6f 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
36static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry."); 36static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry.");
37static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry."); 37static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry.");
38 38
39class Directory : NonCopyable { 39class DirectoryBackend : NonCopyable {
40public: 40public:
41 Directory() { } 41 DirectoryBackend() { }
42 virtual ~Directory() { } 42 virtual ~DirectoryBackend() { }
43
44 /**
45 * Open the directory
46 * @return true if the directory opened correctly
47 */
48 virtual bool Open() = 0;
43 49
44 /** 50 /**
45 * List files contained in the directory 51 * 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 4e8f4c04d..e6d571391 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() {
17Directory_RomFS::~Directory_RomFS() { 17Directory_RomFS::~Directory_RomFS() {
18} 18}
19 19
20bool Directory_RomFS::Open() {
21 return false;
22}
23
20/** 24/**
21 * List files contained in the directory 25 * List files contained in the directory
22 * @param count Number of entries to return at once in entries 26 * @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 4b71c4b13..b775f014d 100644
--- a/src/core/file_sys/directory_romfs.h
+++ b/src/core/file_sys/directory_romfs.h
@@ -6,7 +6,7 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9#include "core/file_sys/directory.h" 9#include "core/file_sys/directory_backend.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12//////////////////////////////////////////////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -14,12 +14,18 @@
14 14
15namespace FileSys { 15namespace FileSys {
16 16
17class Directory_RomFS final : public Directory { 17class Directory_RomFS final : public DirectoryBackend {
18public: 18public:
19 Directory_RomFS(); 19 Directory_RomFS();
20 ~Directory_RomFS() override; 20 ~Directory_RomFS() override;
21 21
22 /** 22 /**
23 * Open the directory
24 * @return true if the directory opened correctly
25 */
26 bool Open() override;
27
28 /**
23 * List files contained in the directory 29 * List files contained in the directory
24 * @param count Number of entries to return at once in entries 30 * @param count Number of entries to return at once in entries
25 * @param entries Buffer to read data into 31 * @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 fd558def9..000000000
--- a/src/core/file_sys/directory_sdmc.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/directory_sdmc.h"
11#include "core/file_sys/archive_sdmc.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const std::string& path) {
19 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
20 // the root directory we set while opening the archive.
21 // For example, opening /../../usr/bin can give the emulated program your installed programs.
22 std::string absolute_path = archive->GetMountPoint() + path;
23 FileUtil::ScanDirectoryTree(absolute_path, directory);
24 children_iterator = directory.children.begin();
25}
26
27Directory_SDMC::~Directory_SDMC() {
28 Close();
29}
30
31/**
32 * List files contained in the directory
33 * @param count Number of entries to return at once in entries
34 * @param entries Buffer to read data into
35 * @return Number of entries listed
36 */
37u32 Directory_SDMC::Read(const u32 count, Entry* entries) {
38 u32 entries_read = 0;
39
40 while (entries_read < count && children_iterator != directory.children.cend()) {
41 const FileUtil::FSTEntry& file = *children_iterator;
42 const std::string& filename = file.virtualName;
43 Entry& entry = entries[entries_read];
44
45 WARN_LOG(FILESYS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
46
47 // TODO(Link Mauve): use a proper conversion to UTF-16.
48 for (int j = 0; j < FILENAME_LENGTH; ++j) {
49 entry.filename[j] = filename[j];
50 if (!filename[j])
51 break;
52 }
53
54 FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
55
56 entry.is_directory = file.isDirectory;
57 entry.is_hidden = (filename[0] == '.');
58 entry.is_read_only = 0;
59 entry.file_size = file.size;
60
61 // We emulate a SD card where the archive bit has never been cleared, as it would be on
62 // most user SD cards.
63 // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
64 // file bit.
65 entry.is_archive = !file.isDirectory;
66
67 ++entries_read;
68 ++children_iterator;
69 }
70 return entries_read;
71}
72
73/**
74 * Close the directory
75 * @return true if the directory closed correctly
76 */
77bool Directory_SDMC::Close() const {
78 return true;
79}
80
81} // 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 cb8d32fda..000000000
--- a/src/core/file_sys/directory_sdmc.h
+++ /dev/null
@@ -1,48 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/directory.h"
11#include "core/file_sys/archive_sdmc.h"
12#include "core/loader/loader.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19class Directory_SDMC final : public Directory {
20public:
21 Directory_SDMC();
22 Directory_SDMC(const Archive_SDMC* archive, const std::string& path);
23 ~Directory_SDMC() override;
24
25 /**
26 * List files contained in the directory
27 * @param count Number of entries to return at once in entries
28 * @param entries Buffer to read data into
29 * @return Number of entries listed
30 */
31 u32 Read(const u32 count, Entry* entries) override;
32
33 /**
34 * Close the directory
35 * @return true if the directory closed correctly
36 */
37 bool Close() const override;
38
39private:
40 u32 total_entries_in_directory;
41 FileUtil::FSTEntry directory;
42
43 // We need to remember the last entry we returned, so a subsequent call to Read will continue
44 // from the next one. This iterator will always point to the next unread entry.
45 std::vector<FileUtil::FSTEntry>::iterator children_iterator;
46};
47
48} // 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 000000000..eabf58057
--- /dev/null
+++ b/src/core/file_sys/disk_archive.cpp
@@ -0,0 +1,167 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/disk_archive.h"
11#include "core/settings.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
19 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
20 DiskFile* file = new DiskFile(this, path, mode);
21 if (!file->Open())
22 return nullptr;
23 return std::unique_ptr<FileBackend>(file);
24}
25
26bool DiskArchive::DeleteFile(const FileSys::Path& path) const {
27 return FileUtil::Delete(GetMountPoint() + path.AsString());
28}
29
30bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
31 return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
32}
33
34bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const {
35 return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
36}
37
38bool DiskArchive::CreateDirectory(const Path& path) const {
39 return FileUtil::CreateDir(GetMountPoint() + path.AsString());
40}
41
42bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const {
43 return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
44}
45
46std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
47 LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
48 DiskDirectory* directory = new DiskDirectory(this, path);
49 if (!directory->Open())
50 return nullptr;
51 return std::unique_ptr<DirectoryBackend>(directory);
52}
53
54////////////////////////////////////////////////////////////////////////////////////////////////////
55
56DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) {
57 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
58 // the root directory we set while opening the archive.
59 // For example, opening /../../etc/passwd can give the emulated program your users list.
60 this->path = archive->GetMountPoint() + path.AsString();
61 this->mode.hex = mode.hex;
62 this->archive = archive;
63}
64
65bool DiskFile::Open() {
66 if (!mode.create_flag && !FileUtil::Exists(path)) {
67 LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str());
68 return false;
69 }
70
71 std::string mode_string;
72 if (mode.create_flag)
73 mode_string = "w+";
74 else if (mode.write_flag)
75 mode_string = "r+"; // Files opened with Write access can be read from
76 else if (mode.read_flag)
77 mode_string = "r";
78
79 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
80 mode_string += "b";
81
82 file = new FileUtil::IOFile(path, mode_string.c_str());
83 return true;
84}
85
86size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const {
87 file->Seek(offset, SEEK_SET);
88 return file->ReadBytes(buffer, length);
89}
90
91size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
92 file->Seek(offset, SEEK_SET);
93 size_t written = file->WriteBytes(buffer, length);
94 if (flush)
95 file->Flush();
96 return written;
97}
98
99size_t DiskFile::GetSize() const {
100 return static_cast<size_t>(file->GetSize());
101}
102
103bool DiskFile::SetSize(const u64 size) const {
104 file->Resize(size);
105 file->Flush();
106 return true;
107}
108
109bool DiskFile::Close() const {
110 return file->Close();
111}
112
113////////////////////////////////////////////////////////////////////////////////////////////////////
114
115DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) {
116 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
117 // the root directory we set while opening the archive.
118 // For example, opening /../../usr/bin can give the emulated program your installed programs.
119 this->path = archive->GetMountPoint() + path.AsString();
120 this->archive = archive;
121}
122
123bool DiskDirectory::Open() {
124 if (!FileUtil::IsDirectory(path))
125 return false;
126 FileUtil::ScanDirectoryTree(path, directory);
127 children_iterator = directory.children.begin();
128 return true;
129}
130
131u32 DiskDirectory::Read(const u32 count, Entry* entries) {
132 u32 entries_read = 0;
133
134 while (entries_read < count && children_iterator != directory.children.cend()) {
135 const FileUtil::FSTEntry& file = *children_iterator;
136 const std::string& filename = file.virtualName;
137 Entry& entry = entries[entries_read];
138
139 LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
140
141 // TODO(Link Mauve): use a proper conversion to UTF-16.
142 for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
143 entry.filename[j] = filename[j];
144 if (!filename[j])
145 break;
146 }
147
148 FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
149
150 entry.is_directory = file.isDirectory;
151 entry.is_hidden = (filename[0] == '.');
152 entry.is_read_only = 0;
153 entry.file_size = file.size;
154
155 // We emulate a SD card where the archive bit has never been cleared, as it would be on
156 // most user SD cards.
157 // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
158 // file bit.
159 entry.is_archive = !file.isDirectory;
160
161 ++entries_read;
162 ++children_iterator;
163 }
164 return entries_read;
165}
166
167} // 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 000000000..778c83953
--- /dev/null
+++ b/src/core/file_sys/disk_archive.h
@@ -0,0 +1,101 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/archive_backend.h"
10#include "core/loader/loader.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// FileSys namespace
14
15namespace FileSys {
16
17/**
18 * Helper which implements a backend accessing the host machine's filesystem.
19 * This should be subclassed by concrete archive types, which will provide the
20 * base directory on the host filesystem and override any required functionality.
21 */
22class DiskArchive : public ArchiveBackend {
23public:
24 DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
25
26 virtual std::string GetName() const = 0;
27 std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
28 bool DeleteFile(const FileSys::Path& path) const override;
29 bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
30 bool DeleteDirectory(const FileSys::Path& path) const override;
31 bool CreateDirectory(const Path& path) const override;
32 bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override;
33 std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
34
35 /**
36 * Getter for the path used for this Archive
37 * @return Mount point of that passthrough archive
38 */
39 const std::string& GetMountPoint() const {
40 return mount_point;
41 }
42
43protected:
44 std::string mount_point;
45};
46
47class DiskFile : public FileBackend {
48public:
49 DiskFile();
50 DiskFile(const DiskArchive* archive, const Path& path, const Mode mode);
51
52 ~DiskFile() override {
53 Close();
54 }
55
56 bool Open() override;
57 size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
58 size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
59 size_t GetSize() const override;
60 bool SetSize(const u64 size) const override;
61 bool Close() const override;
62
63 void Flush() const override {
64 file->Flush();
65 }
66
67protected:
68 const DiskArchive* archive;
69 std::string path;
70 Mode mode;
71 FileUtil::IOFile* file;
72};
73
74class DiskDirectory : public DirectoryBackend {
75public:
76 DiskDirectory();
77 DiskDirectory(const DiskArchive* archive, const Path& path);
78
79 ~DiskDirectory() override {
80 Close();
81 }
82
83 bool Open() override;
84 u32 Read(const u32 count, Entry* entries) override;
85
86 bool Close() const override {
87 return true;
88 }
89
90protected:
91 const DiskArchive* archive;
92 std::string path;
93 u32 total_entries_in_directory;
94 FileUtil::FSTEntry directory;
95
96 // We need to remember the last entry we returned, so a subsequent call to Read will continue
97 // from the next one. This iterator will always point to the next unread entry.
98 std::vector<FileUtil::FSTEntry>::iterator children_iterator;
99};
100
101} // namespace FileSys
diff --git a/src/core/file_sys/file.h b/src/core/file_sys/file_backend.h
index 4013b6c3e..539ec7314 100644
--- a/src/core/file_sys/file.h
+++ b/src/core/file_sys/file_backend.h
@@ -13,10 +13,10 @@
13 13
14namespace FileSys { 14namespace FileSys {
15 15
16class File : NonCopyable { 16class FileBackend : NonCopyable {
17public: 17public:
18 File() { } 18 FileBackend() { }
19 virtual ~File() { } 19 virtual ~FileBackend() { }
20 20
21 /** 21 /**
22 * Open the file 22 * Open the file
@@ -61,6 +61,11 @@ public:
61 * @return true if the file closed correctly 61 * @return true if the file closed correctly
62 */ 62 */
63 virtual bool Close() const = 0; 63 virtual bool Close() const = 0;
64
65 /**
66 * Flushes the file
67 */
68 virtual void Flush() const = 0;
64}; 69};
65 70
66} // namespace FileSys 71} // namespace FileSys
diff --git a/src/core/file_sys/file_romfs.cpp b/src/core/file_sys/file_romfs.cpp
index b55708df4..5f38c2704 100644
--- a/src/core/file_sys/file_romfs.cpp
+++ b/src/core/file_sys/file_romfs.cpp
@@ -5,24 +5,19 @@
5#include "common/common_types.h" 5#include "common/common_types.h"
6 6
7#include "core/file_sys/file_romfs.h" 7#include "core/file_sys/file_romfs.h"
8#include "core/file_sys/archive_romfs.h"
8 9
9//////////////////////////////////////////////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////////////////////////////////////////////
10// FileSys namespace 11// FileSys namespace
11 12
12namespace FileSys { 13namespace FileSys {
13 14
14File_RomFS::File_RomFS() {
15}
16
17File_RomFS::~File_RomFS() {
18}
19
20/** 15/**
21 * Open the file 16 * Open the file
22 * @return true if the file opened correctly 17 * @return true if the file opened correctly
23 */ 18 */
24bool File_RomFS::Open() { 19bool File_RomFS::Open() {
25 return false; 20 return true;
26} 21}
27 22
28/** 23/**
@@ -33,7 +28,9 @@ bool File_RomFS::Open() {
33 * @return Number of bytes read 28 * @return Number of bytes read
34 */ 29 */
35size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const { 30size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
36 return -1; 31 LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length);
32 memcpy(buffer, &archive->raw_data[(u32)offset], length);
33 return length;
37} 34}
38 35
39/** 36/**
@@ -45,7 +42,8 @@ size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
45 * @return Number of bytes written 42 * @return Number of bytes written
46 */ 43 */
47size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { 44size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
48 return -1; 45 LOG_WARNING(Service_FS, "Attempted to write to ROMFS.");
46 return 0;
49} 47}
50 48
51/** 49/**
@@ -53,7 +51,7 @@ size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, co
53 * @return Size of the file in bytes 51 * @return Size of the file in bytes
54 */ 52 */
55size_t File_RomFS::GetSize() const { 53size_t File_RomFS::GetSize() const {
56 return -1; 54 return sizeof(u8) * archive->raw_data.size();
57} 55}
58 56
59/** 57/**
@@ -62,6 +60,7 @@ size_t File_RomFS::GetSize() const {
62 * @return true if successful 60 * @return true if successful
63 */ 61 */
64bool File_RomFS::SetSize(const u64 size) const { 62bool File_RomFS::SetSize(const u64 size) const {
63 LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS");
65 return false; 64 return false;
66} 65}
67 66
diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h
index 5196701d3..32fa6b6d3 100644
--- a/src/core/file_sys/file_romfs.h
+++ b/src/core/file_sys/file_romfs.h
@@ -6,7 +6,7 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9#include "core/file_sys/file.h" 9#include "core/file_sys/file_backend.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12//////////////////////////////////////////////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -14,10 +14,11 @@
14 14
15namespace FileSys { 15namespace FileSys {
16 16
17class File_RomFS final : public File { 17class Archive_RomFS;
18
19class File_RomFS final : public FileBackend {
18public: 20public:
19 File_RomFS(); 21 File_RomFS(const Archive_RomFS* archive) : archive(archive) {}
20 ~File_RomFS() override;
21 22
22 /** 23 /**
23 * Open the file 24 * Open the file
@@ -62,6 +63,11 @@ public:
62 * @return true if the file closed correctly 63 * @return true if the file closed correctly
63 */ 64 */
64 bool Close() const override; 65 bool Close() const override;
66
67 void Flush() const override { }
68
69private:
70 const Archive_RomFS* archive;
65}; 71};
66 72
67} // namespace FileSys 73} // 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 26204392c..000000000
--- a/src/core/file_sys/file_sdmc.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <sys/stat.h>
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/file_sdmc.h"
11#include "core/file_sys/archive_sdmc.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18File_SDMC::File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode) {
19 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
20 // the root directory we set while opening the archive.
21 // For example, opening /../../etc/passwd can give the emulated program your users list.
22 this->path = archive->GetMountPoint() + path;
23 this->mode.hex = mode.hex;
24}
25
26File_SDMC::~File_SDMC() {
27 Close();
28}
29
30/**
31 * Open the file
32 * @return true if the file opened correctly
33 */
34bool File_SDMC::Open() {
35 if (!mode.create_flag && !FileUtil::Exists(path)) {
36 ERROR_LOG(FILESYS, "Non-existing file %s can’t be open without mode create.", path.c_str());
37 return false;
38 }
39
40 std::string mode_string;
41 if (mode.read_flag && mode.write_flag)
42 mode_string = "w+";
43 else if (mode.read_flag)
44 mode_string = "r";
45 else if (mode.write_flag)
46 mode_string = "w";
47
48 file = new FileUtil::IOFile(path, mode_string.c_str());
49 return true;
50}
51
52/**
53 * Read data from the file
54 * @param offset Offset in bytes to start reading data from
55 * @param length Length in bytes of data to read from file
56 * @param buffer Buffer to read data into
57 * @return Number of bytes read
58 */
59size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
60 file->Seek(offset, SEEK_SET);
61 return file->ReadBytes(buffer, length);
62}
63
64/**
65 * Write data to the file
66 * @param offset Offset in bytes to start writing data to
67 * @param length Length in bytes of data to write to file
68 * @param flush The flush parameters (0 == do not flush)
69 * @param buffer Buffer to read data from
70 * @return Number of bytes written
71 */
72size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
73 file->Seek(offset, SEEK_SET);
74 size_t written = file->WriteBytes(buffer, length);
75 if (flush)
76 file->Flush();
77 return written;
78}
79
80/**
81 * Get the size of the file in bytes
82 * @return Size of the file in bytes
83 */
84size_t File_SDMC::GetSize() const {
85 return static_cast<size_t>(file->GetSize());
86}
87
88/**
89 * Set the size of the file in bytes
90 * @param size New size of the file
91 * @return true if successful
92 */
93bool File_SDMC::SetSize(const u64 size) const {
94 file->Resize(size);
95 file->Flush();
96 return true;
97}
98
99/**
100 * Close the file
101 * @return true if the file closed correctly
102 */
103bool File_SDMC::Close() const {
104 return file->Close();
105}
106
107} // 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 df032f7c0..000000000
--- a/src/core/file_sys/file_sdmc.h
+++ /dev/null
@@ -1,75 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/file_util.h"
9
10#include "core/file_sys/file.h"
11#include "core/file_sys/archive_sdmc.h"
12#include "core/loader/loader.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// FileSys namespace
16
17namespace FileSys {
18
19class File_SDMC final : public File {
20public:
21 File_SDMC();
22 File_SDMC(const Archive_SDMC* archive, const std::string& path, const Mode mode);
23 ~File_SDMC() override;
24
25 /**
26 * Open the file
27 * @return true if the file opened correctly
28 */
29 bool Open() override;
30
31 /**
32 * Read data from the file
33 * @param offset Offset in bytes to start reading data from
34 * @param length Length in bytes of data to read from file
35 * @param buffer Buffer to read data into
36 * @return Number of bytes read
37 */
38 size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
39
40 /**
41 * Write data to the file
42 * @param offset Offset in bytes to start writing data to
43 * @param length Length in bytes of data to write to file
44 * @param flush The flush parameters (0 == do not flush)
45 * @param buffer Buffer to read data from
46 * @return Number of bytes written
47 */
48 size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
49
50 /**
51 * Get the size of the file in bytes
52 * @return Size of the file in bytes
53 */
54 size_t GetSize() const override;
55
56 /**
57 * Set the size of the file in bytes
58 * @param size New size of the file
59 * @return true if successful
60 */
61 bool SetSize(const u64 size) const override;
62
63 /**
64 * Close the file
65 * @return true if the file closed correctly
66 */
67 bool Close() const override;
68
69private:
70 std::string path;
71 Mode mode;
72 FileUtil::IOFile* file;
73};
74
75} // namespace FileSys
diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp
index a45e61427..d8ba9e6cf 100644
--- a/src/core/hle/config_mem.cpp
+++ b/src/core/hle/config_mem.cpp
@@ -1,8 +1,9 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_types.h" 5#include "common/common_types.h"
6#include "common/log.h"
6 7
7#include "core/hle/config_mem.h" 8#include "core/hle/config_mem.h"
8 9
@@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
54 break; 55 break;
55 56
56 default: 57 default:
57 ERROR_LOG(HLE, "unknown addr=0x%08X", addr); 58 LOG_ERROR(Kernel, "unknown addr=0x%08X", addr);
58 } 59 }
59} 60}
60 61
diff --git a/src/core/hle/coprocessor.cpp b/src/core/hle/coprocessor.cpp
index 1eb33eb86..e34229a57 100644
--- a/src/core/hle/coprocessor.cpp
+++ b/src/core/hle/coprocessor.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/hle/coprocessor.h" 5#include "core/hle/coprocessor.h"
6#include "core/hle/hle.h" 6#include "core/hle/hle.h"
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 55eaf0621..b44479b2f 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(){
50 50
51template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() { 51template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() {
52 s32 param_1 = 0; 52 s32 param_1 = 0;
53 s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), 53 s32 retval = func(&param_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2),
54 (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))); 54 (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0)));
55 Core::g_app_core->SetReg(1, (u32)param_1); 55 Core::g_app_core->SetReg(1, (u32)param_1);
56 FuncReturn(retval); 56 FuncReturn(retval);
@@ -103,7 +103,7 @@ template<s32 func(void*)> void Wrap() {
103} 103}
104 104
105template<s32 func(s64*, u32, void*, s32)> void Wrap(){ 105template<s32 func(s64*, u32, void*, s32)> void Wrap(){
106 FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), 106 FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)),
107 (s32)PARAM(3))); 107 (s32)PARAM(3)));
108} 108}
109 109
@@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() {
114 FuncReturn(retval); 114 FuncReturn(retval);
115} 115}
116 116
117template<s32 func(u32*, s32, s32)> void Wrap() {
118 u32 param_1 = 0;
119 u32 retval = func(&param_1, PARAM(1), PARAM(2));
120 Core::g_app_core->SetReg(1, param_1);
121 FuncReturn(retval);
122}
123
124template<s32 func(s32*, u32, s32)> void Wrap() {
125 s32 param_1 = 0;
126 u32 retval = func(&param_1, PARAM(1), PARAM(2));
127 Core::g_app_core->SetReg(1, param_1);
128 FuncReturn(retval);
129}
130
117//////////////////////////////////////////////////////////////////////////////////////////////////// 131////////////////////////////////////////////////////////////////////////////////////////////////////
118// Function wrappers that return type u32 132// Function wrappers that return type u32
119 133
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index b03894ad7..cc3d5255a 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <vector> 5#include <vector>
6 6
@@ -8,6 +8,7 @@
8#include "core/hle/hle.h" 8#include "core/hle/hle.h"
9#include "core/hle/kernel/thread.h" 9#include "core/hle/kernel/thread.h"
10#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
11#include "core/hle/service/fs/archive.h"
11 12
12//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
13 14
@@ -20,7 +21,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n
20const FunctionDef* GetSVCInfo(u32 opcode) { 21const FunctionDef* GetSVCInfo(u32 opcode) {
21 u32 func_num = opcode & 0xFFFFFF; // 8 bits 22 u32 func_num = opcode & 0xFFFFFF; // 8 bits
22 if (func_num > 0xFF) { 23 if (func_num > 0xFF) {
23 ERROR_LOG(HLE,"unknown svc=0x%02X", func_num); 24 LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num);
24 return nullptr; 25 return nullptr;
25 } 26 }
26 return &g_module_db[0].func_table[func_num]; 27 return &g_module_db[0].func_table[func_num];
@@ -35,14 +36,12 @@ void CallSVC(u32 opcode) {
35 if (info->func) { 36 if (info->func) {
36 info->func(); 37 info->func();
37 } else { 38 } else {
38 ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str()); 39 LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str());
39 } 40 }
40} 41}
41 42
42void Reschedule(const char *reason) { 43void Reschedule(const char *reason) {
43#ifdef _DEBUG 44 _dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
44 _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
45#endif
46 Core::g_app_core->PrepareReschedule(); 45 Core::g_app_core->PrepareReschedule();
47 g_reschedule = true; 46 g_reschedule = true;
48} 47}
@@ -58,18 +57,20 @@ void RegisterAllModules() {
58 57
59void Init() { 58void Init() {
60 Service::Init(); 59 Service::Init();
61 60 Service::FS::ArchiveInit();
61
62 RegisterAllModules(); 62 RegisterAllModules();
63 63
64 NOTICE_LOG(HLE, "initialized OK"); 64 LOG_DEBUG(Kernel, "initialized OK");
65} 65}
66 66
67void Shutdown() { 67void Shutdown() {
68 Service::FS::ArchiveShutdown();
68 Service::Shutdown(); 69 Service::Shutdown();
69 70
70 g_module_db.clear(); 71 g_module_db.clear();
71 72
72 NOTICE_LOG(HLE, "shutdown OK"); 73 LOG_DEBUG(Kernel, "shutdown OK");
73} 74}
74 75
75} // namespace 76} // namespace
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index bf4d84575..4ab258c69 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 2b21657da..9a921108d 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -24,23 +24,12 @@ public:
24 Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } 24 Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; }
25 25
26 std::string name; ///< Name of address arbiter object (optional) 26 std::string name; ///< Name of address arbiter object (optional)
27
28 /**
29 * Wait for kernel object to synchronize
30 * @param wait Boolean wait set if current thread should wait as a result of sync operation
31 * @return Result of operation, 0 on success, otherwise error code
32 */
33 Result WaitSynchronization(bool* wait) override {
34 // TODO(bunnei): ImplementMe
35 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
36 return 0;
37 }
38}; 27};
39 28
40//////////////////////////////////////////////////////////////////////////////////////////////////// 29////////////////////////////////////////////////////////////////////////////////////////////////////
41 30
42/// Arbitrate an address 31/// Arbitrate an address
43Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { 32ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
44 switch (type) { 33 switch (type) {
45 34
46 // Signal thread(s) waiting for arbitrate address... 35 // Signal thread(s) waiting for arbitrate address...
@@ -58,16 +47,16 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va
58 // Wait current thread (acquire the arbiter)... 47 // Wait current thread (acquire the arbiter)...
59 case ArbitrationType::WaitIfLessThan: 48 case ArbitrationType::WaitIfLessThan:
60 if ((s32)Memory::Read32(address) <= value) { 49 if ((s32)Memory::Read32(address) <= value) {
61 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); 50 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
62 HLE::Reschedule(__func__); 51 HLE::Reschedule(__func__);
63 } 52 }
64 break; 53 break;
65 54
66 default: 55 default:
67 ERROR_LOG(KERNEL, "unknown type=%d", type); 56 LOG_ERROR(Kernel, "unknown type=%d", type);
68 return -1; 57 return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
69 } 58 }
70 return 0; 59 return RESULT_SUCCESS;
71} 60}
72 61
73/// Create an address arbiter 62/// Create an address arbiter
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index a483fe466..8a5fb10b4 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -11,7 +11,7 @@
11// Address arbiters are an underlying kernel synchronization object that can be created/used via 11// Address arbiters are an underlying kernel synchronization object that can be created/used via
12// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR 12// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
13// applications use them as an underlying mechanism to implement thread-safe barriers, events, and 13// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
14// semphores. 14// semphores.
15 15
16//////////////////////////////////////////////////////////////////////////////////////////////////// 16////////////////////////////////////////////////////////////////////////////////////////////////////
17// Kernel namespace 17// Kernel namespace
@@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
28}; 28};
29 29
30/// Arbitrate an address 30/// Arbitrate an address
31Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); 31ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
32 32
33/// Create an address arbiter 33/// Create an address arbiter
34Handle CreateAddressArbiter(const std::string& name = "Unknown"); 34Handle 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 764082d71..000000000
--- a/src/core/hle/kernel/archive.cpp
+++ /dev/null
@@ -1,436 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/file_util.h"
7#include "common/math_util.h"
8
9#include "core/file_sys/archive.h"
10#include "core/file_sys/archive_sdmc.h"
11#include "core/file_sys/directory.h"
12#include "core/hle/service/service.h"
13#include "core/hle/kernel/archive.h"
14
15////////////////////////////////////////////////////////////////////////////////////////////////////
16// Kernel namespace
17
18namespace Kernel {
19
20// Command to access archive file
21enum class FileCommand : u32 {
22 Dummy1 = 0x000100C6,
23 Control = 0x040100C4,
24 OpenSubFile = 0x08010100,
25 Read = 0x080200C2,
26 Write = 0x08030102,
27 GetSize = 0x08040000,
28 SetSize = 0x08050080,
29 GetAttributes = 0x08060000,
30 SetAttributes = 0x08070040,
31 Close = 0x08080000,
32 Flush = 0x08090000,
33};
34
35// Command to access directory
36enum class DirectoryCommand : u32 {
37 Dummy1 = 0x000100C6,
38 Control = 0x040100C4,
39 Read = 0x08010042,
40 Close = 0x08020000,
41};
42
43class Archive : public Object {
44public:
45 std::string GetTypeName() const override { return "Archive"; }
46 std::string GetName() const override { return name; }
47
48 static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
49 Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
50
51 std::string name; ///< Name of archive (optional)
52 FileSys::Archive* backend; ///< Archive backend interface
53
54 /**
55 * Synchronize kernel object
56 * @param wait Boolean wait set if current thread should wait as a result of sync operation
57 * @return Result of operation, 0 on success, otherwise error code
58 */
59 Result SyncRequest(bool* wait) override {
60 u32* cmd_buff = Service::GetCommandBuffer();
61 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
62
63 switch (cmd) {
64 // Read from archive...
65 case FileCommand::Read:
66 {
67 u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
68 u32 length = cmd_buff[3];
69 u32 address = cmd_buff[5];
70
71 // Number of bytes read
72 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
73 break;
74 }
75 // Write to archive...
76 case FileCommand::Write:
77 {
78 u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
79 u32 length = cmd_buff[3];
80 u32 flush = cmd_buff[4];
81 u32 address = cmd_buff[6];
82
83 // Number of bytes written
84 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
85 break;
86 }
87 case FileCommand::GetSize:
88 {
89 u64 filesize = (u64) backend->GetSize();
90 cmd_buff[2] = (u32) filesize; // Lower word
91 cmd_buff[3] = (u32) (filesize >> 32); // Upper word
92 break;
93 }
94 case FileCommand::SetSize:
95 {
96 backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
97 break;
98 }
99 case FileCommand::Close:
100 {
101 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
102 Kernel::g_object_pool.Destroy<Archive>(GetHandle());
103 CloseArchive(backend->GetIdCode());
104 break;
105 }
106 // Unknown command...
107 default:
108 {
109 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
110 return -1;
111 }
112 }
113 cmd_buff[1] = 0; // No error
114 return 0;
115 }
116
117 /**
118 * Wait for kernel object to synchronize
119 * @param wait Boolean wait set if current thread should wait as a result of sync operation
120 * @return Result of operation, 0 on success, otherwise error code
121 */
122 Result WaitSynchronization(bool* wait) override {
123 // TODO(bunnei): ImplementMe
124 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
125 return 0;
126 }
127};
128
129class File : public Object {
130public:
131 std::string GetTypeName() const override { return "File"; }
132 std::string GetName() const override { return path; }
133
134 static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
135 Kernel::HandleType GetHandleType() const override { return HandleType::File; }
136
137 std::string path; ///< Path of the file
138 std::unique_ptr<FileSys::File> backend; ///< File backend interface
139
140 /**
141 * Synchronize kernel object
142 * @param wait Boolean wait set if current thread should wait as a result of sync operation
143 * @return Result of operation, 0 on success, otherwise error code
144 */
145 Result SyncRequest(bool* wait) override {
146 u32* cmd_buff = Service::GetCommandBuffer();
147 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
148 switch (cmd) {
149
150 // Read from file...
151 case FileCommand::Read:
152 {
153 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
154 u32 length = cmd_buff[3];
155 u32 address = cmd_buff[5];
156 DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x",
157 GetTypeName().c_str(), GetName().c_str(), offset, length, address);
158 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
159 break;
160 }
161
162 // Write to file...
163 case FileCommand::Write:
164 {
165 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
166 u32 length = cmd_buff[3];
167 u32 flush = cmd_buff[4];
168 u32 address = cmd_buff[6];
169 DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
170 GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
171 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
172 break;
173 }
174
175 case FileCommand::GetSize:
176 {
177 DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
178 u64 size = backend->GetSize();
179 cmd_buff[2] = (u32)size;
180 cmd_buff[3] = size >> 32;
181 break;
182 }
183
184 case FileCommand::SetSize:
185 {
186 u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
187 DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), size);
188 backend->SetSize(size);
189 break;
190 }
191
192 case FileCommand::Close:
193 {
194 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
195 Kernel::g_object_pool.Destroy<File>(GetHandle());
196 break;
197 }
198
199 // Unknown command...
200 default:
201 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
202 cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
203 return -1;
204 }
205 cmd_buff[1] = 0; // No error
206 return 0;
207 }
208
209 /**
210 * Wait for kernel object to synchronize
211 * @param wait Boolean wait set if current thread should wait as a result of sync operation
212 * @return Result of operation, 0 on success, otherwise error code
213 */
214 Result WaitSynchronization(bool* wait) override {
215 // TODO(bunnei): ImplementMe
216 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
217 return 0;
218 }
219};
220
221class Directory : public Object {
222public:
223 std::string GetTypeName() const override { return "Directory"; }
224 std::string GetName() const override { return path; }
225
226 static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
227 Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
228
229 std::string path; ///< Path of the directory
230 std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
231
232 /**
233 * Synchronize kernel object
234 * @param wait Boolean wait set if current thread should wait as a result of sync operation
235 * @return Result of operation, 0 on success, otherwise error code
236 */
237 Result SyncRequest(bool* wait) override {
238 u32* cmd_buff = Service::GetCommandBuffer();
239 DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
240 switch (cmd) {
241
242 // Read from directory...
243 case DirectoryCommand::Read:
244 {
245 u32 count = cmd_buff[1];
246 u32 address = cmd_buff[3];
247 FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
248 DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count);
249
250 // Number of entries actually read
251 cmd_buff[2] = backend->Read(count, entries);
252 break;
253 }
254
255 case DirectoryCommand::Close:
256 {
257 DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
258 Kernel::g_object_pool.Destroy<Directory>(GetHandle());
259 break;
260 }
261
262 // Unknown command...
263 default:
264 ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
265 cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
266 return -1;
267 }
268 cmd_buff[1] = 0; // No error
269 return 0;
270 }
271
272 /**
273 * Wait for kernel object to synchronize
274 * @param wait Boolean wait set if current thread should wait as a result of sync operation
275 * @return Result of operation, 0 on success, otherwise error code
276 */
277 Result WaitSynchronization(bool* wait) override {
278 // TODO(bunnei): ImplementMe
279 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
280 return 0;
281 }
282};
283
284////////////////////////////////////////////////////////////////////////////////////////////////////
285
286std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
287
288/**
289 * Opens an archive
290 * @param id_code IdCode of the archive to open
291 * @return Handle to archive if it exists, otherwise a null handle (0)
292 */
293Handle OpenArchive(FileSys::Archive::IdCode id_code) {
294 auto itr = g_archive_map.find(id_code);
295 if (itr == g_archive_map.end()) {
296 return 0;
297 }
298 return itr->second;
299}
300
301/**
302 * Closes an archive
303 * @param id_code IdCode of the archive to open
304 * @return Result of operation, 0 on success, otherwise error code
305 */
306Result CloseArchive(FileSys::Archive::IdCode id_code) {
307 if (1 != g_archive_map.erase(id_code)) {
308 ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code);
309 return -1;
310 }
311
312 INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
313 return 0;
314}
315
316/**
317 * Mounts an archive
318 * @param archive Pointer to the archive to mount
319 * @return Result of operation, 0 on success, otherwise error code
320 */
321Result MountArchive(Archive* archive) {
322 FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
323 if (0 != OpenArchive(id_code)) {
324 ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
325 return -1;
326 }
327 g_archive_map[id_code] = archive->GetHandle();
328 INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
329 return 0;
330}
331
332/**
333 * Creates an Archive
334 * @param handle Handle to newly created archive object
335 * @param backend File system backend interface to the archive
336 * @param name Optional name of Archive
337 * @return Newly created Archive object
338 */
339Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
340 Archive* archive = new Archive;
341 handle = Kernel::g_object_pool.Create(archive);
342 archive->name = name;
343 archive->backend = backend;
344
345 MountArchive(archive);
346
347 return archive;
348}
349
350/**
351 * Creates an Archive
352 * @param backend File system backend interface to the archive
353 * @param name Optional name of Archive
354 * @return Handle to newly created Archive object
355 */
356Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
357 Handle handle;
358 CreateArchive(handle, backend, name);
359 return handle;
360}
361
362/**
363 * Open a File from an Archive
364 * @param archive_handle Handle to an open Archive object
365 * @param path Path to the File inside of the Archive
366 * @param mode Mode under which to open the File
367 * @return Opened File object
368 */
369Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
370 File* file = new File;
371 Handle handle = Kernel::g_object_pool.Create(file);
372
373 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
374 file->path = path;
375 file->backend = archive->backend->OpenFile(path, mode);
376
377 if (!file->backend)
378 return 0;
379
380 return handle;
381}
382
383/**
384 * Create a Directory from an Archive
385 * @param archive_handle Handle to an open Archive object
386 * @param path Path to the Directory inside of the Archive
387 * @return Opened Directory object
388 */
389Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& path) {
390 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
391 if (archive == nullptr)
392 return -1;
393 if (archive->backend->CreateDirectory(path))
394 return 0;
395 return -1;
396}
397
398/**
399 * Open a Directory from an Archive
400 * @param archive_handle Handle to an open Archive object
401 * @param path Path to the Directory inside of the Archive
402 * @return Opened Directory object
403 */
404Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) {
405 Directory* directory = new Directory;
406 Handle handle = Kernel::g_object_pool.Create(directory);
407
408 Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
409 directory->path = path;
410 directory->backend = archive->backend->OpenDirectory(path);
411
412 return handle;
413}
414
415/// Initialize archives
416void ArchiveInit() {
417 g_archive_map.clear();
418
419 // TODO(Link Mauve): Add the other archive types (see here for the known types:
420 // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
421 // archive type is SDMC, so it is the only one getting exposed.
422
423 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
424 auto archive = new FileSys::Archive_SDMC(sdmc_directory);
425 if (archive->Initialize())
426 CreateArchive(archive, "SDMC");
427 else
428 ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
429}
430
431/// Shutdown archives
432void ArchiveShutdown() {
433 g_archive_map.clear();
434}
435
436} // namespace Kernel
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
deleted file mode 100644
index 0230996b6..000000000
--- a/src/core/hle/kernel/archive.h
+++ /dev/null
@@ -1,70 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10#include "core/file_sys/archive.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// Kernel namespace
14
15namespace Kernel {
16
17/**
18 * Opens an archive
19 * @param id_code IdCode of the archive to open
20 * @return Handle to archive if it exists, otherwise a null handle (0)
21 */
22Handle OpenArchive(FileSys::Archive::IdCode id_code);
23
24/**
25 * Closes an archive
26 * @param id_code IdCode of the archive to open
27 * @return true if it worked fine
28 */
29Result CloseArchive(FileSys::Archive::IdCode id_code);
30
31/**
32 * Creates an Archive
33 * @param backend File system backend interface to the archive
34 * @param name Optional name of Archive
35 * @return Handle to newly created Archive object
36 */
37Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
38
39/**
40 * Open a File from an Archive
41 * @param archive_handle Handle to an open Archive object
42 * @param path Path to the File inside of the Archive
43 * @param mode Mode under which to open the File
44 * @return Opened File object
45 */
46Handle OpenFileFromArchive(Handle archive_handle, const std::string& name, const FileSys::Mode mode);
47
48/**
49 * Create a Directory from an Archive
50 * @param archive_handle Handle to an open Archive object
51 * @param path Path to the Directory inside of the Archive
52 * @return Whether creation of directory succeeded
53 */
54Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& name);
55
56/**
57 * Open a Directory from an Archive
58 * @param archive_handle Handle to an open Archive object
59 * @param path Path to the Directory inside of the Archive
60 * @return Opened Directory object
61 */
62Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& name);
63
64/// Initialize archives
65void ArchiveInit();
66
67/// Shutdown archives
68void ArchiveShutdown();
69
70} // namespace FileSys
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 45ed79be8..288080209 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
6#include <algorithm> 6#include <algorithm>
@@ -30,13 +30,8 @@ public:
30 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event 30 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
31 std::string name; ///< Name of event (optional) 31 std::string name; ///< Name of event (optional)
32 32
33 /** 33 ResultVal<bool> WaitSynchronization() override {
34 * Wait for kernel object to synchronize 34 bool wait = locked;
35 * @param wait Boolean wait set if current thread should wait as a result of sync operation
36 * @return Result of operation, 0 on success, otherwise error code
37 */
38 Result WaitSynchronization(bool* wait) override {
39 *wait = locked;
40 if (locked) { 35 if (locked) {
41 Handle thread = GetCurrentThreadHandle(); 36 Handle thread = GetCurrentThreadHandle();
42 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { 37 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
@@ -47,7 +42,7 @@ public:
47 if (reset_type != RESETTYPE_STICKY && !permanent_locked) { 42 if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
48 locked = true; 43 locked = true;
49 } 44 }
50 return 0; 45 return MakeResult<bool>(wait);
51 } 46 }
52}; 47};
53 48
@@ -57,12 +52,12 @@ public:
57 * @param permanent_locked Boolean permanent locked value to set event 52 * @param permanent_locked Boolean permanent locked value to set event
58 * @return Result of operation, 0 on success, otherwise error code 53 * @return Result of operation, 0 on success, otherwise error code
59 */ 54 */
60Result SetPermanentLock(Handle handle, const bool permanent_locked) { 55ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
61 Event* evt = g_object_pool.GetFast<Event>(handle); 56 Event* evt = g_object_pool.Get<Event>(handle);
62 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); 57 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
63 58
64 evt->permanent_locked = permanent_locked; 59 evt->permanent_locked = permanent_locked;
65 return 0; 60 return RESULT_SUCCESS;
66} 61}
67 62
68/** 63/**
@@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
71 * @param locked Boolean locked value to set event 66 * @param locked Boolean locked value to set event
72 * @return Result of operation, 0 on success, otherwise error code 67 * @return Result of operation, 0 on success, otherwise error code
73 */ 68 */
74Result SetEventLocked(const Handle handle, const bool locked) { 69ResultCode SetEventLocked(const Handle handle, const bool locked) {
75 Event* evt = g_object_pool.GetFast<Event>(handle); 70 Event* evt = g_object_pool.Get<Event>(handle);
76 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); 71 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
77 72
78 if (!evt->permanent_locked) { 73 if (!evt->permanent_locked) {
79 evt->locked = locked; 74 evt->locked = locked;
80 } 75 }
81 return 0; 76 return RESULT_SUCCESS;
82} 77}
83 78
84/** 79/**
@@ -86,16 +81,16 @@ Result SetEventLocked(const Handle handle, const bool locked) {
86 * @param handle Handle to event to signal 81 * @param handle Handle to event to signal
87 * @return Result of operation, 0 on success, otherwise error code 82 * @return Result of operation, 0 on success, otherwise error code
88 */ 83 */
89Result SignalEvent(const Handle handle) { 84ResultCode SignalEvent(const Handle handle) {
90 Event* evt = g_object_pool.GetFast<Event>(handle); 85 Event* evt = g_object_pool.Get<Event>(handle);
91 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); 86 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
92 87
93 // Resume threads waiting for event to signal 88 // Resume threads waiting for event to signal
94 bool event_caught = false; 89 bool event_caught = false;
95 for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { 90 for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
96 ResumeThreadFromWait( evt->waiting_threads[i]); 91 ResumeThreadFromWait( evt->waiting_threads[i]);
97 92
98 // If any thread is signalled awake by this event, assume the event was "caught" and reset 93 // If any thread is signalled awake by this event, assume the event was "caught" and reset
99 // the event. This will result in the next thread waiting on the event to block. Otherwise, 94 // the event. This will result in the next thread waiting on the event to block. Otherwise,
100 // the event will not be reset, and the next thread to call WaitSynchronization on it will 95 // the event will not be reset, and the next thread to call WaitSynchronization on it will
101 // not block. Not sure if this is correct behavior, but it seems to work. 96 // not block. Not sure if this is correct behavior, but it seems to work.
@@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) {
106 if (!evt->permanent_locked) { 101 if (!evt->permanent_locked) {
107 evt->locked = event_caught; 102 evt->locked = event_caught;
108 } 103 }
109 return 0; 104 return RESULT_SUCCESS;
110} 105}
111 106
112/** 107/**
@@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) {
114 * @param handle Handle to event to clear 109 * @param handle Handle to event to clear
115 * @return Result of operation, 0 on success, otherwise error code 110 * @return Result of operation, 0 on success, otherwise error code
116 */ 111 */
117Result ClearEvent(Handle handle) { 112ResultCode ClearEvent(Handle handle) {
118 Event* evt = g_object_pool.GetFast<Event>(handle); 113 Event* evt = g_object_pool.Get<Event>(handle);
119 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); 114 if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
120 115
121 if (!evt->permanent_locked) { 116 if (!evt->permanent_locked) {
122 evt->locked = true; 117 evt->locked = true;
123 } 118 }
124 return 0; 119 return RESULT_SUCCESS;
125} 120}
126 121
127/** 122/**
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index c39b33180..73aec4e79 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
@@ -15,31 +15,27 @@ namespace Kernel {
15 * Changes whether an event is locked or not 15 * Changes whether an event is locked or not
16 * @param handle Handle to event to change 16 * @param handle Handle to event to change
17 * @param locked Boolean locked value to set event 17 * @param locked Boolean locked value to set event
18 * @return Result of operation, 0 on success, otherwise error code
19 */ 18 */
20Result SetEventLocked(const Handle handle, const bool locked); 19ResultCode SetEventLocked(const Handle handle, const bool locked);
21 20
22/** 21/**
23 * Hackish function to set an events permanent lock state, used to pass through synch blocks 22 * Hackish function to set an events permanent lock state, used to pass through synch blocks
24 * @param handle Handle to event to change 23 * @param handle Handle to event to change
25 * @param permanent_locked Boolean permanent locked value to set event 24 * @param permanent_locked Boolean permanent locked value to set event
26 * @return Result of operation, 0 on success, otherwise error code
27 */ 25 */
28Result SetPermanentLock(Handle handle, const bool permanent_locked); 26ResultCode SetPermanentLock(Handle handle, const bool permanent_locked);
29 27
30/** 28/**
31 * Signals an event 29 * Signals an event
32 * @param handle Handle to event to signal 30 * @param handle Handle to event to signal
33 * @return Result of operation, 0 on success, otherwise error code
34 */ 31 */
35Result SignalEvent(const Handle handle); 32ResultCode SignalEvent(const Handle handle);
36 33
37/** 34/**
38 * Clears an event 35 * Clears an event
39 * @param handle Handle to event to clear 36 * @param handle Handle to event to clear
40 * @return Result of operation, 0 on success, otherwise error code
41 */ 37 */
42Result ClearEvent(Handle handle); 38ResultCode ClearEvent(Handle handle);
43 39
44/** 40/**
45 * Creates an event 41 * Creates an event
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 88cbc1af5..6a690e915 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,18 +1,20 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4
5#include <algorithm>
4 6
5#include "common/common.h" 7#include "common/common.h"
6 8
7#include "core/core.h" 9#include "core/core.h"
8#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/thread.h" 11#include "core/hle/kernel/thread.h"
10#include "core/hle/kernel/archive.h"
11 12
12namespace Kernel { 13namespace Kernel {
13 14
14Handle g_main_thread = 0; 15Handle g_main_thread = 0;
15ObjectPool g_object_pool; 16ObjectPool g_object_pool;
17u64 g_program_id = 0;
16 18
17ObjectPool::ObjectPool() { 19ObjectPool::ObjectPool() {
18 next_id = INITIAL_NEXT_ID; 20 next_id = INITIAL_NEXT_ID;
@@ -33,11 +35,11 @@ Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
33 return i + HANDLE_OFFSET; 35 return i + HANDLE_OFFSET;
34 } 36 }
35 } 37 }
36 ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); 38 LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use.");
37 return 0; 39 return 0;
38} 40}
39 41
40bool ObjectPool::IsValid(Handle handle) { 42bool ObjectPool::IsValid(Handle handle) const {
41 int index = handle - HANDLE_OFFSET; 43 int index = handle - HANDLE_OFFSET;
42 if (index < 0) 44 if (index < 0)
43 return false; 45 return false;
@@ -60,7 +62,7 @@ void ObjectPool::Clear() {
60 62
61Object* &ObjectPool::operator [](Handle handle) 63Object* &ObjectPool::operator [](Handle handle)
62{ 64{
63 _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); 65 _dbg_assert_msg_(Kernel, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
64 return pool[handle - HANDLE_OFFSET]; 66 return pool[handle - HANDLE_OFFSET];
65} 67}
66 68
@@ -68,37 +70,30 @@ void ObjectPool::List() {
68 for (int i = 0; i < MAX_COUNT; i++) { 70 for (int i = 0; i < MAX_COUNT; i++) {
69 if (occupied[i]) { 71 if (occupied[i]) {
70 if (pool[i]) { 72 if (pool[i]) {
71 INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), 73 LOG_DEBUG(Kernel, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
72 pool[i]->GetName().c_str()); 74 pool[i]->GetName().c_str());
73 } 75 }
74 } 76 }
75 } 77 }
76} 78}
77 79
78int ObjectPool::GetCount() { 80int ObjectPool::GetCount() const {
79 int count = 0; 81 return std::count(occupied.begin(), occupied.end(), true);
80 for (int i = 0; i < MAX_COUNT; i++) {
81 if (occupied[i])
82 count++;
83 }
84 return count;
85} 82}
86 83
87Object* ObjectPool::CreateByIDType(int type) { 84Object* ObjectPool::CreateByIDType(int type) {
88 ERROR_LOG(COMMON, "Unimplemented: %d.", type); 85 LOG_ERROR(Kernel, "Unimplemented: %d.", type);
89 return nullptr; 86 return nullptr;
90} 87}
91 88
92/// Initialize the kernel 89/// Initialize the kernel
93void Init() { 90void Init() {
94 Kernel::ThreadingInit(); 91 Kernel::ThreadingInit();
95 Kernel::ArchiveInit();
96} 92}
97 93
98/// Shutdown the kernel 94/// Shutdown the kernel
99void Shutdown() { 95void Shutdown() {
100 Kernel::ThreadingShutdown(); 96 Kernel::ThreadingShutdown();
101 Kernel::ArchiveShutdown();
102 97
103 g_object_pool.Clear(); // Free all kernel objects 98 g_object_pool.Clear(); // Free all kernel objects
104} 99}
@@ -109,8 +104,6 @@ void Shutdown() {
109 * @return True on success, otherwise false 104 * @return True on success, otherwise false
110 */ 105 */
111bool LoadExec(u32 entry_point) { 106bool LoadExec(u32 entry_point) {
112 Init();
113
114 Core::g_app_core->SetPC(entry_point); 107 Core::g_app_core->SetPC(entry_point);
115 108
116 // 0x30 is the typical main thread priority I've seen used so far 109 // 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 867d1b89c..7123485be 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,12 +1,13 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <string> 8#include <string>
9#include "common/common.h" 9#include "common/common.h"
10#include "core/hle/result.h"
10 11
11typedef u32 Handle; 12typedef u32 Handle;
12typedef s32 Result; 13typedef s32 Result;
@@ -21,7 +22,7 @@ enum KernelHandle {
21enum class HandleType : u32 { 22enum class HandleType : u32 {
22 Unknown = 0, 23 Unknown = 0,
23 Port = 1, 24 Port = 1,
24 Service = 2, 25 Session = 2,
25 Event = 3, 26 Event = 3,
26 Mutex = 4, 27 Mutex = 4,
27 SharedMemory = 5, 28 SharedMemory = 5,
@@ -29,12 +30,9 @@ enum class HandleType : u32 {
29 Thread = 7, 30 Thread = 7,
30 Process = 8, 31 Process = 8,
31 AddressArbiter = 9, 32 AddressArbiter = 9,
32 File = 10, 33 Semaphore = 10,
33 Semaphore = 11,
34 Archive = 12,
35 Directory = 13,
36}; 34};
37 35
38enum { 36enum {
39 DEFAULT_STACK_SIZE = 0x4000, 37 DEFAULT_STACK_SIZE = 0x4000,
40}; 38};
@@ -52,21 +50,13 @@ public:
52 virtual Kernel::HandleType GetHandleType() const = 0; 50 virtual Kernel::HandleType GetHandleType() const = 0;
53 51
54 /** 52 /**
55 * Synchronize kernel object 53 * Wait for kernel object to synchronize.
56 * @param wait Boolean wait set if current thread should wait as a result of sync operation 54 * @return True if the current thread should wait as a result of the wait
57 * @return Result of operation, 0 on success, otherwise error code
58 */ 55 */
59 virtual Result SyncRequest(bool* wait) { 56 virtual ResultVal<bool> WaitSynchronization() {
60 ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); 57 LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
61 return -1; 58 return UnimplementedFunction(ErrorModule::Kernel);
62 } 59 }
63
64 /**
65 * Wait for kernel object to synchronize
66 * @param wait Boolean wait set if current thread should wait as a result of sync operation
67 * @return Result of operation, 0 on success, otherwise error code
68 */
69 virtual Result WaitSynchronization(bool* wait) = 0;
70}; 60};
71 61
72class ObjectPool : NonCopyable { 62class ObjectPool : NonCopyable {
@@ -80,38 +70,29 @@ public:
80 static Object* CreateByIDType(int type); 70 static Object* CreateByIDType(int type);
81 71
82 template <class T> 72 template <class T>
83 u32 Destroy(Handle handle) { 73 void Destroy(Handle handle) {
84 u32 error; 74 if (Get<T>(handle)) {
85 if (Get<T>(handle, error)) {
86 occupied[handle - HANDLE_OFFSET] = false; 75 occupied[handle - HANDLE_OFFSET] = false;
87 delete pool[handle - HANDLE_OFFSET]; 76 delete pool[handle - HANDLE_OFFSET];
88 } 77 }
89 return error; 78 }
90 };
91 79
92 bool IsValid(Handle handle); 80 bool IsValid(Handle handle) const;
93 81
94 template <class T> 82 template <class T>
95 T* Get(Handle handle, u32& outError) { 83 T* Get(Handle handle) {
96 if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { 84 if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
97 // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP 85 if (handle != 0) {
98 if (handle != 0 && (u32)handle != 0x80020001) { 86 LOG_ERROR(Kernel, "Bad object handle %08x", handle);
99 WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
100 } 87 }
101 outError = 0;//T::GetMissingErrorCode(); 88 return nullptr;
102 return 0;
103 } else { 89 } else {
104 // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally, 90 Object* t = pool[handle - HANDLE_OFFSET];
105 // it just acted as a static case and everything worked. This means that we will never 91 if (t->GetHandleType() != T::GetStaticHandleType()) {
106 // see the Wrong type object error below, but we'll just have to live with that danger. 92 LOG_ERROR(Kernel, "Wrong object type for %08x", handle);
107 T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]); 93 return nullptr;
108 if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
109 WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
110 outError = 0;//T::GetMissingErrorCode();
111 return 0;
112 } 94 }
113 outError = 0;//SCE_KERNEL_ERROR_OK; 95 return static_cast<T*>(t);
114 return t;
115 } 96 }
116 } 97 }
117 98
@@ -119,7 +100,7 @@ public:
119 template <class T> 100 template <class T>
120 T *GetFast(Handle handle) { 101 T *GetFast(Handle handle) {
121 const Handle realHandle = handle - HANDLE_OFFSET; 102 const Handle realHandle = handle - HANDLE_OFFSET;
122 _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); 103 _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
123 return static_cast<T*>(pool[realHandle]); 104 return static_cast<T*>(pool[realHandle]);
124 } 105 }
125 106
@@ -139,9 +120,9 @@ public:
139 } 120 }
140 121
141 bool GetIDType(Handle handle, HandleType* type) const { 122 bool GetIDType(Handle handle, HandleType* type) const {
142 if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || 123 if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
143 !occupied[handle - HANDLE_OFFSET]) { 124 !occupied[handle - HANDLE_OFFSET]) {
144 ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); 125 LOG_ERROR(Kernel, "Bad object handle %08X", handle);
145 return false; 126 return false;
146 } 127 }
147 Object* t = pool[handle - HANDLE_OFFSET]; 128 Object* t = pool[handle - HANDLE_OFFSET];
@@ -152,10 +133,10 @@ public:
152 Object* &operator [](Handle handle); 133 Object* &operator [](Handle handle);
153 void List(); 134 void List();
154 void Clear(); 135 void Clear();
155 int GetCount(); 136 int GetCount() const;
156 137
157private: 138private:
158 139
159 enum { 140 enum {
160 MAX_COUNT = 0x1000, 141 MAX_COUNT = 0x1000,
161 HANDLE_OFFSET = 0x100, 142 HANDLE_OFFSET = 0x100,
@@ -170,6 +151,12 @@ private:
170extern ObjectPool g_object_pool; 151extern ObjectPool g_object_pool;
171extern Handle g_main_thread; 152extern Handle g_main_thread;
172 153
154/// The ID code of the currently running game
155/// TODO(Subv): This variable should not be here,
156/// we need a way to store information about the currently loaded application
157/// for later query during runtime, maybe using the LDR service?
158extern u64 g_program_id;
159
173/// Initialize the kernel 160/// Initialize the kernel
174void Init(); 161void Init();
175 162
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index fcfd061ac..5a173e129 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
6#include <vector> 6#include <vector>
@@ -27,32 +27,7 @@ public:
27 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex 27 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
28 std::string name; ///< Name of mutex (optional) 28 std::string name; ///< Name of mutex (optional)
29 29
30 /** 30 ResultVal<bool> WaitSynchronization() override;
31 * Synchronize kernel object
32 * @param wait Boolean wait set if current thread should wait as a result of sync operation
33 * @return Result of operation, 0 on success, otherwise error code
34 */
35 Result SyncRequest(bool* wait) override {
36 // TODO(bunnei): ImplementMe
37 locked = true;
38 return 0;
39 }
40
41 /**
42 * Wait for kernel object to synchronize
43 * @param wait Boolean wait set if current thread should wait as a result of sync operation
44 * @return Result of operation, 0 on success, otherwise error code
45 */
46 Result WaitSynchronization(bool* wait) override {
47 // TODO(bunnei): ImplementMe
48 *wait = locked;
49
50 if (locked) {
51 Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
52 }
53
54 return 0;
55 }
56}; 31};
57 32
58//////////////////////////////////////////////////////////////////////////////////////////////////// 33////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -60,21 +35,46 @@ public:
60typedef std::multimap<Handle, Handle> MutexMap; 35typedef std::multimap<Handle, Handle> MutexMap;
61static MutexMap g_mutex_held_locks; 36static MutexMap g_mutex_held_locks;
62 37
63void MutexAcquireLock(Mutex* mutex, Handle thread) { 38/**
39 * Acquires the specified mutex for the specified thread
40 * @param mutex Mutex that is to be acquired
41 * @param thread Thread that will acquired
42 */
43void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
64 g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); 44 g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
65 mutex->lock_thread = thread; 45 mutex->lock_thread = thread;
66} 46}
67 47
68void MutexAcquireLock(Mutex* mutex) { 48bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
69 Handle thread = GetCurrentThreadHandle();
70 MutexAcquireLock(mutex, thread); 49 MutexAcquireLock(mutex, thread);
50 Kernel::ResumeThreadFromWait(thread);
51 return true;
52}
53
54/**
55 * Resumes a thread waiting for the specified mutex
56 * @param mutex The mutex that some thread is waiting on
57 */
58void ResumeWaitingThread(Mutex* mutex) {
59 // Find the next waiting thread for the mutex...
60 if (mutex->waiting_threads.empty()) {
61 // Reset mutex lock thread handle, nothing is waiting
62 mutex->locked = false;
63 mutex->lock_thread = -1;
64 }
65 else {
66 // Resume the next waiting thread and re-lock the mutex
67 std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
68 ReleaseMutexForThread(mutex, *iter);
69 mutex->waiting_threads.erase(iter);
70 }
71} 71}
72 72
73void MutexEraseLock(Mutex* mutex) { 73void MutexEraseLock(Mutex* mutex) {
74 Handle handle = mutex->GetHandle(); 74 Handle handle = mutex->GetHandle();
75 auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); 75 auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
76 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { 76 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
77 if ((*iter).second == handle) { 77 if (iter->second == handle) {
78 g_mutex_held_locks.erase(iter); 78 g_mutex_held_locks.erase(iter);
79 break; 79 break;
80 } 80 }
@@ -82,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) {
82 mutex->lock_thread = -1; 82 mutex->lock_thread = -1;
83} 83}
84 84
85void ReleaseThreadMutexes(Handle thread) {
86 auto locked = g_mutex_held_locks.equal_range(thread);
87
88 // Release every mutex that the thread holds, and resume execution on the waiting threads
89 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
90 Mutex* mutex = g_object_pool.GetFast<Mutex>(iter->second);
91 ResumeWaitingThread(mutex);
92 }
93
94 // Erase all the locks that this thread holds
95 g_mutex_held_locks.erase(thread);
96}
97
85bool LockMutex(Mutex* mutex) { 98bool LockMutex(Mutex* mutex) {
86 // Mutex alread locked? 99 // Mutex alread locked?
87 if (mutex->locked) { 100 if (mutex->locked) {
@@ -91,43 +104,27 @@ bool LockMutex(Mutex* mutex) {
91 return true; 104 return true;
92} 105}
93 106
94bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
95 MutexAcquireLock(mutex, thread);
96 Kernel::ResumeThreadFromWait(thread);
97 return true;
98}
99
100bool ReleaseMutex(Mutex* mutex) { 107bool ReleaseMutex(Mutex* mutex) {
101 MutexEraseLock(mutex); 108 MutexEraseLock(mutex);
102 bool woke_threads = false; 109 ResumeWaitingThread(mutex);
103 110 return true;
104 // Find the next waiting thread for the mutex...
105 while (!woke_threads && !mutex->waiting_threads.empty()) {
106 std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
107 woke_threads |= ReleaseMutexForThread(mutex, *iter);
108 mutex->waiting_threads.erase(iter);
109 }
110 // Reset mutex lock thread handle, nothing is waiting
111 if (!woke_threads) {
112 mutex->locked = false;
113 mutex->lock_thread = -1;
114 }
115 return woke_threads;
116} 111}
117 112
118/** 113/**
119 * Releases a mutex 114 * Releases a mutex
120 * @param handle Handle to mutex to release 115 * @param handle Handle to mutex to release
121 */ 116 */
122Result ReleaseMutex(Handle handle) { 117ResultCode ReleaseMutex(Handle handle) {
123 Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); 118 Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle);
124 119 if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
125 _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
126 120
127 if (!ReleaseMutex(mutex)) { 121 if (!ReleaseMutex(mutex)) {
128 return -1; 122 // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure
123 // what error condition this is supposed to be signaling.
124 return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel,
125 ErrorSummary::NothingHappened, ErrorLevel::Temporary);
129 } 126 }
130 return 0; 127 return RESULT_SUCCESS;
131} 128}
132 129
133/** 130/**
@@ -167,4 +164,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
167 return handle; 164 return handle;
168} 165}
169 166
167ResultVal<bool> Mutex::WaitSynchronization() {
168 bool wait = locked;
169 if (locked) {
170 Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
171 }
172 else {
173 // Lock the mutex when the first thread accesses it
174 locked = true;
175 MutexAcquireLock(this);
176 }
177
178 return MakeResult<bool>(wait);
179}
170} // namespace 180} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 7d7b5137e..7f4909a6e 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
@@ -13,9 +13,8 @@ namespace Kernel {
13/** 13/**
14 * Releases a mutex 14 * Releases a mutex
15 * @param handle Handle to mutex to release 15 * @param handle Handle to mutex to release
16 * @return Result of operation, 0 on success, otherwise error code
17 */ 16 */
18Result ReleaseMutex(Handle handle); 17ResultCode ReleaseMutex(Handle handle);
19 18
20/** 19/**
21 * Creates a mutex 20 * Creates a mutex
@@ -25,4 +24,10 @@ Result ReleaseMutex(Handle handle);
25 */ 24 */
26Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); 25Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
27 26
27/**
28 * Releases all the mutexes held by the specified thread
29 * @param thread Thread that is holding the mutexes
30 */
31void ReleaseThreadMutexes(Handle thread);
32
28} // namespace 33} // namespace
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
new file mode 100644
index 000000000..6f56da8a9
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -0,0 +1,94 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <queue>
6
7#include "common/common.h"
8
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/semaphore.h"
11#include "core/hle/kernel/thread.h"
12
13namespace Kernel {
14
15class Semaphore : public Object {
16public:
17 std::string GetTypeName() const override { return "Semaphore"; }
18 std::string GetName() const override { return name; }
19
20 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; }
21 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; }
22
23 u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
24 u32 available_count; ///< Number of free slots left in the semaphore
25 std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
26 std::string name; ///< Name of semaphore (optional)
27
28 /**
29 * Tests whether a semaphore still has free slots
30 * @return Whether the semaphore is available
31 */
32 bool IsAvailable() const {
33 return available_count > 0;
34 }
35
36 ResultVal<bool> WaitSynchronization() override {
37 bool wait = !IsAvailable();
38
39 if (wait) {
40 Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
41 waiting_threads.push(GetCurrentThreadHandle());
42 } else {
43 --available_count;
44 }
45
46 return MakeResult<bool>(wait);
47 }
48};
49
50////////////////////////////////////////////////////////////////////////////////////////////////////
51
52ResultCode CreateSemaphore(Handle* handle, u32 initial_count,
53 u32 max_count, const std::string& name) {
54
55 if (initial_count > max_count)
56 return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
57 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
58
59 Semaphore* semaphore = new Semaphore;
60 *handle = g_object_pool.Create(semaphore);
61
62 // When the semaphore is created, some slots are reserved for other threads,
63 // and the rest is reserved for the caller thread
64 semaphore->max_count = max_count;
65 semaphore->available_count = initial_count;
66 semaphore->name = name;
67
68 return RESULT_SUCCESS;
69}
70
71ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
72 Semaphore* semaphore = g_object_pool.Get<Semaphore>(handle);
73 if (semaphore == nullptr)
74 return InvalidHandle(ErrorModule::Kernel);
75
76 if (semaphore->max_count - semaphore->available_count < release_count)
77 return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
78 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
79
80 *count = semaphore->available_count;
81 semaphore->available_count += release_count;
82
83 // Notify some of the threads that the semaphore has been released
84 // stop once the semaphore is full again or there are no more waiting threads
85 while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
86 Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
87 semaphore->waiting_threads.pop();
88 --semaphore->available_count;
89 }
90
91 return RESULT_SUCCESS;
92}
93
94} // namespace
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
new file mode 100644
index 000000000..f0075fdb8
--- /dev/null
+++ b/src/core/hle/kernel/semaphore.h
@@ -0,0 +1,32 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10
11namespace Kernel {
12
13/**
14 * Creates a semaphore.
15 * @param handle Pointer to the handle of the newly created object
16 * @param initial_count Number of slots reserved for other threads
17 * @param max_count Maximum number of slots the semaphore can have
18 * @param name Optional name of semaphore
19 * @return ResultCode of the error
20 */
21ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown");
22
23/**
24 * Releases a certain number of slots from a semaphore.
25 * @param count The number of free slots the semaphore had before this call
26 * @param handle The handle of the semaphore to release
27 * @param release_count The number of slots to release
28 * @return ResultCode of the error
29 */
30ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
31
32} // namespace
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
new file mode 100644
index 000000000..06ae4bc39
--- /dev/null
+++ b/src/core/hle/kernel/session.h
@@ -0,0 +1,58 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/kernel/kernel.h"
8
9namespace Kernel {
10
11static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
12
13/**
14 * Returns a pointer to the command buffer in kernel memory
15 * @param offset Optional offset into command buffer
16 * @return Pointer to command buffer
17 */
18inline static u32* GetCommandBuffer(const int offset=0) {
19 return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
20}
21
22/**
23 * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS
24 * primitive for communication between different processes, and are used to implement service calls
25 * to the various system services.
26 *
27 * To make a service call, the client must write the command header and parameters to the buffer
28 * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
29 * SVC call with its Session handle. The kernel will read the command header, using it to marshall
30 * the parameters to the process at the server endpoint of the session. After the server replies to
31 * the request, the response is marshalled back to the caller's TLS buffer and control is
32 * transferred back to it.
33 *
34 * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC
35 * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called
36 * with the session handle, this class's SyncRequest method is called, which should read the TLS
37 * buffer and emulate the call accordingly. Since the code can directly read the emulated memory,
38 * no parameter marshalling is done.
39 *
40 * In the long term, this should be turned into the full-fledged IPC mechanism implemented by
41 * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
42 * opposed to HLE simulations.
43 */
44class Session : public Object {
45public:
46 std::string GetTypeName() const override { return "Session"; }
47
48 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Session; }
49 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Session; }
50
51 /**
52 * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls
53 * aren't supported yet.
54 */
55 virtual ResultVal<bool> SyncRequest() = 0;
56};
57
58}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 6bd5e2728..3c8c502c6 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common.h" 5#include "common/common.h"
6 6
@@ -16,17 +16,6 @@ public:
16 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } 16 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
17 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } 17 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
18 18
19 /**
20 * Wait for kernel object to synchronize
21 * @param wait Boolean wait set if current thread should wait as a result of sync operation
22 * @return Result of operation, 0 on success, otherwise error code
23 */
24 Result WaitSynchronization(bool* wait) override {
25 // TODO(bunnei): ImplementMe
26 ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
27 return 0;
28 }
29
30 u32 base_address; ///< Address of shared memory block in RAM 19 u32 base_address; ///< Address of shared memory block in RAM
31 MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) 20 MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
32 MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) 21 MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field)
@@ -48,11 +37,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
48 return shared_memory; 37 return shared_memory;
49} 38}
50 39
51/**
52 * Creates a shared memory object
53 * @param name Optional name of shared memory object
54 * @return Handle of newly created shared memory object
55 */
56Handle CreateSharedMemory(const std::string& name) { 40Handle CreateSharedMemory(const std::string& name) {
57 Handle handle; 41 Handle handle;
58 CreateSharedMemory(handle, name); 42 CreateSharedMemory(handle, name);
@@ -67,39 +51,36 @@ Handle CreateSharedMemory(const std::string& name) {
67 * @param other_permissions Memory block map other permissions (specified by SVC field) 51 * @param other_permissions Memory block map other permissions (specified by SVC field)
68 * @return Result of operation, 0 on success, otherwise error code 52 * @return Result of operation, 0 on success, otherwise error code
69 */ 53 */
70Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, 54ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
71 MemoryPermission other_permissions) { 55 MemoryPermission other_permissions) {
72 56
73 if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { 57 if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
74 ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", 58 LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
75 handle); 59 handle, address);
76 return -1; 60 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
61 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
77 } 62 }
78 SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle); 63 SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
79 _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle); 64 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
80 65
81 shared_memory->base_address = address; 66 shared_memory->base_address = address;
82 shared_memory->permissions = permissions; 67 shared_memory->permissions = permissions;
83 shared_memory->other_permissions = other_permissions; 68 shared_memory->other_permissions = other_permissions;
84 69
85 return 0; 70 return RESULT_SUCCESS;
86} 71}
87 72
88/** 73ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
89 * Gets a pointer to the shared memory block 74 SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
90 * @param handle Shared memory block handle 75 if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
91 * @param offset Offset from the start of the shared memory block to get pointer
92 * @return Pointer to the shared memory block from the specified offset
93 */
94u8* GetSharedMemoryPointer(Handle handle, u32 offset) {
95 SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle);
96 _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle);
97 76
98 if (0 != shared_memory->base_address) 77 if (0 != shared_memory->base_address)
99 return Memory::GetPointer(shared_memory->base_address + offset); 78 return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
100 79
101 ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); 80 LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
102 return nullptr; 81 // TODO(yuriks): Verify error code.
82 return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
83 ErrorSummary::InvalidState, ErrorLevel::Permanent);
103} 84}
104 85
105} // namespace 86} // namespace
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 6204d8a45..bb778ec26 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
@@ -36,9 +36,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown");
36 * @param address Address in system memory to map shared memory block to 36 * @param address Address in system memory to map shared memory block to
37 * @param permissions Memory block map permissions (specified by SVC field) 37 * @param permissions Memory block map permissions (specified by SVC field)
38 * @param other_permissions Memory block map other permissions (specified by SVC field) 38 * @param other_permissions Memory block map other permissions (specified by SVC field)
39 * @return Result of operation, 0 on success, otherwise error code
40 */ 39 */
41Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, 40ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions,
42 MemoryPermission other_permissions); 41 MemoryPermission other_permissions);
43 42
44/** 43/**
@@ -47,6 +46,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions,
47 * @param offset Offset from the start of the shared memory block to get pointer 46 * @param offset Offset from the start of the shared memory block to get pointer
48 * @return Pointer to the shared memory block from the specified offset 47 * @return Pointer to the shared memory block from the specified offset
49 */ 48 */
50u8* GetSharedMemoryPointer(Handle handle, u32 offset); 49ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset);
51 50
52} // namespace 51} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index e15590c49..1c04701de 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <list> 6#include <list>
@@ -11,10 +11,12 @@
11#include "common/thread_queue_list.h" 11#include "common/thread_queue_list.h"
12 12
13#include "core/core.h" 13#include "core/core.h"
14#include "core/mem_map.h"
15#include "core/hle/hle.h" 14#include "core/hle/hle.h"
16#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
17#include "core/hle/kernel/mutex.h"
18#include "core/hle/result.h"
19#include "core/mem_map.h"
18 20
19namespace Kernel { 21namespace Kernel {
20 22
@@ -33,25 +35,23 @@ public:
33 inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } 35 inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
34 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } 36 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
35 37
36 /** 38 ResultVal<bool> WaitSynchronization() override {
37 * Wait for kernel object to synchronize 39 const bool wait = status != THREADSTATUS_DORMANT;
38 * @param wait Boolean wait set if current thread should wait as a result of sync operation 40 if (wait) {
39 * @return Result of operation, 0 on success, otherwise error code
40 */
41 Result WaitSynchronization(bool* wait) override {
42 if (status != THREADSTATUS_DORMANT) {
43 Handle thread = GetCurrentThreadHandle(); 41 Handle thread = GetCurrentThreadHandle();
44 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { 42 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
45 waiting_threads.push_back(thread); 43 waiting_threads.push_back(thread);
46 } 44 }
47 WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); 45 WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
48 *wait = true;
49 } 46 }
50 return 0; 47
48 return MakeResult<bool>(wait);
51 } 49 }
52 50
53 ThreadContext context; 51 ThreadContext context;
54 52
53 u32 thread_id;
54
55 u32 status; 55 u32 status;
56 u32 entry_point; 56 u32 entry_point;
57 u32 stack_top; 57 u32 stack_top;
@@ -64,6 +64,7 @@ public:
64 64
65 WaitType wait_type; 65 WaitType wait_type;
66 Handle wait_handle; 66 Handle wait_handle;
67 VAddr wait_address;
67 68
68 std::vector<Handle> waiting_threads; 69 std::vector<Handle> waiting_threads;
69 70
@@ -71,17 +72,20 @@ public:
71}; 72};
72 73
73// Lists all thread ids that aren't deleted/etc. 74// Lists all thread ids that aren't deleted/etc.
74std::vector<Handle> g_thread_queue; 75static std::vector<Handle> thread_queue;
75 76
76// Lists only ready thread ids. 77// Lists only ready thread ids.
77Common::ThreadQueueList<Handle> g_thread_ready_queue; 78static Common::ThreadQueueList<Handle> thread_ready_queue;
79
80static Handle current_thread_handle;
81static Thread* current_thread;
78 82
79Handle g_current_thread_handle; 83static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
80Thread* g_current_thread; 84static u32 next_thread_id; ///< The next available thread id
81 85
82/// Gets the current thread 86/// Gets the current thread
83inline Thread* GetCurrentThread() { 87inline Thread* GetCurrentThread() {
84 return g_current_thread; 88 return current_thread;
85} 89}
86 90
87/// Gets the current thread handle 91/// Gets the current thread handle
@@ -91,8 +95,8 @@ Handle GetCurrentThreadHandle() {
91 95
92/// Sets the current thread 96/// Sets the current thread
93inline void SetCurrentThread(Thread* t) { 97inline void SetCurrentThread(Thread* t) {
94 g_current_thread = t; 98 current_thread = t;
95 g_current_thread_handle = t->GetHandle(); 99 current_thread_handle = t->GetHandle();
96} 100}
97 101
98/// Saves the current CPU context 102/// Saves the current CPU context
@@ -113,7 +117,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
113 t->context.pc = t->context.reg_15 = t->entry_point; 117 t->context.pc = t->context.reg_15 = t->entry_point;
114 t->context.sp = t->stack_top; 118 t->context.sp = t->stack_top;
115 t->context.cpsr = 0x1F; // Usermode 119 t->context.cpsr = 0x1F; // Usermode
116 120
117 // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a 121 // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a
118 // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be 122 // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be
119 // agnostic of the CPU core. 123 // agnostic of the CPU core.
@@ -124,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
124 } 128 }
125 t->wait_type = WAITTYPE_NONE; 129 t->wait_type = WAITTYPE_NONE;
126 t->wait_handle = 0; 130 t->wait_handle = 0;
131 t->wait_address = 0;
127} 132}
128 133
129/// Change a thread to "ready" state 134/// Change a thread to "ready" state
@@ -131,40 +136,44 @@ void ChangeReadyState(Thread* t, bool ready) {
131 Handle handle = t->GetHandle(); 136 Handle handle = t->GetHandle();
132 if (t->IsReady()) { 137 if (t->IsReady()) {
133 if (!ready) { 138 if (!ready) {
134 g_thread_ready_queue.remove(t->current_priority, handle); 139 thread_ready_queue.remove(t->current_priority, handle);
135 } 140 }
136 } else if (ready) { 141 } else if (ready) {
137 if (t->IsRunning()) { 142 if (t->IsRunning()) {
138 g_thread_ready_queue.push_front(t->current_priority, handle); 143 thread_ready_queue.push_front(t->current_priority, handle);
139 } else { 144 } else {
140 g_thread_ready_queue.push_back(t->current_priority, handle); 145 thread_ready_queue.push_back(t->current_priority, handle);
141 } 146 }
142 t->status = THREADSTATUS_READY; 147 t->status = THREADSTATUS_READY;
143 } 148 }
144} 149}
145 150
146/// Verify that a thread has not been released from waiting 151/// Verify that a thread has not been released from waiting
147inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) { 152static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
148 Thread* thread = g_object_pool.GetFast<Thread>(handle); 153 _dbg_assert_(Kernel, thread != nullptr);
149 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); 154 return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting());
150 155}
151 if (type != thread->wait_type || wait_handle != thread->wait_handle)
152 return false;
153 156
154 return true; 157/// Verify that a thread has not been released from waiting (with wait address)
158static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
159 _dbg_assert_(Kernel, thread != nullptr);
160 return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address);
155} 161}
156 162
157/// Stops the current thread 163/// Stops the current thread
158void StopThread(Handle handle, const char* reason) { 164ResultCode StopThread(Handle handle, const char* reason) {
159 Thread* thread = g_object_pool.GetFast<Thread>(handle); 165 Thread* thread = g_object_pool.Get<Thread>(handle);
160 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); 166 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
161 167
168 // Release all the mutexes that this thread holds
169 ReleaseThreadMutexes(handle);
170
162 ChangeReadyState(thread, false); 171 ChangeReadyState(thread, false);
163 thread->status = THREADSTATUS_DORMANT; 172 thread->status = THREADSTATUS_DORMANT;
164 for (size_t i = 0; i < thread->waiting_threads.size(); ++i) { 173 for (Handle waiting_handle : thread->waiting_threads) {
165 const Handle waiting_thread = thread->waiting_threads[i]; 174 Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
166 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { 175 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
167 ResumeThreadFromWait(waiting_thread); 176 ResumeThreadFromWait(waiting_handle);
168 } 177 }
169 } 178 }
170 thread->waiting_threads.clear(); 179 thread->waiting_threads.clear();
@@ -172,6 +181,9 @@ void StopThread(Handle handle, const char* reason) {
172 // Stopped threads are never waiting. 181 // Stopped threads are never waiting.
173 thread->wait_type = WAITTYPE_NONE; 182 thread->wait_type = WAITTYPE_NONE;
174 thread->wait_handle = 0; 183 thread->wait_handle = 0;
184 thread->wait_address = 0;
185
186 return RESULT_SUCCESS;
175} 187}
176 188
177/// Changes a threads state 189/// Changes a threads state
@@ -181,10 +193,10 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
181 } 193 }
182 ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); 194 ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
183 t->status = new_status; 195 t->status = new_status;
184 196
185 if (new_status == THREADSTATUS_WAIT) { 197 if (new_status == THREADSTATUS_WAIT) {
186 if (t->wait_type == WAITTYPE_NONE) { 198 if (t->wait_type == WAITTYPE_NONE) {
187 ERROR_LOG(KERNEL, "Waittype none not allowed"); 199 LOG_ERROR(Kernel, "Waittype none not allowed");
188 } 200 }
189 } 201 }
190} 202}
@@ -195,13 +207,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
195 s32 priority = THREADPRIO_LOWEST; 207 s32 priority = THREADPRIO_LOWEST;
196 208
197 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 209 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
198 for (const auto& handle : g_thread_queue) { 210 for (Handle handle : thread_queue) {
211 Thread* thread = g_object_pool.Get<Thread>(handle);
199 212
200 // TODO(bunnei): Verify arbiter address... 213 if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
201 if (!VerifyWait(handle, WAITTYPE_ARB, arbiter))
202 continue; 214 continue;
203 215
204 Thread* thread = g_object_pool.GetFast<Thread>(handle); 216 if (thread == nullptr)
217 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
218
205 if(thread->current_priority <= priority) { 219 if(thread->current_priority <= priority) {
206 highest_priority_thread = handle; 220 highest_priority_thread = handle;
207 priority = thread->current_priority; 221 priority = thread->current_priority;
@@ -216,12 +230,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
216 230
217/// Arbitrate all threads currently waiting 231/// Arbitrate all threads currently waiting
218void ArbitrateAllThreads(u32 arbiter, u32 address) { 232void ArbitrateAllThreads(u32 arbiter, u32 address) {
219 233
220 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 234 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
221 for (const auto& handle : g_thread_queue) { 235 for (Handle handle : thread_queue) {
236 Thread* thread = g_object_pool.Get<Thread>(handle);
222 237
223 // TODO(bunnei): Verify arbiter address... 238 if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
224 if (VerifyWait(handle, WAITTYPE_ARB, arbiter))
225 ResumeThreadFromWait(handle); 239 ResumeThreadFromWait(handle);
226 } 240 }
227} 241}
@@ -238,11 +252,11 @@ void CallThread(Thread* t) {
238/// Switches CPU context to that of the specified thread 252/// Switches CPU context to that of the specified thread
239void SwitchContext(Thread* t) { 253void SwitchContext(Thread* t) {
240 Thread* cur = GetCurrentThread(); 254 Thread* cur = GetCurrentThread();
241 255
242 // Save context for current thread 256 // Save context for current thread
243 if (cur) { 257 if (cur) {
244 SaveContext(cur->context); 258 SaveContext(cur->context);
245 259
246 if (cur->IsRunning()) { 260 if (cur->IsRunning()) {
247 ChangeReadyState(cur, true); 261 ChangeReadyState(cur, true);
248 } 262 }
@@ -263,23 +277,18 @@ void SwitchContext(Thread* t) {
263Thread* NextThread() { 277Thread* NextThread() {
264 Handle next; 278 Handle next;
265 Thread* cur = GetCurrentThread(); 279 Thread* cur = GetCurrentThread();
266 280
267 if (cur && cur->IsRunning()) { 281 if (cur && cur->IsRunning()) {
268 next = g_thread_ready_queue.pop_first_better(cur->current_priority); 282 next = thread_ready_queue.pop_first_better(cur->current_priority);
269 } else { 283 } else {
270 next = g_thread_ready_queue.pop_first(); 284 next = thread_ready_queue.pop_first();
271 } 285 }
272 if (next == 0) { 286 if (next == 0) {
273 return nullptr; 287 return nullptr;
274 } 288 }
275 return Kernel::g_object_pool.GetFast<Thread>(next); 289 return Kernel::g_object_pool.Get<Thread>(next);
276} 290}
277 291
278/**
279 * Puts the current thread in the wait state for the given type
280 * @param wait_type Type of wait
281 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
282 */
283void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { 292void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
284 Thread* thread = GetCurrentThread(); 293 Thread* thread = GetCurrentThread();
285 thread->wait_type = wait_type; 294 thread->wait_type = wait_type;
@@ -287,10 +296,14 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
287 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 296 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
288} 297}
289 298
299void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
300 WaitCurrentThread(wait_type, wait_handle);
301 GetCurrentThread()->wait_address = wait_address;
302}
303
290/// Resumes a thread from waiting by marking it as "ready" 304/// Resumes a thread from waiting by marking it as "ready"
291void ResumeThreadFromWait(Handle handle) { 305void ResumeThreadFromWait(Handle handle) {
292 u32 error; 306 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
293 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
294 if (thread) { 307 if (thread) {
295 thread->status &= ~THREADSTATUS_WAIT; 308 thread->status &= ~THREADSTATUS_WAIT;
296 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 309 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
@@ -305,12 +318,12 @@ void DebugThreadQueue() {
305 if (!thread) { 318 if (!thread) {
306 return; 319 return;
307 } 320 }
308 INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); 321 LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
309 for (u32 i = 0; i < g_thread_queue.size(); i++) { 322 for (u32 i = 0; i < thread_queue.size(); i++) {
310 Handle handle = g_thread_queue[i]; 323 Handle handle = thread_queue[i];
311 s32 priority = g_thread_ready_queue.contains(handle); 324 s32 priority = thread_ready_queue.contains(handle);
312 if (priority != -1) { 325 if (priority != -1) {
313 INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); 326 LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
314 } 327 }
315 } 328 }
316} 329}
@@ -319,16 +332,17 @@ void DebugThreadQueue() {
319Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, 332Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
320 s32 processor_id, u32 stack_top, int stack_size) { 333 s32 processor_id, u32 stack_top, int stack_size) {
321 334
322 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), 335 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
323 "CreateThread priority=%d, outside of allowable range!", priority) 336 "priority=%d, outside of allowable range!", priority)
324 337
325 Thread* thread = new Thread; 338 Thread* thread = new Thread;
326 339
327 handle = Kernel::g_object_pool.Create(thread); 340 handle = Kernel::g_object_pool.Create(thread);
328 341
329 g_thread_queue.push_back(handle); 342 thread_queue.push_back(handle);
330 g_thread_ready_queue.prepare(priority); 343 thread_ready_queue.prepare(priority);
331 344
345 thread->thread_id = next_thread_id++;
332 thread->status = THREADSTATUS_DORMANT; 346 thread->status = THREADSTATUS_DORMANT;
333 thread->entry_point = entry_point; 347 thread->entry_point = entry_point;
334 thread->stack_top = stack_top; 348 thread->stack_top = stack_top;
@@ -337,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
337 thread->processor_id = processor_id; 351 thread->processor_id = processor_id;
338 thread->wait_type = WAITTYPE_NONE; 352 thread->wait_type = WAITTYPE_NONE;
339 thread->wait_handle = 0; 353 thread->wait_handle = 0;
354 thread->wait_address = 0;
340 thread->name = name; 355 thread->name = name;
341 356
342 return thread; 357 return thread;
@@ -347,28 +362,28 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
347 u32 stack_top, int stack_size) { 362 u32 stack_top, int stack_size) {
348 363
349 if (name == nullptr) { 364 if (name == nullptr) {
350 ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); 365 LOG_ERROR(Kernel_SVC, "nullptr name");
351 return -1; 366 return -1;
352 } 367 }
353 if ((u32)stack_size < 0x200) { 368 if ((u32)stack_size < 0x200) {
354 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, 369 LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
355 stack_size); 370 stack_size);
356 return -1; 371 return -1;
357 } 372 }
358 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 373 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
359 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 374 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
360 WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", 375 LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
361 name, priority, new_priority); 376 name, priority, new_priority);
362 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 377 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
363 // validity of this 378 // validity of this
364 priority = new_priority; 379 priority = new_priority;
365 } 380 }
366 if (!Memory::GetPointer(entry_point)) { 381 if (!Memory::GetPointer(entry_point)) {
367 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); 382 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
368 return -1; 383 return -1;
369 } 384 }
370 Handle handle; 385 Handle handle;
371 Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, 386 Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
372 stack_size); 387 stack_size);
373 388
374 ResetThread(thread, arg, 0); 389 ResetThread(thread, arg, 0);
@@ -378,26 +393,30 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
378} 393}
379 394
380/// Get the priority of the thread specified by handle 395/// Get the priority of the thread specified by handle
381u32 GetThreadPriority(const Handle handle) { 396ResultVal<u32> GetThreadPriority(const Handle handle) {
382 Thread* thread = g_object_pool.GetFast<Thread>(handle); 397 Thread* thread = g_object_pool.Get<Thread>(handle);
383 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); 398 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
384 return thread->current_priority; 399
400 return MakeResult<u32>(thread->current_priority);
385} 401}
386 402
387/// Set the priority of the thread specified by handle 403/// Set the priority of the thread specified by handle
388Result SetThreadPriority(Handle handle, s32 priority) { 404ResultCode SetThreadPriority(Handle handle, s32 priority) {
389 Thread* thread = nullptr; 405 Thread* thread = nullptr;
390 if (!handle) { 406 if (!handle) {
391 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? 407 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
392 } else { 408 } else {
393 thread = g_object_pool.GetFast<Thread>(handle); 409 thread = g_object_pool.Get<Thread>(handle);
410 if (thread == nullptr) {
411 return InvalidHandle(ErrorModule::Kernel);
412 }
394 } 413 }
395 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); 414 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
396 415
397 // If priority is invalid, clamp to valid range 416 // If priority is invalid, clamp to valid range
398 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 417 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
399 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 418 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
400 WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); 419 LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
401 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 420 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
402 // validity of this 421 // validity of this
403 priority = new_priority; 422 priority = new_priority;
@@ -405,37 +424,37 @@ Result SetThreadPriority(Handle handle, s32 priority) {
405 424
406 // Change thread priority 425 // Change thread priority
407 s32 old = thread->current_priority; 426 s32 old = thread->current_priority;
408 g_thread_ready_queue.remove(old, handle); 427 thread_ready_queue.remove(old, handle);
409 thread->current_priority = priority; 428 thread->current_priority = priority;
410 g_thread_ready_queue.prepare(thread->current_priority); 429 thread_ready_queue.prepare(thread->current_priority);
411 430
412 // Change thread status to "ready" and push to ready queue 431 // Change thread status to "ready" and push to ready queue
413 if (thread->IsRunning()) { 432 if (thread->IsRunning()) {
414 thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; 433 thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
415 } 434 }
416 if (thread->IsReady()) { 435 if (thread->IsReady()) {
417 g_thread_ready_queue.push_back(thread->current_priority, handle); 436 thread_ready_queue.push_back(thread->current_priority, handle);
418 } 437 }
419 438
420 return 0; 439 return RESULT_SUCCESS;
421} 440}
422 441
423/// Sets up the primary application thread 442/// Sets up the primary application thread
424Handle SetupMainThread(s32 priority, int stack_size) { 443Handle SetupMainThread(s32 priority, int stack_size) {
425 Handle handle; 444 Handle handle;
426 445
427 // Initialize new "main" thread 446 // Initialize new "main" thread
428 Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, 447 Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
429 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); 448 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
430 449
431 ResetThread(thread, 0, 0); 450 ResetThread(thread, 0, 0);
432 451
433 // If running another thread already, set it to "ready" state 452 // If running another thread already, set it to "ready" state
434 Thread* cur = GetCurrentThread(); 453 Thread* cur = GetCurrentThread();
435 if (cur && cur->IsRunning()) { 454 if (cur && cur->IsRunning()) {
436 ChangeReadyState(cur, true); 455 ChangeReadyState(cur, true);
437 } 456 }
438 457
439 // Run new "main" thread 458 // Run new "main" thread
440 SetCurrentThread(thread); 459 SetCurrentThread(thread);
441 thread->status = THREADSTATUS_RUNNING; 460 thread->status = THREADSTATUS_RUNNING;
@@ -451,13 +470,13 @@ void Reschedule() {
451 Thread* next = NextThread(); 470 Thread* next = NextThread();
452 HLE::g_reschedule = false; 471 HLE::g_reschedule = false;
453 if (next > 0) { 472 if (next > 0) {
454 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); 473 LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
455 474
456 SwitchContext(next); 475 SwitchContext(next);
457 476
458 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep 477 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
459 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. 478 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
460 // This results in the current thread yielding on a VBLANK once, and then it will be 479 // This results in the current thread yielding on a VBLANK once, and then it will be
461 // immediately placed back in the queue for execution. 480 // immediately placed back in the queue for execution.
462 if (prev->wait_type == WAITTYPE_VBLANK) { 481 if (prev->wait_type == WAITTYPE_VBLANK) {
463 ResumeThreadFromWait(prev->GetHandle()); 482 ResumeThreadFromWait(prev->GetHandle());
@@ -465,9 +484,21 @@ void Reschedule() {
465 } 484 }
466} 485}
467 486
487ResultCode GetThreadId(u32* thread_id, Handle handle) {
488 Thread* thread = g_object_pool.Get<Thread>(handle);
489 if (thread == nullptr)
490 return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
491 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
492
493 *thread_id = thread->thread_id;
494
495 return RESULT_SUCCESS;
496}
497
468//////////////////////////////////////////////////////////////////////////////////////////////////// 498////////////////////////////////////////////////////////////////////////////////////////////////////
469 499
470void ThreadingInit() { 500void ThreadingInit() {
501 next_thread_id = INITIAL_THREAD_ID;
471} 502}
472 503
473void ThreadingShutdown() { 504void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 39fa38b75..be7adface 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -1,11 +1,15 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8
9#include "core/mem_map.h"
10
8#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/result.h"
9 13
10enum ThreadPriority { 14enum ThreadPriority {
11 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 15 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
@@ -55,7 +59,15 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
55void Reschedule(); 59void Reschedule();
56 60
57/// Stops the current thread 61/// Stops the current thread
58void StopThread(Handle thread, const char* reason); 62ResultCode StopThread(Handle thread, const char* reason);
63
64/**
65 * Retrieves the ID of the specified thread handle
66 * @param thread_id Will contain the output thread id
67 * @param handle Handle to the thread we want
68 * @return Whether the function was successful or not
69 */
70ResultCode GetThreadId(u32* thread_id, Handle handle);
59 71
60/// Resumes a thread from waiting by marking it as "ready" 72/// Resumes a thread from waiting by marking it as "ready"
61void ResumeThreadFromWait(Handle handle); 73void ResumeThreadFromWait(Handle handle);
@@ -76,14 +88,22 @@ Handle GetCurrentThreadHandle();
76 */ 88 */
77void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); 89void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
78 90
91/**
92 * Puts the current thread in the wait state for the given type
93 * @param wait_type Type of wait
94 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
95 * @param wait_address Arbitration address used to resume from wait
96 */
97void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
98
79/// Put current thread in a wait state - on WaitSynchronization 99/// Put current thread in a wait state - on WaitSynchronization
80void WaitThread_Synchronization(); 100void WaitThread_Synchronization();
81 101
82/// Get the priority of the thread specified by handle 102/// Get the priority of the thread specified by handle
83u32 GetThreadPriority(const Handle handle); 103ResultVal<u32> GetThreadPriority(const Handle handle);
84 104
85/// Set the priority of the thread specified by handle 105/// Set the priority of the thread specified by handle
86Result SetThreadPriority(Handle handle, s32 priority); 106ResultCode SetThreadPriority(Handle handle, s32 priority);
87 107
88/// Initialize threading 108/// Initialize threading
89void ThreadingInit(); 109void ThreadingInit();
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
new file mode 100644
index 000000000..14d2be4a2
--- /dev/null
+++ b/src/core/hle/result.h
@@ -0,0 +1,402 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cassert>
8#include <cstddef>
9#include <type_traits>
10#include <utility>
11
12#include "common/common_types.h"
13#include "common/bit_field.h"
14
15// All the constants in this file come from http://3dbrew.org/wiki/Error_codes
16
17/// Detailed description of the error. This listing is likely incomplete.
18enum class ErrorDescription : u32 {
19 Success = 0,
20 FS_NotFound = 100,
21 FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
22 InvalidSection = 1000,
23 TooLarge = 1001,
24 NotAuthorized = 1002,
25 AlreadyDone = 1003,
26 InvalidSize = 1004,
27 InvalidEnumValue = 1005,
28 InvalidCombination = 1006,
29 NoData = 1007,
30 Busy = 1008,
31 MisalignedAddress = 1009,
32 MisalignedSize = 1010,
33 OutOfMemory = 1011,
34 NotImplemented = 1012,
35 InvalidAddress = 1013,
36 InvalidPointer = 1014,
37 InvalidHandle = 1015,
38 NotInitialized = 1016,
39 AlreadyInitialized = 1017,
40 NotFound = 1018,
41 CancelRequested = 1019,
42 AlreadyExists = 1020,
43 OutOfRange = 1021,
44 Timeout = 1022,
45 InvalidResultValue = 1023,
46};
47
48/**
49 * Identifies the module which caused the error. Error codes can be propagated through a call
50 * chain, meaning that this doesn't always correspond to the module where the API call made is
51 * contained.
52 */
53enum class ErrorModule : u32 {
54 Common = 0,
55 Kernel = 1,
56 Util = 2,
57 FileServer = 3,
58 LoaderServer = 4,
59 TCB = 5,
60 OS = 6,
61 DBG = 7,
62 DMNT = 8,
63 PDN = 9,
64 GX = 10,
65 I2C = 11,
66 GPIO = 12,
67 DD = 13,
68 CODEC = 14,
69 SPI = 15,
70 PXI = 16,
71 FS = 17,
72 DI = 18,
73 HID = 19,
74 CAM = 20,
75 PI = 21,
76 PM = 22,
77 PM_LOW = 23,
78 FSI = 24,
79 SRV = 25,
80 NDM = 26,
81 NWM = 27,
82 SOC = 28,
83 LDR = 29,
84 ACC = 30,
85 RomFS = 31,
86 AM = 32,
87 HIO = 33,
88 Updater = 34,
89 MIC = 35,
90 FND = 36,
91 MP = 37,
92 MPWL = 38,
93 AC = 39,
94 HTTP = 40,
95 DSP = 41,
96 SND = 42,
97 DLP = 43,
98 HIO_LOW = 44,
99 CSND = 45,
100 SSL = 46,
101 AM_LOW = 47,
102 NEX = 48,
103 Friends = 49,
104 RDT = 50,
105 Applet = 51,
106 NIM = 52,
107 PTM = 53,
108 MIDI = 54,
109 MC = 55,
110 SWC = 56,
111 FatFS = 57,
112 NGC = 58,
113 CARD = 59,
114 CARDNOR = 60,
115 SDMC = 61,
116 BOSS = 62,
117 DBM = 63,
118 Config = 64,
119 PS = 65,
120 CEC = 66,
121 IR = 67,
122 UDS = 68,
123 PL = 69,
124 CUP = 70,
125 Gyroscope = 71,
126 MCU = 72,
127 NS = 73,
128 News = 74,
129 RO_1 = 75,
130 GD = 76,
131 CardSPI = 77,
132 EC = 78,
133 RO_2 = 79,
134 WebBrowser = 80,
135 Test = 81,
136 ENC = 82,
137 PIA = 83,
138
139 Application = 254,
140 InvalidResult = 255
141};
142
143/// A less specific error cause.
144enum class ErrorSummary : u32 {
145 Success = 0,
146 NothingHappened = 1,
147 WouldBlock = 2,
148 OutOfResource = 3, ///< There are no more kernel resources (memory, table slots) to
149 ///< execute the operation.
150 NotFound = 4, ///< A file or resource was not found.
151 InvalidState = 5,
152 NotSupported = 6, ///< The operation is not supported or not implemented.
153 InvalidArgument = 7, ///< Returned when a passed argument is invalid in the current runtime
154 ///< context. (Invalid handle, out-of-bounds pointer or size, etc.)
155 WrongArgument = 8, ///< Returned when a passed argument is in an incorrect format for use
156 ///< with the function. (E.g. Invalid enum value)
157 Canceled = 9,
158 StatusChanged = 10,
159 Internal = 11,
160
161 InvalidResult = 63
162};
163
164/// The severity of the error.
165enum class ErrorLevel : u32 {
166 Success = 0,
167 Info = 1,
168
169 Status = 25,
170 Temporary = 26,
171 Permanent = 27,
172 Usage = 28,
173 Reinitialize = 29,
174 Reset = 30,
175 Fatal = 31
176};
177
178/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields.
179union ResultCode {
180 u32 raw;
181
182 BitField<0, 10, ErrorDescription> description;
183 BitField<10, 8, ErrorModule> module;
184
185 BitField<21, 6, ErrorSummary> summary;
186 BitField<27, 5, ErrorLevel> level;
187
188 // The last bit of `level` is checked by apps and the kernel to determine if a result code is an error
189 BitField<31, 1, u32> is_error;
190
191 explicit ResultCode(u32 raw) : raw(raw) {}
192 ResultCode(ErrorDescription description_, ErrorModule module_,
193 ErrorSummary summary_, ErrorLevel level_) : raw(0) {
194 description = description_;
195 module = module_;
196 summary = summary_;
197 level = level_;
198 }
199
200 ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; }
201
202 bool IsSuccess() const {
203 return is_error == 0;
204 }
205
206 bool IsError() const {
207 return is_error == 1;
208 }
209};
210
211inline bool operator==(const ResultCode a, const ResultCode b) {
212 return a.raw == b.raw;
213}
214
215inline bool operator!=(const ResultCode a, const ResultCode b) {
216 return a.raw != b.raw;
217}
218
219// Convenience functions for creating some common kinds of errors:
220
221/// The default success `ResultCode`.
222const ResultCode RESULT_SUCCESS(0);
223
224/// Might be returned instead of a dummy success for unimplemented APIs.
225inline ResultCode UnimplementedFunction(ErrorModule module) {
226 return ResultCode(ErrorDescription::NotImplemented, module,
227 ErrorSummary::NotSupported, ErrorLevel::Permanent);
228}
229/// Returned when a function is passed an invalid handle.
230inline ResultCode InvalidHandle(ErrorModule module) {
231 return ResultCode(ErrorDescription::InvalidHandle, module,
232 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
233}
234
235/**
236 * This is an optional value type. It holds a `ResultCode` and, if that code is a success code,
237 * also holds a result of type `T`. If the code is an error code then trying to access the inner
238 * value fails, thus ensuring that the ResultCode of functions is always checked properly before
239 * their return value is used. It is similar in concept to the `std::optional` type
240 * (http://en.cppreference.com/w/cpp/experimental/optional) originally proposed for inclusion in
241 * C++14, or the `Result` type in Rust (http://doc.rust-lang.org/std/result/index.html).
242 *
243 * An example of how it could be used:
244 * \code
245 * ResultVal<int> Frobnicate(float strength) {
246 * if (strength < 0.f || strength > 1.0f) {
247 * // Can't frobnicate too weakly or too strongly
248 * return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Common,
249 * ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
250 * } else {
251 * // Frobnicated! Give caller a cookie
252 * return MakeResult<int>(42);
253 * }
254 * }
255 * \endcode
256 *
257 * \code
258 * ResultVal<int> frob_result = Frobnicate(0.75f);
259 * if (frob_result) {
260 * // Frobbed ok
261 * printf("My cookie is %d\n", *frob_result);
262 * } else {
263 * printf("Guess I overdid it. :( Error code: %ux\n", frob_result.code().hex);
264 * }
265 * \endcode
266 */
267template <typename T>
268class ResultVal {
269public:
270 /// Constructs an empty `ResultVal` with the given error code. The code must not be a success code.
271 ResultVal(ResultCode error_code = ResultCode(-1))
272 : result_code(error_code)
273 {
274 assert(error_code.IsError());
275 UpdateDebugPtr();
276 }
277
278 /**
279 * Similar to the non-member function `MakeResult`, with the exception that you can manually
280 * specify the success code. `success_code` must not be an error code.
281 */
282 template <typename... Args>
283 static ResultVal WithCode(ResultCode success_code, Args&&... args) {
284 ResultVal<T> result;
285 result.emplace(success_code, std::forward<Args>(args)...);
286 return result;
287 }
288
289 ResultVal(const ResultVal& o)
290 : result_code(o.result_code)
291 {
292 if (!o.empty()) {
293 new (&storage) T(*o.GetPointer());
294 }
295 UpdateDebugPtr();
296 }
297
298 ResultVal(ResultVal&& o)
299 : result_code(o.result_code)
300 {
301 if (!o.empty()) {
302 new (&storage) T(std::move(*o.GetPointer()));
303 }
304 UpdateDebugPtr();
305 }
306
307 ~ResultVal() {
308 if (!empty()) {
309 GetPointer()->~T();
310 }
311 }
312
313 ResultVal& operator=(const ResultVal& o) {
314 if (*this) {
315 if (o) {
316 *GetPointer() = *o.GetPointer();
317 } else {
318 GetPointer()->~T();
319 }
320 } else {
321 if (o) {
322 new (&storage) T(*o.GetPointer());
323 }
324 }
325 result_code = o.result_code;
326 UpdateDebugPtr();
327
328 return *this;
329 }
330
331 /**
332 * Replaces the current result with a new constructed result value in-place. The code must not
333 * be an error code.
334 */
335 template <typename... Args>
336 void emplace(ResultCode success_code, Args&&... args) {
337 assert(success_code.IsSuccess());
338 if (!empty()) {
339 GetPointer()->~T();
340 }
341 new (&storage) T(std::forward<Args>(args)...);
342 result_code = success_code;
343 UpdateDebugPtr();
344 }
345
346 /// Returns true if the `ResultVal` contains an error code and no value.
347 bool empty() const { return result_code.IsError(); }
348
349 /// Returns true if the `ResultVal` contains a return value.
350 bool Succeeded() const { return result_code.IsSuccess(); }
351 /// Returns true if the `ResultVal` contains an error code and no value.
352 bool Failed() const { return empty(); }
353
354 ResultCode Code() const { return result_code; }
355
356 const T& operator* () const { return *GetPointer(); }
357 T& operator* () { return *GetPointer(); }
358 const T* operator->() const { return GetPointer(); }
359 T* operator->() { return GetPointer(); }
360
361 /// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
362 template <typename U>
363 T ValueOr(U&& value) const {
364 return !empty() ? *GetPointer() : std::move(value);
365 }
366
367private:
368 typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
369
370 StorageType storage;
371 ResultCode result_code;
372#if _DEBUG
373 // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the
374 // need to cast `storage` to a pointer or pay attention to `result_code`.
375 const T* debug_ptr;
376#endif
377
378 void UpdateDebugPtr() {
379#if _DEBUG
380 debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
381#endif
382 }
383
384 const T* GetPointer() const {
385 assert(!empty());
386 return static_cast<const T*>(static_cast<const void*>(&storage));
387 }
388
389 T* GetPointer() {
390 assert(!empty());
391 return static_cast<T*>(static_cast<void*>(&storage));
392 }
393};
394
395/**
396 * This function is a helper used to construct `ResultVal`s. It receives the arguments to construct
397 * `T` with and creates a success `ResultVal` contained the constructed value.
398 */
399template <typename T, typename... Args>
400ResultVal<T> MakeResult(Args&&... args) {
401 return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...);
402}
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index b39603bdf..311682abf 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -11,6 +11,24 @@
11 11
12namespace AC_U { 12namespace AC_U {
13 13
14/**
15 * AC_U::GetWifiStatus service function
16 * Outputs:
17 * 1 : Result of function, 0 on success, otherwise error code
18 * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
19 */
20void GetWifiStatus(Service::Interface* self) {
21 u32* cmd_buff = Kernel::GetCommandBuffer();
22
23 // TODO(purpasmart96): This function is only a stub,
24 // it returns a valid result without implementing full functionality.
25
26 cmd_buff[1] = 0; // No error
27 cmd_buff[2] = 0; // Connection type set to none
28
29 LOG_WARNING(Service_AC, "(STUBBED) called");
30}
31
14const Interface::FunctionInfo FunctionTable[] = { 32const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010000, nullptr, "CreateDefaultConfig"}, 33 {0x00010000, nullptr, "CreateDefaultConfig"},
16 {0x00040006, nullptr, "ConnectAsync"}, 34 {0x00040006, nullptr, "ConnectAsync"},
@@ -18,16 +36,16 @@ const Interface::FunctionInfo FunctionTable[] = {
18 {0x00080004, nullptr, "CloseAsync"}, 36 {0x00080004, nullptr, "CloseAsync"},
19 {0x00090002, nullptr, "GetCloseResult"}, 37 {0x00090002, nullptr, "GetCloseResult"},
20 {0x000A0000, nullptr, "GetLastErrorCode"}, 38 {0x000A0000, nullptr, "GetLastErrorCode"},
21 {0x000D0000, nullptr, "GetWifiStatus"}, 39 {0x000D0000, GetWifiStatus, "GetWifiStatus"},
22 {0x000E0042, nullptr, "GetCurrentAPInfo"}, 40 {0x000E0042, nullptr, "GetCurrentAPInfo"},
23 {0x00100042, nullptr, "GetCurrentNZoneInfo"}, 41 {0x00100042, nullptr, "GetCurrentNZoneInfo"},
24 {0x00110042, nullptr, "GetNZoneApNumService"}, 42 {0x00110042, nullptr, "GetNZoneApNumService"},
25 {0x00240042, nullptr, "AddDenyApType "}, 43 {0x00240042, nullptr, "AddDenyApType"},
26 {0x00270002, nullptr, "GetInfraPriority "}, 44 {0x00270002, nullptr, "GetInfraPriority"},
27 {0x002D0082, nullptr, "SetRequestEulaVersion"}, 45 {0x002D0082, nullptr, "SetRequestEulaVersion"},
28 {0x00300004, nullptr, "RegisterDisconnectEvent"}, 46 {0x00300004, nullptr, "RegisterDisconnectEvent"},
29 {0x003C0042, nullptr, "GetAPSSIDList"}, 47 {0x003C0042, nullptr, "GetAPSSIDList"},
30 {0x003E0042, nullptr, "IsConnected "}, 48 {0x003E0042, nullptr, "IsConnected"},
31 {0x00400042, nullptr, "SetClientVersion"}, 49 {0x00400042, nullptr, "SetClientVersion"},
32}; 50};
33 51
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h
index 3c5958d27..c91b28353 100644
--- a/src/core/hle/service/ac_u.h
+++ b/src/core/hle/service/ac_u.h
@@ -9,7 +9,7 @@
9//////////////////////////////////////////////////////////////////////////////////////////////////// 9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace AC_U 10// Namespace AC_U
11 11
12// socket service "ac:u" 12// socket service "ac:u"
13 13
14namespace AC_U { 14namespace AC_U {
15 15
@@ -21,7 +21,7 @@ public:
21 * Gets the string port name used by CTROS for the service 21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service 22 * @return Port name of service
23 */ 23 */
24 std::string GetPortName() const { 24 std::string GetPortName() const override {
25 return "ac:u"; 25 return "ac:u";
26 } 26 }
27}; 27};
diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp
new file mode 100644
index 000000000..b8b06418c
--- /dev/null
+++ b/src/core/hle/service/am_app.cpp
@@ -0,0 +1,24 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/am_app.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace AM_APP
11
12namespace AM_APP {
13
14// Empty arrays are illegal -- commented out until an entry is added.
15//const Interface::FunctionInfo FunctionTable[] = { };
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18// Interface class
19
20Interface::Interface() {
21 //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
22}
23
24} // namespace
diff --git a/src/core/hle/service/am_app.h b/src/core/hle/service/am_app.h
new file mode 100644
index 000000000..86a5f5b74
--- /dev/null
+++ b/src/core/hle/service/am_app.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace AM_APP
11
12namespace AM_APP {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "am:app";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp
new file mode 100644
index 000000000..403cac353
--- /dev/null
+++ b/src/core/hle/service/am_net.cpp
@@ -0,0 +1,47 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/am_net.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace AM_NET
11
12namespace AM_NET {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x08010000, nullptr, "OpenTicket"},
16 {0x08020002, nullptr, "TicketAbortInstall"},
17 {0x08030002, nullptr, "TicketFinalizeInstall"},
18 {0x08040100, nullptr, "InstallTitleBegin"},
19 {0x08050000, nullptr, "InstallTitleAbort"},
20 {0x080600C0, nullptr, "InstallTitleResume"},
21 {0x08070000, nullptr, "InstallTitleAbortTMD"},
22 {0x08080000, nullptr, "InstallTitleFinish"},
23 {0x080A0000, nullptr, "OpenTMD"},
24 {0x080B0002, nullptr, "TMDAbortInstall"},
25 {0x080C0042, nullptr, "TMDFinalizeInstall"},
26 {0x080E0040, nullptr, "OpenContentCreate"},
27 {0x080F0002, nullptr, "ContentAbortInstall"},
28 {0x08100040, nullptr, "OpenContentResume"},
29 {0x08120002, nullptr, "ContentFinalizeInstall"},
30 {0x08130000, nullptr, "GetTotalContents"},
31 {0x08140042, nullptr, "GetContentIndexes"},
32 {0x08150044, nullptr, "GetContentsInfo"},
33 {0x08190108, nullptr, "Unknown"},
34 {0x081B00C2, nullptr, "InstallTitlesFinish"},
35};
36
37////////////////////////////////////////////////////////////////////////////////////////////////////
38// Interface class
39
40Interface::Interface() {
41 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
42}
43
44Interface::~Interface() {
45}
46
47} // namespace
diff --git a/src/core/hle/service/fs_user.h b/src/core/hle/service/am_net.h
index 005382540..4816e1697 100644
--- a/src/core/hle/service/fs_user.h
+++ b/src/core/hle/service/am_net.h
@@ -7,24 +7,20 @@
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9//////////////////////////////////////////////////////////////////////////////////////////////////// 9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace FS_User 10// Namespace AM_NET
11 11
12namespace FS_User { 12namespace AM_NET {
13 13
14/// Interface to "fs:USER" service
15class Interface : public Service::Interface { 14class Interface : public Service::Interface {
16public: 15public:
17
18 Interface(); 16 Interface();
19
20 ~Interface(); 17 ~Interface();
21
22 /** 18 /**
23 * Gets the string port name used by CTROS for the service 19 * Gets the string port name used by CTROS for the service
24 * @return Port name of service 20 * @return Port name of service
25 */ 21 */
26 std::string GetPortName() const override { 22 std::string GetPortName() const override {
27 return "fs:USER"; 23 return "am:net";
28 } 24 }
29}; 25};
30 26
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index 617b6add4..ebfba4d8d 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -4,10 +4,12 @@
4 4
5 5
6#include "common/common.h" 6#include "common/common.h"
7#include "common/file_util.h"
7 8
8#include "core/hle/hle.h" 9#include "core/hle/hle.h"
9#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/mutex.h" 11#include "core/hle/kernel/mutex.h"
12#include "core/hle/kernel/shared_memory.h"
11#include "apt_u.h" 13#include "apt_u.h"
12 14
13//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,6 +17,20 @@
15 17
16namespace APT_U { 18namespace APT_U {
17 19
20// Address used for shared font (as observed on HW)
21// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
22// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
23// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
24// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
25// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
26static const VAddr SHARED_FONT_VADDR = 0x18000000;
27
28// Handle to shared memory region designated to for shared system font
29static Handle shared_font_mem = 0;
30
31static Handle lock_handle = 0;
32static std::vector<u8> shared_font;
33
18/// Signals used by APT functions 34/// Signals used by APT functions
19enum class SignalType : u32 { 35enum class SignalType : u32 {
20 None = 0x0, 36 None = 0x0,
@@ -24,83 +40,178 @@ enum class SignalType : u32 {
24}; 40};
25 41
26void Initialize(Service::Interface* self) { 42void Initialize(Service::Interface* self) {
27 u32* cmd_buff = Service::GetCommandBuffer(); 43 u32* cmd_buff = Kernel::GetCommandBuffer();
28 44
29 cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle 45 cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
30 cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle 46 cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
31 47
32 Kernel::SetEventLocked(cmd_buff[3], true); 48 Kernel::SetEventLocked(cmd_buff[3], true);
33 Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event 49 Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
34 50
51 _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock");
52 Kernel::ReleaseMutex(lock_handle);
53
35 cmd_buff[1] = 0; // No error 54 cmd_buff[1] = 0; // No error
36 DEBUG_LOG(KERNEL, "called"); 55
56 LOG_DEBUG(Service_APT, "called");
37} 57}
38 58
39void GetLockHandle(Service::Interface* self) { 59void GetLockHandle(Service::Interface* self) {
40 u32* cmd_buff = Service::GetCommandBuffer(); 60 u32* cmd_buff = Kernel::GetCommandBuffer();
41 u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field 61 u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
62
63 if (0 == lock_handle) {
64 // TODO(bunnei): Verify if this is created here or at application boot?
65 lock_handle = Kernel::CreateMutex(false, "APT_U:Lock");
66 Kernel::ReleaseMutex(lock_handle);
67 }
42 cmd_buff[1] = 0; // No error 68 cmd_buff[1] = 0; // No error
43 cmd_buff[5] = Kernel::CreateMutex(false, "APT_U:Lock"); 69
44 DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]); 70 // Not sure what these parameters are used for, but retail apps check that they are 0 after
71 // GetLockHandle has been called.
72 cmd_buff[2] = 0;
73 cmd_buff[3] = 0;
74 cmd_buff[4] = 0;
75
76 cmd_buff[5] = lock_handle;
77 LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
45} 78}
46 79
47void Enable(Service::Interface* self) { 80void Enable(Service::Interface* self) {
48 u32* cmd_buff = Service::GetCommandBuffer(); 81 u32* cmd_buff = Kernel::GetCommandBuffer();
49 u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? 82 u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
50 cmd_buff[1] = 0; // No error 83 cmd_buff[1] = 0; // No error
51 WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk); 84 LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
52} 85}
53 86
54void InquireNotification(Service::Interface* self) { 87void InquireNotification(Service::Interface* self) {
55 u32* cmd_buff = Service::GetCommandBuffer(); 88 u32* cmd_buff = Kernel::GetCommandBuffer();
56 u32 app_id = cmd_buff[2]; 89 u32 app_id = cmd_buff[2];
57 cmd_buff[1] = 0; // No error 90 cmd_buff[1] = 0; // No error
58 cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type 91 cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
59 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id); 92 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
60} 93}
61 94
95/**
96 * APT_U::ReceiveParameter service function. This returns the current parameter data from NS state,
97 * from the source process which set the parameters. Once finished, NS will clear a flag in the NS
98 * state so that this command will return an error if this command is used again if parameters were
99 * not set again. This is called when the second Initialize event is triggered. It returns a signal
100 * type indicating why it was triggered.
101 * Inputs:
102 * 1 : AppID
103 * 2 : Parameter buffer size, max size is 0x1000
104 * Outputs:
105 * 1 : Result of function, 0 on success, otherwise error code
106 * 2 : Unknown, for now assume AppID of the process which sent these parameters
107 * 3 : Unknown, for now assume Signal type
108 * 4 : Actual parameter buffer size, this is <= to the the input size
109 * 5 : Value
110 * 6 : Handle from the source process which set the parameters, likely used for shared memory
111 * 7 : Size
112 * 8 : Output parameter buffer ptr
113 */
62void ReceiveParameter(Service::Interface* self) { 114void ReceiveParameter(Service::Interface* self) {
63 u32* cmd_buff = Service::GetCommandBuffer(); 115 u32* cmd_buff = Kernel::GetCommandBuffer();
64 u32 app_id = cmd_buff[1]; 116 u32 app_id = cmd_buff[1];
65 u32 buffer_size = cmd_buff[2]; 117 u32 buffer_size = cmd_buff[2];
66 cmd_buff[1] = 0; // No error 118 cmd_buff[1] = 0; // No error
67 cmd_buff[2] = 0; 119 cmd_buff[2] = 0;
68 cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type 120 cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
69 cmd_buff[4] = 0x10; 121 cmd_buff[4] = 0x10; // Parameter buffer size (16)
70 cmd_buff[5] = 0; 122 cmd_buff[5] = 0;
71 cmd_buff[6] = 0; 123 cmd_buff[6] = 0;
72 cmd_buff[7] = 0; 124 cmd_buff[7] = 0;
73 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); 125 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
74} 126}
75 127
76/** 128/**
77* APT_U::GlanceParameter service function 129 * APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
78* Inputs: 130 * (except for the word value prior to the output handle), except this will not clear the flag
79* 1 : AppID 131 * (except when responseword[3]==8 || responseword[3]==9) in NS state.
80* 2 : Parameter buffer size, max size is 0x1000 132 * Inputs:
81* Outputs: 133 * 1 : AppID
82* 1 : Result of function, 0 on success, otherwise error code 134 * 2 : Parameter buffer size, max size is 0x1000
83* 2 : Unknown, for now assume AppID of the process which sent these parameters 135 * Outputs:
84* 3 : Unknown, for now assume Signal type 136 * 1 : Result of function, 0 on success, otherwise error code
85* 4 : Actual parameter buffer size, this is <= to the the input size 137 * 2 : Unknown, for now assume AppID of the process which sent these parameters
86* 5 : Value 138 * 3 : Unknown, for now assume Signal type
87* 6 : Handle from the source process which set the parameters, likely used for shared memory 139 * 4 : Actual parameter buffer size, this is <= to the the input size
88* 7 : Size 140 * 5 : Value
89* 8 : Output parameter buffer ptr 141 * 6 : Handle from the source process which set the parameters, likely used for shared memory
90*/ 142 * 7 : Size
143 * 8 : Output parameter buffer ptr
144 */
91void GlanceParameter(Service::Interface* self) { 145void GlanceParameter(Service::Interface* self) {
92 u32* cmd_buff = Service::GetCommandBuffer(); 146 u32* cmd_buff = Kernel::GetCommandBuffer();
93 u32 app_id = cmd_buff[1]; 147 u32 app_id = cmd_buff[1];
94 u32 buffer_size = cmd_buff[2]; 148 u32 buffer_size = cmd_buff[2];
149
95 cmd_buff[1] = 0; // No error 150 cmd_buff[1] = 0; // No error
96 cmd_buff[2] = 0; 151 cmd_buff[2] = 0;
97 cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type 152 cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type
98 cmd_buff[4] = 0; 153 cmd_buff[4] = 0x10; // Parameter buffer size (16)
99 cmd_buff[5] = 0; 154 cmd_buff[5] = 0;
100 cmd_buff[6] = 0; 155 cmd_buff[6] = 0;
101 cmd_buff[7] = 0; 156 cmd_buff[7] = 0;
102 cmd_buff[8] = 0; 157
103 WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); 158 LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
159}
160
161/**
162 * APT_U::AppletUtility service function
163 * Inputs:
164 * 1 : Unknown, but clearly used for something
165 * 2 : Buffer 1 size (purpose is unknown)
166 * 3 : Buffer 2 size (purpose is unknown)
167 * 5 : Buffer 1 address (purpose is unknown)
168 * 65 : Buffer 2 address (purpose is unknown)
169 * Outputs:
170 * 1 : Result of function, 0 on success, otherwise error code
171 */
172void AppletUtility(Service::Interface* self) {
173 u32* cmd_buff = Kernel::GetCommandBuffer();
174
175 // These are from 3dbrew - I'm not really sure what they're used for.
176 u32 unk = cmd_buff[1];
177 u32 buffer1_size = cmd_buff[2];
178 u32 buffer2_size = cmd_buff[3];
179 u32 buffer1_addr = cmd_buff[5];
180 u32 buffer2_addr = cmd_buff[65];
181
182 cmd_buff[1] = 0; // No error
183
184 LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
185 "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
186 buffer1_addr, buffer2_addr);
187}
188
189/**
190 * APT_U::GetSharedFont service function
191 * Outputs:
192 * 1 : Result of function, 0 on success, otherwise error code
193 * 2 : Virtual address of where shared font will be loaded in memory
194 * 4 : Handle to shared font memory
195 */
196void GetSharedFont(Service::Interface* self) {
197 LOG_TRACE(Kernel_SVC, "called");
198
199 u32* cmd_buff = Kernel::GetCommandBuffer();
200
201 if (!shared_font.empty()) {
202 // TODO(bunnei): This function shouldn't copy the shared font every time it's called.
203 // Instead, it should probably map the shared font as RO memory. We don't currently have
204 // an easy way to do this, but the copy should be sufficient for now.
205 memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
206
207 cmd_buff[0] = 0x00440082;
208 cmd_buff[1] = 0; // No error
209 cmd_buff[2] = SHARED_FONT_VADDR;
210 cmd_buff[4] = shared_font_mem;
211 } else {
212 cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
213 LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
214 }
104} 215}
105 216
106const Interface::FunctionInfo FunctionTable[] = { 217const Interface::FunctionInfo FunctionTable[] = {
@@ -171,14 +282,14 @@ const Interface::FunctionInfo FunctionTable[] = {
171 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, 282 {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
172 {0x00420080, nullptr, "SleepSystem"}, 283 {0x00420080, nullptr, "SleepSystem"},
173 {0x00430040, nullptr, "NotifyToWait"}, 284 {0x00430040, nullptr, "NotifyToWait"},
174 {0x00440000, nullptr, "GetSharedFont"}, 285 {0x00440000, GetSharedFont, "GetSharedFont"},
175 {0x00450040, nullptr, "GetWirelessRebootInfo"}, 286 {0x00450040, nullptr, "GetWirelessRebootInfo"},
176 {0x00460104, nullptr, "Wrap"}, 287 {0x00460104, nullptr, "Wrap"},
177 {0x00470104, nullptr, "Unwrap"}, 288 {0x00470104, nullptr, "Unwrap"},
178 {0x00480100, nullptr, "GetProgramInfo"}, 289 {0x00480100, nullptr, "GetProgramInfo"},
179 {0x00490180, nullptr, "Reboot"}, 290 {0x00490180, nullptr, "Reboot"},
180 {0x004A0040, nullptr, "GetCaptureInfo"}, 291 {0x004A0040, nullptr, "GetCaptureInfo"},
181 {0x004B00C2, nullptr, "AppletUtility"}, 292 {0x004B00C2, AppletUtility, "AppletUtility"},
182 {0x004C0000, nullptr, "SetFatalErrDispMode"}, 293 {0x004C0000, nullptr, "SetFatalErrDispMode"},
183 {0x004D0080, nullptr, "GetAppletProgramInfo"}, 294 {0x004D0080, nullptr, "GetAppletProgramInfo"},
184 {0x004E0000, nullptr, "HardwareResetAsync"}, 295 {0x004E0000, nullptr, "HardwareResetAsync"},
@@ -190,6 +301,32 @@ const Interface::FunctionInfo FunctionTable[] = {
190// Interface class 301// Interface class
191 302
192Interface::Interface() { 303Interface::Interface() {
304 // Load the shared system font (if available).
305 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
306 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
307 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
308 // "shared_font.bin" in the Citra "sysdata" directory.
309
310 shared_font.clear();
311 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
312
313 FileUtil::CreateFullPath(filepath); // Create path if not already created
314 FileUtil::IOFile file(filepath, "rb");
315
316 if (file.IsOpen()) {
317 // Read shared font data
318 shared_font.resize(file.GetSize());
319 file.ReadBytes(shared_font.data(), file.GetSize());
320
321 // Create shared font memory object
322 shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem");
323 } else {
324 LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
325 shared_font_mem = 0;
326 }
327
328 lock_handle = 0;
329
193 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 330 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
194} 331}
195 332
diff --git a/src/core/hle/service/apt_u.h b/src/core/hle/service/apt_u.h
index 5af39e085..306730400 100644
--- a/src/core/hle/service/apt_u.h
+++ b/src/core/hle/service/apt_u.h
@@ -13,8 +13,8 @@ namespace APT_U {
13 13
14// Application and title launching service. These services handle signaling for home/power button as 14// Application and title launching service. These services handle signaling for home/power button as
15// well. Only one session for either APT service can be open at a time, normally processes close the 15// well. Only one session for either APT service can be open at a time, normally processes close the
16// service handle immediately once finished using the service. The commands for APT:U and APT:S are 16// service handle immediately once finished using the service. The commands for APT:U and APT:S are
17// exactly the same, however certain commands are only accessible with APT:S(NS module will call 17// exactly the same, however certain commands are only accessible with APT:S(NS module will call
18// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services. 18// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
19 19
20/// Interface to "APT:U" service 20/// 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 000000000..b2ff4a756
--- /dev/null
+++ b/src/core/hle/service/boss_u.cpp
@@ -0,0 +1,28 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/boss_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace BOSS_U
11
12namespace BOSS_U {
13
14 const Interface::FunctionInfo FunctionTable[] = {
15 {0x00020100, nullptr, "GetStorageInfo"},
16 };
17
18 ////////////////////////////////////////////////////////////////////////////////////////////////////
19 // Interface class
20
21 Interface::Interface() {
22 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
23 }
24
25 Interface::~Interface() {
26 }
27
28} // namespace
diff --git a/src/core/hle/service/boss_u.h b/src/core/hle/service/boss_u.h
new file mode 100644
index 000000000..af39b8e65
--- /dev/null
+++ b/src/core/hle/service/boss_u.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace BOSS_U
11
12namespace BOSS_U {
13
14 class Interface : public Service::Interface {
15 public:
16 Interface();
17 ~Interface();
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const {
23 return "boss:U";
24 }
25 };
26
27} // namespace
diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp
new file mode 100644
index 000000000..25d903516
--- /dev/null
+++ b/src/core/hle/service/cecd_u.cpp
@@ -0,0 +1,24 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/cecd_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CECD_U
11
12namespace CECD_U {
13
14// Empty arrays are illegal -- commented out until an entry is added.
15//const Interface::FunctionInfo FunctionTable[] = { };
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18// Interface class
19
20Interface::Interface() {
21 //Register(FunctionTable, ARRAY_SIZE(FunctionTable));
22}
23
24} // namespace
diff --git a/src/core/hle/service/cecd_u.h b/src/core/hle/service/cecd_u.h
new file mode 100644
index 000000000..969e1ed1b
--- /dev/null
+++ b/src/core/hle/service/cecd_u.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CECD_U
11
12namespace CECD_U {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "cecd:u";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/cfg_i.cpp b/src/core/hle/service/cfg_i.cpp
new file mode 100644
index 000000000..88d13d459
--- /dev/null
+++ b/src/core/hle/service/cfg_i.cpp
@@ -0,0 +1,59 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/cfg_i.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CFG_I
11
12namespace CFG_I {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x04010082, nullptr, "GetConfigInfoBlk8"},
16 {0x04020082, nullptr, "GetConfigInfoBlk4"},
17 {0x04030000, nullptr, "UpdateConfigNANDSavegame"},
18 {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
19 {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
20 {0x04060000, nullptr, "SecureInfoGetRegion"},
21 {0x04070000, nullptr, "SecureInfoGetByte101"},
22 {0x04080042, nullptr, "SecureInfoGetSerialNo"},
23 {0x04090000, nullptr, "UpdateConfigBlk00040003"},
24 {0x08010082, nullptr, "GetConfigInfoBlk8"},
25 {0x08020082, nullptr, "GetConfigInfoBlk4"},
26 {0x08030000, nullptr, "UpdateConfigNANDSavegame"},
27 {0x080400C2, nullptr, "CreateConfigInfoBlk"},
28 {0x08050000, nullptr, "DeleteConfigNANDSavefile"},
29 {0x08060000, nullptr, "FormatConfig"},
30 {0x08070000, nullptr, "Unknown"},
31 {0x08080000, nullptr, "UpdateConfigBlk1"},
32 {0x08090000, nullptr, "UpdateConfigBlk2"},
33 {0x080A0000, nullptr, "UpdateConfigBlk3"},
34 {0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
35 {0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
36 {0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
37 {0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
38 {0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
39 {0x08100000, nullptr, "GetLocalFriendCodeSeed"},
40 {0x08110084, nullptr, "SetSecureInfo"},
41 {0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
42 {0x08130000, nullptr, "VerifySigSecureInfo"},
43 {0x08140042, nullptr, "SecureInfoGetData"},
44 {0x08150042, nullptr, "SecureInfoGetSignature"},
45 {0x08160000, nullptr, "SecureInfoGetRegion"},
46 {0x08170000, nullptr, "SecureInfoGetByte101"},
47 {0x08180042, nullptr, "SecureInfoGetSerialNo"},
48};
49////////////////////////////////////////////////////////////////////////////////////////////////////
50// Interface class
51
52Interface::Interface() {
53 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
54}
55
56Interface::~Interface() {
57}
58
59} // namespace
diff --git a/src/core/hle/service/cfg_i.h b/src/core/hle/service/cfg_i.h
new file mode 100644
index 000000000..fe343c968
--- /dev/null
+++ b/src/core/hle/service/cfg_i.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CFG_I
11
12namespace CFG_I {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17 ~Interface();
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "cfg:i";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp
index 822b0e2b8..2e9d7bf21 100644
--- a/src/core/hle/service/cfg_u.cpp
+++ b/src/core/hle/service/cfg_u.cpp
@@ -11,6 +11,94 @@
11 11
12namespace CFG_U { 12namespace CFG_U {
13 13
14// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
15#define C(code) ((code)[0] | ((code)[1] << 8))
16
17static const std::array<u16, 187> country_codes = {
18 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7
19 C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15
20 C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23
21 C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31
22 C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39
23 C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47
24 C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55
25 0, 0, 0, 0, 0, 0, 0, 0, // 56-63
26 C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71
27 C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79
28 C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87
29 C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95
30 C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103
31 C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111
32 C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119
33 C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127
34 C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135
35 C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143
36 C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151
37 C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159
38 C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167
39 C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175
40 C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183
41 C("SM"), C("VA"), C("BM") // 184-186
42};
43
44#undef C
45
46/**
47 * CFG_User::GetCountryCodeString service function
48 * Inputs:
49 * 1 : Country Code ID
50 * Outputs:
51 * 1 : Result of function, 0 on success, otherwise error code
52 * 2 : Country's 2-char string
53 */
54static void GetCountryCodeString(Service::Interface* self) {
55 u32* cmd_buffer = Kernel::GetCommandBuffer();
56 u32 country_code_id = cmd_buffer[1];
57
58 if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
59 LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id);
60 cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
61 return;
62 }
63
64 cmd_buffer[1] = 0;
65 cmd_buffer[2] = country_codes[country_code_id];
66}
67
68/**
69 * CFG_User::GetCountryCodeID service function
70 * Inputs:
71 * 1 : Country Code 2-char string
72 * Outputs:
73 * 1 : Result of function, 0 on success, otherwise error code
74 * 2 : Country Code ID
75 */
76static void GetCountryCodeID(Service::Interface* self) {
77 u32* cmd_buffer = Kernel::GetCommandBuffer();
78 u16 country_code = cmd_buffer[1];
79 u16 country_code_id = 0;
80
81 // The following algorithm will fail if the first country code isn't 0.
82 _dbg_assert_(Service_CFG, country_codes[0] == 0);
83
84 for (size_t id = 0; id < country_codes.size(); ++id) {
85 if (country_codes[id] == country_code) {
86 country_code_id = id;
87 break;
88 }
89 }
90
91 if (0 == country_code_id) {
92 LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8);
93 cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
94 cmd_buffer[2] = 0xFFFF;
95 return;
96 }
97
98 cmd_buffer[1] = 0;
99 cmd_buffer[2] = country_code_id;
100}
101
14const Interface::FunctionInfo FunctionTable[] = { 102const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010082, nullptr, "GetConfigInfoBlk2"}, 103 {0x00010082, nullptr, "GetConfigInfoBlk2"},
16 {0x00020000, nullptr, "SecureInfoGetRegion"}, 104 {0x00020000, nullptr, "SecureInfoGetRegion"},
@@ -20,8 +108,8 @@ const Interface::FunctionInfo FunctionTable[] = {
20 {0x00060000, nullptr, "GetModelNintendo2DS"}, 108 {0x00060000, nullptr, "GetModelNintendo2DS"},
21 {0x00070040, nullptr, "unknown"}, 109 {0x00070040, nullptr, "unknown"},
22 {0x00080080, nullptr, "unknown"}, 110 {0x00080080, nullptr, "unknown"},
23 {0x00090080, nullptr, "GetCountryCodeString"}, 111 {0x00090040, GetCountryCodeString, "GetCountryCodeString"},
24 {0x000A0040, nullptr, "GetCountryCodeID"}, 112 {0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
25}; 113};
26//////////////////////////////////////////////////////////////////////////////////////////////////// 114////////////////////////////////////////////////////////////////////////////////////////////////////
27// Interface class 115// Interface class
diff --git a/src/core/hle/service/cfg_u.h b/src/core/hle/service/cfg_u.h
index 7525bd7c6..8075d19a8 100644
--- a/src/core/hle/service/cfg_u.h
+++ b/src/core/hle/service/cfg_u.h
@@ -19,7 +19,7 @@ public:
19 * Gets the string port name used by CTROS for the service 19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service 20 * @return Port name of service
21 */ 21 */
22 std::string GetPortName() const { 22 std::string GetPortName() const override {
23 return "cfg:u"; 23 return "cfg:u";
24 } 24 }
25}; 25};
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
new file mode 100644
index 000000000..6e59a9bf3
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -0,0 +1,39 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/csnd_snd.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CSND_SND
11
12namespace CSND_SND {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010140, nullptr, "Initialize"},
16 {0x00020000, nullptr, "Shutdown"},
17 {0x00030040, nullptr, "Unknown"},
18 {0x00040080, nullptr, "Unknown"},
19 {0x00050000, nullptr, "Unknown"},
20 {0x00060000, nullptr, "Unknown"},
21 {0x00070000, nullptr, "Unknown"},
22 {0x00080040, nullptr, "Unknown"},
23 {0x00090082, nullptr, "FlushDCache"},
24 {0x000A0082, nullptr, "StoreDCache"},
25 {0x000B0082, nullptr, "InvalidateDCache"},
26 {0x000C0000, nullptr, "Unknown"},
27};
28
29////////////////////////////////////////////////////////////////////////////////////////////////////
30// Interface class
31
32Interface::Interface() {
33 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
34}
35
36Interface::~Interface() {
37}
38
39} // namespace
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
new file mode 100644
index 000000000..31cc85b07
--- /dev/null
+++ b/src/core/hle/service/csnd_snd.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace CSND_SND
11
12namespace CSND_SND {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17 ~Interface();
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "csnd:SND";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index 9e84ac938..bd82063c6 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/log.h" 5#include "common/log.h"
6#include "core/hle/hle.h" 6#include "core/hle/hle.h"
7#include "core/hle/kernel/event.h"
7#include "core/hle/service/dsp_dsp.h" 8#include "core/hle/service/dsp_dsp.h"
8 9
9//////////////////////////////////////////////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,38 +12,181 @@
11 12
12namespace DSP_DSP { 13namespace DSP_DSP {
13 14
15static u32 read_pipe_count;
16static Handle semaphore_event;
17static Handle interrupt_event;
18
19/**
20 * DSP_DSP::ConvertProcessAddressFromDspDram service function
21 * Inputs:
22 * 1 : Address
23 * Outputs:
24 * 1 : Result of function, 0 on success, otherwise error code
25 * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address)
26 */
27void ConvertProcessAddressFromDspDram(Service::Interface* self) {
28 u32* cmd_buff = Kernel::GetCommandBuffer();
29
30 u32 addr = cmd_buff[1];
31
32 cmd_buff[1] = 0; // No error
33 cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000);
34
35 LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr);
36}
37
38/**
39 * DSP_DSP::LoadComponent service function
40 * Inputs:
41 * 1 : Size
42 * 2 : Unknown (observed only half word used)
43 * 3 : Unknown (observed only half word used)
44 * 4 : (size << 4) | 0xA
45 * 5 : Buffer address
46 * Outputs:
47 * 1 : Result of function, 0 on success, otherwise error code
48 * 2 : Component loaded, 0 on not loaded, 1 on loaded
49 */
50void LoadComponent(Service::Interface* self) {
51 u32* cmd_buff = Kernel::GetCommandBuffer();
52
53 cmd_buff[1] = 0; // No error
54 cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
55
56 // TODO(bunnei): Implement real DSP firmware loading
57
58 LOG_WARNING(Service_DSP, "(STUBBED) called");
59}
60
61/**
62 * DSP_DSP::GetSemaphoreEventHandle service function
63 * Outputs:
64 * 1 : Result of function, 0 on success, otherwise error code
65 * 3 : Semaphore event handle
66 */
67void GetSemaphoreEventHandle(Service::Interface* self) {
68 u32* cmd_buff = Kernel::GetCommandBuffer();
69
70 cmd_buff[1] = 0; // No error
71 cmd_buff[3] = semaphore_event; // Event handle
72
73 LOG_WARNING(Service_DSP, "(STUBBED) called");
74}
75
76/**
77 * DSP_DSP::RegisterInterruptEvents service function
78 * Inputs:
79 * 1 : Parameter 0 (purpose unknown)
80 * 2 : Parameter 1 (purpose unknown)
81 * 4 : Interrupt event handle
82 * Outputs:
83 * 1 : Result of function, 0 on success, otherwise error code
84 */
85void RegisterInterruptEvents(Service::Interface* self) {
86 u32* cmd_buff = Kernel::GetCommandBuffer();
87
88 interrupt_event = static_cast<Handle>(cmd_buff[4]);
89
90 cmd_buff[1] = 0; // No error
91
92 LOG_WARNING(Service_DSP, "(STUBBED) called");
93}
94
95/**
96 * DSP_DSP::WriteReg0x10 service function
97 * Inputs:
98 * 1 : Unknown (observed only half word used)
99 * Outputs:
100 * 1 : Result of function, 0 on success, otherwise error code
101 */
102void WriteReg0x10(Service::Interface* self) {
103 u32* cmd_buff = Kernel::GetCommandBuffer();
104
105 Kernel::SignalEvent(interrupt_event);
106
107 cmd_buff[1] = 0; // No error
108
109 LOG_WARNING(Service_DSP, "(STUBBED) called");
110}
111
112/**
113 * DSP_DSP::ReadPipeIfPossible service function
114 * Inputs:
115 * 1 : Unknown
116 * 2 : Unknown
117 * 3 : Size in bytes of read (observed only lower half word used)
118 * 0x41 : Virtual address to read from DSP pipe to in memory
119 * Outputs:
120 * 1 : Result of function, 0 on success, otherwise error code
121 * 2 : Number of bytes read from pipe
122 */
123void ReadPipeIfPossible(Service::Interface* self) {
124 u32* cmd_buff = Kernel::GetCommandBuffer();
125
126 u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
127 VAddr addr = cmd_buff[0x41];
128
129 // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
130 // TODO: Remove this hack :)
131 static const std::array<u16, 16> canned_read_pipe = {
132 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
133 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
134 };
135
136 u32 initial_size = read_pipe_count;
137
138 for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
139 if (read_pipe_count < canned_read_pipe.size()) {
140 Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
141 read_pipe_count++;
142 } else {
143 LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
144 break;
145 }
146 }
147
148 cmd_buff[1] = 0; // No error
149 cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
150
151 LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr);
152}
153
14const Interface::FunctionInfo FunctionTable[] = { 154const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010040, nullptr, "RecvData"}, 155 {0x00010040, nullptr, "RecvData"},
16 {0x00020040, nullptr, "RecvDataIsReady"}, 156 {0x00020040, nullptr, "RecvDataIsReady"},
17 {0x00030080, nullptr, "SendData"}, 157 {0x00030080, nullptr, "SendData"},
18 {0x00040040, nullptr, "SendDataIsEmpty"}, 158 {0x00040040, nullptr, "SendDataIsEmpty"},
19 {0x00070040, nullptr, "WriteReg0x10"}, 159 {0x00070040, WriteReg0x10, "WriteReg0x10"},
20 {0x00080000, nullptr, "GetSemaphore"}, 160 {0x00080000, nullptr, "GetSemaphore"},
21 {0x00090040, nullptr, "ClearSemaphore"}, 161 {0x00090040, nullptr, "ClearSemaphore"},
22 {0x000B0000, nullptr, "CheckSemaphoreRequest"}, 162 {0x000B0000, nullptr, "CheckSemaphoreRequest"},
23 {0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"}, 163 {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
24 {0x000D0082, nullptr, "WriteProcessPipe"}, 164 {0x000D0082, nullptr, "WriteProcessPipe"},
25 {0x001000C0, nullptr, "ReadPipeIfPossible"}, 165 {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
26 {0x001100C2, nullptr, "LoadComponent"}, 166 {0x001100C2, LoadComponent, "LoadComponent"},
27 {0x00120000, nullptr, "UnloadComponent"}, 167 {0x00120000, nullptr, "UnloadComponent"},
28 {0x00130082, nullptr, "FlushDataCache"}, 168 {0x00130082, nullptr, "FlushDataCache"},
29 {0x00140082, nullptr, "InvalidateDCache "}, 169 {0x00140082, nullptr, "InvalidateDCache"},
30 {0x00150082, nullptr, "RegisterInterruptEvents"}, 170 {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"},
31 {0x00160000, nullptr, "GetSemaphoreEventHandle"}, 171 {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"},
32 {0x00170040, nullptr, "SetSemaphoreMask"}, 172 {0x00170040, nullptr, "SetSemaphoreMask"},
33 {0x00180040, nullptr, "GetPhysicalAddress"}, 173 {0x00180040, nullptr, "GetPhysicalAddress"},
34 {0x00190040, nullptr, "GetVirtualAddress" }, 174 {0x00190040, nullptr, "GetVirtualAddress"},
35 {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, 175 {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
36 {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, 176 {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
37 {0x001C0082, nullptr, "SetIirFilterEQ"}, 177 {0x001C0082, nullptr, "SetIirFilterEQ"},
38 {0x001F0000, nullptr, "GetHeadphoneStatus"}, 178 {0x001F0000, nullptr, "GetHeadphoneStatus"},
39 {0x00210000, nullptr, "GetIsDspOccupied"}, 179 {0x00210000, nullptr, "GetIsDspOccupied"},
40}; 180};
41 181
42//////////////////////////////////////////////////////////////////////////////////////////////////// 182////////////////////////////////////////////////////////////////////////////////////////////////////
43// Interface class 183// Interface class
44 184
45Interface::Interface() { 185Interface::Interface() {
186 semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
187 interrupt_event = 0;
188 read_pipe_count = 0;
189
46 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 190 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
47} 191}
48 192
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index c439ed266..9431b62f6 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -19,8 +19,8 @@ public:
19 * Gets the string port name used by CTROS for the service 19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service 20 * @return Port name of service
21 */ 21 */
22 std::string GetPortName() const { 22 std::string GetPortName() const override {
23 return "dsp:DSP"; 23 return "dsp::DSP";
24 } 24 }
25}; 25};
26 26
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index 917b2f8ca..785c351e9 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -20,8 +20,8 @@ namespace ERR_F {
20 Interface::Interface() { 20 Interface::Interface() {
21 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 21 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
22 } 22 }
23 23
24 Interface::~Interface() { 24 Interface::~Interface() {
25 } 25 }
26 26
27} // namespace 27} // namespace
diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h
index 5da663267..6d7141c1b 100644
--- a/src/core/hle/service/err_f.h
+++ b/src/core/hle/service/err_f.h
@@ -19,9 +19,9 @@ namespace ERR_F {
19 * Gets the string port name used by CTROS for the service 19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service 20 * @return Port name of service
21 */ 21 */
22 std::string GetPortName() const { 22 std::string GetPortName() const override {
23 return "err:f"; 23 return "err:f";
24 } 24 }
25 }; 25 };
26 26
27} // namespace 27} // namespace
diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp
new file mode 100644
index 000000000..58023e536
--- /dev/null
+++ b/src/core/hle/service/frd_u.cpp
@@ -0,0 +1,35 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/frd_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace FRD_U
11
12namespace FRD_U {
13
14 const Interface::FunctionInfo FunctionTable[] = {
15 {0x00050000, nullptr, "GetFriendKey"},
16 {0x00080000, nullptr, "GetMyPresence"},
17 {0x00100040, nullptr, "GetPassword"},
18 {0x00190042, nullptr, "GetFriendFavoriteGame"},
19 {0x001A00C4, nullptr, "GetFriendInfo"},
20 {0x001B0080, nullptr, "IsOnFriendList"},
21 {0x001C0042, nullptr, "DecodeLocalFriendCode"},
22 {0x001D0002, nullptr, "SetCurrentlyPlayingText"},
23 {0x00320042, nullptr, "SetClientSdkVersion"}
24 };
25 ////////////////////////////////////////////////////////////////////////////////////////////////////
26 // Interface class
27
28 Interface::Interface() {
29 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
30 }
31
32 Interface::~Interface() {
33 }
34
35} // namespace
diff --git a/src/core/hle/service/frd_u.h b/src/core/hle/service/frd_u.h
new file mode 100644
index 000000000..4020c6664
--- /dev/null
+++ b/src/core/hle/service/frd_u.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace FRD_U
11
12namespace FRD_U {
13
14 class Interface : public Service::Interface {
15 public:
16 Interface();
17 ~Interface();
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "frd:u";
24 }
25 };
26
27} // namespace
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
new file mode 100644
index 000000000..9c3834733
--- /dev/null
+++ b/src/core/hle/service/fs/archive.cpp
@@ -0,0 +1,431 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <unordered_map>
7
8#include "common/common_types.h"
9#include "common/file_util.h"
10#include "common/math_util.h"
11
12#include "core/file_sys/archive_savedata.h"
13#include "core/file_sys/archive_backend.h"
14#include "core/file_sys/archive_sdmc.h"
15#include "core/file_sys/directory_backend.h"
16#include "core/hle/service/fs/archive.h"
17#include "core/hle/kernel/session.h"
18#include "core/hle/result.h"
19
20// Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map.
21// Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
22namespace std {
23 template <>
24 struct hash<Service::FS::ArchiveIdCode> {
25 typedef Service::FS::ArchiveIdCode argument_type;
26 typedef std::size_t result_type;
27
28 result_type operator()(const argument_type& id_code) const {
29 typedef std::underlying_type<argument_type>::type Type;
30 return std::hash<Type>()(static_cast<Type>(id_code));
31 }
32 };
33}
34
35namespace Service {
36namespace FS {
37
38// Command to access archive file
39enum class FileCommand : u32 {
40 Dummy1 = 0x000100C6,
41 Control = 0x040100C4,
42 OpenSubFile = 0x08010100,
43 Read = 0x080200C2,
44 Write = 0x08030102,
45 GetSize = 0x08040000,
46 SetSize = 0x08050080,
47 GetAttributes = 0x08060000,
48 SetAttributes = 0x08070040,
49 Close = 0x08080000,
50 Flush = 0x08090000,
51};
52
53// Command to access directory
54enum class DirectoryCommand : u32 {
55 Dummy1 = 0x000100C6,
56 Control = 0x040100C4,
57 Read = 0x08010042,
58 Close = 0x08020000,
59};
60
61class Archive {
62public:
63 Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code)
64 : backend(std::move(backend)), id_code(id_code) {
65 }
66
67 std::string GetName() const { return "Archive: " + backend->GetName(); }
68
69 ArchiveIdCode id_code; ///< Id code of the archive
70 std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface
71};
72
73class File : public Kernel::Session {
74public:
75 File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path)
76 : backend(std::move(backend)), path(path) {
77 }
78
79 std::string GetName() const override { return "Path: " + path.DebugStr(); }
80
81 FileSys::Path path; ///< Path of the file
82 std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface
83
84 ResultVal<bool> SyncRequest() override {
85 u32* cmd_buff = Kernel::GetCommandBuffer();
86 FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
87 switch (cmd) {
88
89 // Read from file...
90 case FileCommand::Read:
91 {
92 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
93 u32 length = cmd_buff[3];
94 u32 address = cmd_buff[5];
95 LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
96 GetTypeName().c_str(), GetName().c_str(), offset, length, address);
97 cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
98 break;
99 }
100
101 // Write to file...
102 case FileCommand::Write:
103 {
104 u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
105 u32 length = cmd_buff[3];
106 u32 flush = cmd_buff[4];
107 u32 address = cmd_buff[6];
108 LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
109 GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
110 cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
111 break;
112 }
113
114 case FileCommand::GetSize:
115 {
116 LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
117 u64 size = backend->GetSize();
118 cmd_buff[2] = (u32)size;
119 cmd_buff[3] = size >> 32;
120 break;
121 }
122
123 case FileCommand::SetSize:
124 {
125 u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
126 LOG_TRACE(Service_FS, "SetSize %s %s size=%llu",
127 GetTypeName().c_str(), GetName().c_str(), size);
128 backend->SetSize(size);
129 break;
130 }
131
132 case FileCommand::Close:
133 {
134 LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
135 Kernel::g_object_pool.Destroy<File>(GetHandle());
136 break;
137 }
138
139 case FileCommand::Flush:
140 {
141 LOG_TRACE(Service_FS, "Flush");
142 backend->Flush();
143 break;
144 }
145
146 // Unknown command...
147 default:
148 LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
149 ResultCode error = UnimplementedFunction(ErrorModule::FS);
150 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
151 return error;
152 }
153 cmd_buff[1] = 0; // No error
154 return MakeResult<bool>(false);
155 }
156};
157
158class Directory : public Kernel::Session {
159public:
160 Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path)
161 : backend(std::move(backend)), path(path) {
162 }
163
164 std::string GetName() const override { return "Directory: " + path.DebugStr(); }
165
166 FileSys::Path path; ///< Path of the directory
167 std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface
168
169 ResultVal<bool> SyncRequest() override {
170 u32* cmd_buff = Kernel::GetCommandBuffer();
171 DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
172 switch (cmd) {
173
174 // Read from directory...
175 case DirectoryCommand::Read:
176 {
177 u32 count = cmd_buff[1];
178 u32 address = cmd_buff[3];
179 auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
180 LOG_TRACE(Service_FS, "Read %s %s: count=%d",
181 GetTypeName().c_str(), GetName().c_str(), count);
182
183 // Number of entries actually read
184 cmd_buff[2] = backend->Read(count, entries);
185 break;
186 }
187
188 case DirectoryCommand::Close:
189 {
190 LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
191 Kernel::g_object_pool.Destroy<Directory>(GetHandle());
192 break;
193 }
194
195 // Unknown command...
196 default:
197 LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
198 ResultCode error = UnimplementedFunction(ErrorModule::FS);
199 cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
200 return MakeResult<bool>(false);
201 }
202 cmd_buff[1] = 0; // No error
203 return MakeResult<bool>(false);
204 }
205};
206
207////////////////////////////////////////////////////////////////////////////////////////////////////
208
209/**
210 * Map of registered archives, identified by id code. Once an archive is registered here, it is
211 * never removed until the FS service is shut down.
212 */
213static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map;
214
215/**
216 * Map of active archive handles. Values are pointers to the archives in `idcode_map`.
217 */
218static std::unordered_map<ArchiveHandle, Archive*> handle_map;
219static ArchiveHandle next_handle;
220
221static Archive* GetArchive(ArchiveHandle handle) {
222 auto itr = handle_map.find(handle);
223 return (itr == handle_map.end()) ? nullptr : itr->second;
224}
225
226ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
227 LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code);
228
229 auto itr = id_code_map.find(id_code);
230 if (itr == id_code_map.end()) {
231 if (id_code == ArchiveIdCode::SaveData) {
232 // When a SaveData archive is created for the first time, it is not yet formatted
233 // and the save file/directory structure expected by the game has not yet been initialized.
234 // Returning the NotFormatted error code will signal the game to provision the SaveData archive
235 // with the files and folders that it expects.
236 // The FormatSaveData service call will create the SaveData archive when it is called.
237 return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
238 ErrorSummary::InvalidState, ErrorLevel::Status);
239 }
240 // TODO: Verify error against hardware
241 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
242 ErrorSummary::NotFound, ErrorLevel::Permanent);
243 }
244
245 // This should never even happen in the first place with 64-bit handles,
246 while (handle_map.count(next_handle) != 0) {
247 ++next_handle;
248 }
249 handle_map.emplace(next_handle, itr->second.get());
250 return MakeResult<ArchiveHandle>(next_handle++);
251}
252
253ResultCode CloseArchive(ArchiveHandle handle) {
254 if (handle_map.erase(handle) == 0)
255 return InvalidHandle(ErrorModule::FS);
256 else
257 return RESULT_SUCCESS;
258}
259
260// TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in
261// http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22
262ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) {
263 auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code));
264
265 bool inserted = result.second;
266 _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code");
267
268 auto& archive = result.first->second;
269 LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code);
270 return RESULT_SUCCESS;
271}
272
273ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
274 Archive* archive = GetArchive(archive_handle);
275 if (archive == nullptr)
276 return InvalidHandle(ErrorModule::FS);
277
278 std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
279 if (backend == nullptr) {
280 return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
281 ErrorSummary::NotFound, ErrorLevel::Status);
282 }
283
284 auto file = std::make_unique<File>(std::move(backend), path);
285 Handle handle = Kernel::g_object_pool.Create(file.release());
286 return MakeResult<Handle>(handle);
287}
288
289ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
290 Archive* archive = GetArchive(archive_handle);
291 if (archive == nullptr)
292 return InvalidHandle(ErrorModule::FS);
293
294 if (archive->backend->DeleteFile(path))
295 return RESULT_SUCCESS;
296 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
297 ErrorSummary::Canceled, ErrorLevel::Status);
298}
299
300ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
301 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
302 Archive* src_archive = GetArchive(src_archive_handle);
303 Archive* dest_archive = GetArchive(dest_archive_handle);
304 if (src_archive == nullptr || dest_archive == nullptr)
305 return InvalidHandle(ErrorModule::FS);
306
307 if (src_archive == dest_archive) {
308 if (src_archive->backend->RenameFile(src_path, dest_path))
309 return RESULT_SUCCESS;
310 } else {
311 // TODO: Implement renaming across archives
312 return UnimplementedFunction(ErrorModule::FS);
313 }
314
315 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
316 // exist or similar. Verify.
317 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
318 ErrorSummary::NothingHappened, ErrorLevel::Status);
319}
320
321ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
322 Archive* archive = GetArchive(archive_handle);
323 if (archive == nullptr)
324 return InvalidHandle(ErrorModule::FS);
325
326 if (archive->backend->DeleteDirectory(path))
327 return RESULT_SUCCESS;
328 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
329 ErrorSummary::Canceled, ErrorLevel::Status);
330}
331
332ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
333 Archive* archive = GetArchive(archive_handle);
334 if (archive == nullptr)
335 return InvalidHandle(ErrorModule::FS);
336
337 if (archive->backend->CreateDirectory(path))
338 return RESULT_SUCCESS;
339 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
340 ErrorSummary::Canceled, ErrorLevel::Status);
341}
342
343ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
344 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
345 Archive* src_archive = GetArchive(src_archive_handle);
346 Archive* dest_archive = GetArchive(dest_archive_handle);
347 if (src_archive == nullptr || dest_archive == nullptr)
348 return InvalidHandle(ErrorModule::FS);
349
350 if (src_archive == dest_archive) {
351 if (src_archive->backend->RenameDirectory(src_path, dest_path))
352 return RESULT_SUCCESS;
353 } else {
354 // TODO: Implement renaming across archives
355 return UnimplementedFunction(ErrorModule::FS);
356 }
357
358 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
359 // exist or similar. Verify.
360 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
361 ErrorSummary::NothingHappened, ErrorLevel::Status);
362}
363
364/**
365 * Open a Directory from an Archive
366 * @param archive_handle Handle to an open Archive object
367 * @param path Path to the Directory inside of the Archive
368 * @return Opened Directory object
369 */
370ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
371 Archive* archive = GetArchive(archive_handle);
372 if (archive == nullptr)
373 return InvalidHandle(ErrorModule::FS);
374
375 std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path);
376 if (backend == nullptr) {
377 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
378 ErrorSummary::NotFound, ErrorLevel::Permanent);
379 }
380
381 auto directory = std::make_unique<Directory>(std::move(backend), path);
382 Handle handle = Kernel::g_object_pool.Create(directory.release());
383 return MakeResult<Handle>(handle);
384}
385
386ResultCode FormatSaveData() {
387 // TODO(Subv): Actually wipe the savedata folder after creating or opening it
388
389 // Do not create the archive again if it already exists
390 if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
391 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
392
393 // Create the SaveData archive
394 std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
395 auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
396 Kernel::g_program_id);
397
398 if (savedata_archive->Initialize()) {
399 CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
400 return RESULT_SUCCESS;
401 } else {
402 LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
403 savedata_archive->GetMountPoint().c_str());
404 return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
405 }
406}
407
408/// Initialize archives
409void ArchiveInit() {
410 next_handle = 1;
411
412 // TODO(Link Mauve): Add the other archive types (see here for the known types:
413 // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
414 // archive type is SDMC, so it is the only one getting exposed.
415
416 std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
417 auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
418 if (sdmc_archive->Initialize())
419 CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
420 else
421 LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
422}
423
424/// Shutdown archives
425void ArchiveShutdown() {
426 handle_map.clear();
427 id_code_map.clear();
428}
429
430} // namespace FS
431} // 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 000000000..a128276b6
--- /dev/null
+++ b/src/core/hle/service/fs/archive.h
@@ -0,0 +1,125 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/file_sys/archive_backend.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/result.h"
12
13namespace Service {
14namespace FS {
15
16/// Supported archive types
17enum class ArchiveIdCode : u32 {
18 RomFS = 0x00000003,
19 SaveData = 0x00000004,
20 ExtSaveData = 0x00000006,
21 SharedExtSaveData = 0x00000007,
22 SystemSaveData = 0x00000008,
23 SDMC = 0x00000009,
24 SDMCWriteOnly = 0x0000000A,
25};
26
27typedef u64 ArchiveHandle;
28
29/**
30 * Opens an archive
31 * @param id_code IdCode of the archive to open
32 * @return Handle to the opened archive
33 */
34ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code);
35
36/**
37 * Closes an archive
38 * @param id_code IdCode of the archive to open
39 */
40ResultCode CloseArchive(ArchiveHandle handle);
41
42/**
43 * Creates an Archive
44 * @param backend File system backend interface to the archive
45 * @param id_code Id code used to access this type of archive
46 */
47ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code);
48
49/**
50 * Open a File from an Archive
51 * @param archive_handle Handle to an open Archive object
52 * @param path Path to the File inside of the Archive
53 * @param mode Mode under which to open the File
54 * @return Handle to the opened File object
55 */
56ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
57
58/**
59 * Delete a File from an Archive
60 * @param archive_handle Handle to an open Archive object
61 * @param path Path to the File inside of the Archive
62 * @return Whether deletion succeeded
63 */
64ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
65
66/**
67 * Rename a File between two Archives
68 * @param src_archive_handle Handle to the source Archive object
69 * @param src_path Path to the File inside of the source Archive
70 * @param dest_archive_handle Handle to the destination Archive object
71 * @param dest_path Path to the File inside of the destination Archive
72 * @return Whether rename succeeded
73 */
74ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
75 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
76
77/**
78 * Delete a Directory from an Archive
79 * @param archive_handle Handle to an open Archive object
80 * @param path Path to the Directory inside of the Archive
81 * @return Whether deletion succeeded
82 */
83ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
84
85/**
86 * Create a Directory from an Archive
87 * @param archive_handle Handle to an open Archive object
88 * @param path Path to the Directory inside of the Archive
89 * @return Whether creation of directory succeeded
90 */
91ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
92
93/**
94 * Rename a Directory between two Archives
95 * @param src_archive_handle Handle to the source Archive object
96 * @param src_path Path to the Directory inside of the source Archive
97 * @param dest_archive_handle Handle to the destination Archive object
98 * @param dest_path Path to the Directory inside of the destination Archive
99 * @return Whether rename succeeded
100 */
101ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
102 ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path);
103
104/**
105 * Open a Directory from an Archive
106 * @param archive_handle Handle to an open Archive object
107 * @param path Path to the Directory inside of the Archive
108 * @return Handle to the opened File object
109 */
110ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
111
112/**
113 * Creates a blank SaveData archive.
114 * @return ResultCode 0 on success or the corresponding code on error
115 */
116ResultCode FormatSaveData();
117
118/// Initialize archives
119void ArchiveInit();
120
121/// Shutdown archives
122void ArchiveShutdown();
123
124} // namespace FS
125} // 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 000000000..f99d84b2f
--- /dev/null
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -0,0 +1,529 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common.h"
6#include "common/file_util.h"
7#include "common/scope_exit.h"
8#include "common/string_util.h"
9#include "core/hle/result.h"
10#include "core/hle/service/fs/archive.h"
11#include "core/hle/service/fs/fs_user.h"
12#include "core/settings.h"
13
14////////////////////////////////////////////////////////////////////////////////////////////////////
15// Namespace FS_User
16
17namespace Service {
18namespace FS {
19
20static ArchiveHandle MakeArchiveHandle(u32 low_word, u32 high_word) {
21 return (u64)low_word | ((u64)high_word << 32);
22}
23
24static void Initialize(Service::Interface* self) {
25 u32* cmd_buff = Kernel::GetCommandBuffer();
26
27 // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
28 // http://3dbrew.org/wiki/FS:Initialize#Request
29 cmd_buff[1] = RESULT_SUCCESS.raw;
30
31 LOG_DEBUG(Service_FS, "called");
32}
33
34/**
35 * FS_User::OpenFile service function
36 * Inputs:
37 * 1 : Transaction
38 * 2 : Archive handle lower word
39 * 3 : Archive handle upper word
40 * 4 : Low path type
41 * 5 : Low path size
42 * 6 : Open flags
43 * 7 : Attributes
44 * 8 : (LowPathSize << 14) | 2
45 * 9 : Low path data pointer
46 * Outputs:
47 * 1 : Result of function, 0 on success, otherwise error code
48 * 3 : File handle
49 */
50static void OpenFile(Service::Interface* self) {
51 u32* cmd_buff = Kernel::GetCommandBuffer();
52
53 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
54 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
55 u32 filename_size = cmd_buff[5];
56 FileSys::Mode mode; mode.hex = cmd_buff[6];
57 u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
58 u32 filename_ptr = cmd_buff[9];
59 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
60
61 LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes);
62
63 ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode);
64 cmd_buff[1] = handle.Code().raw;
65 if (handle.Succeeded()) {
66 cmd_buff[3] = *handle;
67 } else {
68 cmd_buff[3] = 0;
69 LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
70 }
71}
72
73/**
74 * FS_User::OpenFileDirectly service function
75 * Inputs:
76 * 1 : Transaction
77 * 2 : Archive ID
78 * 3 : Archive low path type
79 * 4 : Archive low path size
80 * 5 : File low path type
81 * 6 : File low path size
82 * 7 : Flags
83 * 8 : Attributes
84 * 9 : (ArchiveLowPathSize << 14) | 0x802
85 * 10 : Archive low path
86 * 11 : (FileLowPathSize << 14) | 2
87 * 12 : File low path
88 * Outputs:
89 * 1 : Result of function, 0 on success, otherwise error code
90 * 3 : File handle
91 */
92static void OpenFileDirectly(Service::Interface* self) {
93 u32* cmd_buff = Kernel::GetCommandBuffer();
94
95 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[2]);
96 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
97 u32 archivename_size = cmd_buff[4];
98 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[5]);
99 u32 filename_size = cmd_buff[6];
100 FileSys::Mode mode; mode.hex = cmd_buff[7];
101 u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
102 u32 archivename_ptr = cmd_buff[10];
103 u32 filename_ptr = cmd_buff[12];
104 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
105 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
106
107 LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d",
108 archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
109
110 if (archive_path.GetType() != FileSys::Empty) {
111 LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
112 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
113 cmd_buff[3] = 0;
114 return;
115 }
116
117 ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id);
118 if (archive_handle.Failed()) {
119 LOG_ERROR(Service_FS, "failed to get a handle for archive");
120 cmd_buff[1] = archive_handle.Code().raw;
121 cmd_buff[3] = 0;
122 return;
123 }
124 SCOPE_EXIT({ CloseArchive(*archive_handle); });
125
126 ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode);
127 cmd_buff[1] = handle.Code().raw;
128 if (handle.Succeeded()) {
129 cmd_buff[3] = *handle;
130 } else {
131 cmd_buff[3] = 0;
132 LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str());
133 }
134}
135
136/*
137 * FS_User::DeleteFile service function
138 * Inputs:
139 * 2 : Archive handle lower word
140 * 3 : Archive handle upper word
141 * 4 : File path string type
142 * 5 : File path string size
143 * 7 : File path string data
144 * Outputs:
145 * 1 : Result of function, 0 on success, otherwise error code
146 */
147static void DeleteFile(Service::Interface* self) {
148 u32* cmd_buff = Kernel::GetCommandBuffer();
149
150 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
151 auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
152 u32 filename_size = cmd_buff[5];
153 u32 filename_ptr = cmd_buff[7];
154
155 FileSys::Path file_path(filename_type, filename_size, filename_ptr);
156
157 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
158 filename_type, filename_size, file_path.DebugStr().c_str());
159
160 cmd_buff[1] = DeleteFileFromArchive(archive_handle, file_path).raw;
161}
162
163/*
164 * FS_User::RenameFile service function
165 * Inputs:
166 * 2 : Source archive handle lower word
167 * 3 : Source archive handle upper word
168 * 4 : Source file path type
169 * 5 : Source file path size
170 * 6 : Dest archive handle lower word
171 * 7 : Dest archive handle upper word
172 * 8 : Dest file path type
173 * 9 : Dest file path size
174 * 11: Source file path string data
175 * 13: Dest file path string
176 * Outputs:
177 * 1 : Result of function, 0 on success, otherwise error code
178 */
179static void RenameFile(Service::Interface* self) {
180 u32* cmd_buff = Kernel::GetCommandBuffer();
181
182 ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
183 auto src_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
184 u32 src_filename_size = cmd_buff[5];
185 ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);;
186 auto dest_filename_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
187 u32 dest_filename_size = cmd_buff[9];
188 u32 src_filename_ptr = cmd_buff[11];
189 u32 dest_filename_ptr = cmd_buff[13];
190
191 FileSys::Path src_file_path(src_filename_type, src_filename_size, src_filename_ptr);
192 FileSys::Path dest_file_path(dest_filename_type, dest_filename_size, dest_filename_ptr);
193
194 LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
195 src_filename_type, src_filename_size, src_file_path.DebugStr().c_str(),
196 dest_filename_type, dest_filename_size, dest_file_path.DebugStr().c_str());
197
198 cmd_buff[1] = RenameFileBetweenArchives(src_archive_handle, src_file_path, dest_archive_handle, dest_file_path).raw;
199}
200
201/*
202 * FS_User::DeleteDirectory service function
203 * Inputs:
204 * 2 : Archive handle lower word
205 * 3 : Archive handle upper word
206 * 4 : Directory path string type
207 * 5 : Directory path string size
208 * 7 : Directory path string data
209 * Outputs:
210 * 1 : Result of function, 0 on success, otherwise error code
211 */
212static void DeleteDirectory(Service::Interface* self) {
213 u32* cmd_buff = Kernel::GetCommandBuffer();
214
215 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
216 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
217 u32 dirname_size = cmd_buff[5];
218 u32 dirname_ptr = cmd_buff[7];
219
220 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
221
222 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s",
223 dirname_type, dirname_size, dir_path.DebugStr().c_str());
224
225 cmd_buff[1] = DeleteDirectoryFromArchive(archive_handle, dir_path).raw;
226}
227
228/*
229 * FS_User::CreateDirectory service function
230 * Inputs:
231 * 2 : Archive handle lower word
232 * 3 : Archive handle upper word
233 * 4 : Directory path string type
234 * 5 : Directory path string size
235 * 8 : Directory path string data
236 * Outputs:
237 * 1 : Result of function, 0 on success, otherwise error code
238 */
239static void CreateDirectory(Service::Interface* self) {
240 u32* cmd_buff = Kernel::GetCommandBuffer();
241
242 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
243 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
244 u32 dirname_size = cmd_buff[5];
245 u32 dirname_ptr = cmd_buff[8];
246
247 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
248
249 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
250
251 cmd_buff[1] = CreateDirectoryFromArchive(archive_handle, dir_path).raw;
252}
253
254/*
255 * FS_User::RenameDirectory service function
256 * Inputs:
257 * 2 : Source archive handle lower word
258 * 3 : Source archive handle upper word
259 * 4 : Source dir path type
260 * 5 : Source dir path size
261 * 6 : Dest archive handle lower word
262 * 7 : Dest archive handle upper word
263 * 8 : Dest dir path type
264 * 9 : Dest dir path size
265 * 11: Source dir path string data
266 * 13: Dest dir path string
267 * Outputs:
268 * 1 : Result of function, 0 on success, otherwise error code
269 */
270static void RenameDirectory(Service::Interface* self) {
271 u32* cmd_buff = Kernel::GetCommandBuffer();
272
273 ArchiveHandle src_archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
274 auto src_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[4]);
275 u32 src_dirname_size = cmd_buff[5];
276 ArchiveHandle dest_archive_handle = MakeArchiveHandle(cmd_buff[6], cmd_buff[7]);
277 auto dest_dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[8]);
278 u32 dest_dirname_size = cmd_buff[9];
279 u32 src_dirname_ptr = cmd_buff[11];
280 u32 dest_dirname_ptr = cmd_buff[13];
281
282 FileSys::Path src_dir_path(src_dirname_type, src_dirname_size, src_dirname_ptr);
283 FileSys::Path dest_dir_path(dest_dirname_type, dest_dirname_size, dest_dirname_ptr);
284
285 LOG_DEBUG(Service_FS, "src_type=%d src_size=%d src_data=%s dest_type=%d dest_size=%d dest_data=%s",
286 src_dirname_type, src_dirname_size, src_dir_path.DebugStr().c_str(),
287 dest_dirname_type, dest_dirname_size, dest_dir_path.DebugStr().c_str());
288
289 cmd_buff[1] = RenameDirectoryBetweenArchives(src_archive_handle, src_dir_path, dest_archive_handle, dest_dir_path).raw;
290}
291
292/**
293 * FS_User::OpenDirectory service function
294 * Inputs:
295 * 1 : Archive handle low word
296 * 2 : Archive handle high word
297 * 3 : Low path type
298 * 4 : Low path size
299 * 7 : (LowPathSize << 14) | 2
300 * 8 : Low path data pointer
301 * Outputs:
302 * 1 : Result of function, 0 on success, otherwise error code
303 * 3 : Directory handle
304 */
305static void OpenDirectory(Service::Interface* self) {
306 u32* cmd_buff = Kernel::GetCommandBuffer();
307
308 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
309 auto dirname_type = static_cast<FileSys::LowPathType>(cmd_buff[3]);
310 u32 dirname_size = cmd_buff[4];
311 u32 dirname_ptr = cmd_buff[6];
312
313 FileSys::Path dir_path(dirname_type, dirname_size, dirname_ptr);
314
315 LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str());
316
317 ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path);
318 cmd_buff[1] = handle.Code().raw;
319 if (handle.Succeeded()) {
320 cmd_buff[3] = *handle;
321 } else {
322 LOG_ERROR(Service_FS, "failed to get a handle for directory");
323 }
324}
325
326/**
327 * FS_User::OpenArchive service function
328 * Inputs:
329 * 1 : Archive ID
330 * 2 : Archive low path type
331 * 3 : Archive low path size
332 * 4 : (LowPathSize << 14) | 2
333 * 5 : Archive low path
334 * Outputs:
335 * 1 : Result of function, 0 on success, otherwise error code
336 * 2 : Archive handle lower word (unused)
337 * 3 : Archive handle upper word (same as file handle)
338 */
339static void OpenArchive(Service::Interface* self) {
340 u32* cmd_buff = Kernel::GetCommandBuffer();
341
342 auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
343 auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
344 u32 archivename_size = cmd_buff[3];
345 u32 archivename_ptr = cmd_buff[5];
346 FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
347
348 LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
349
350 if (archive_path.GetType() != FileSys::Empty) {
351 LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported");
352 cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
353 return;
354 }
355
356 ResultVal<ArchiveHandle> handle = OpenArchive(archive_id);
357 cmd_buff[1] = handle.Code().raw;
358 if (handle.Succeeded()) {
359 cmd_buff[2] = *handle & 0xFFFFFFFF;
360 cmd_buff[3] = (*handle >> 32) & 0xFFFFFFFF;
361 } else {
362 cmd_buff[2] = cmd_buff[3] = 0;
363 LOG_ERROR(Service_FS, "failed to get a handle for archive");
364 }
365}
366
367/**
368 * FS_User::CloseArchive service function
369 * Inputs:
370 * 0 : 0x080E0080
371 * 1 : Archive handle low word
372 * 2 : Archive handle high word
373 * Outputs:
374 * 0 : ??? TODO(yuriks): Verify return header
375 * 1 : Result of function, 0 on success, otherwise error code
376 */
377static void CloseArchive(Service::Interface* self) {
378 u32* cmd_buff = Kernel::GetCommandBuffer();
379
380 ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
381 cmd_buff[1] = CloseArchive(archive_handle).raw;
382}
383
384/*
385* FS_User::IsSdmcDetected service function
386* Outputs:
387* 1 : Result of function, 0 on success, otherwise error code
388* 2 : Whether the Sdmc could be detected
389*/
390static void IsSdmcDetected(Service::Interface* self) {
391 u32* cmd_buff = Kernel::GetCommandBuffer();
392
393 cmd_buff[1] = 0;
394 cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
395
396 LOG_DEBUG(Service_FS, "called");
397}
398
399/**
400 * FS_User::FormatSaveData service function
401 * Inputs:
402 * Outputs:
403 * 1 : Result of function, 0 on success, otherwise error code
404 */
405static void FormatSaveData(Service::Interface* self) {
406 u32* cmd_buff = Kernel::GetCommandBuffer();
407 LOG_DEBUG(Service_FS, "(STUBBED)");
408
409 // TODO(Subv): Find out what the inputs and outputs of this function are
410
411 cmd_buff[1] = FormatSaveData().raw;
412}
413
414/**
415 * FS_User::FormatThisUserSaveData service function
416 * Inputs:
417 * Outputs:
418 * 1 : Result of function, 0 on success, otherwise error code
419 */
420static void FormatThisUserSaveData(Service::Interface* self) {
421 u32* cmd_buff = Kernel::GetCommandBuffer();
422 LOG_DEBUG(Service_FS, "(STUBBED)");
423
424 // TODO(Subv): Find out what the inputs and outputs of this function are
425
426 cmd_buff[1] = FormatSaveData().raw;
427}
428
429const FSUserInterface::FunctionInfo FunctionTable[] = {
430 {0x000100C6, nullptr, "Dummy1"},
431 {0x040100C4, nullptr, "Control"},
432 {0x08010002, Initialize, "Initialize"},
433 {0x080201C2, OpenFile, "OpenFile"},
434 {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
435 {0x08040142, DeleteFile, "DeleteFile"},
436 {0x08050244, RenameFile, "RenameFile"},
437 {0x08060142, DeleteDirectory, "DeleteDirectory"},
438 {0x08070142, nullptr, "DeleteDirectoryRecursively"},
439 {0x08080202, nullptr, "CreateFile"},
440 {0x08090182, CreateDirectory, "CreateDirectory"},
441 {0x080A0244, RenameDirectory, "RenameDirectory"},
442 {0x080B0102, OpenDirectory, "OpenDirectory"},
443 {0x080C00C2, OpenArchive, "OpenArchive"},
444 {0x080D0144, nullptr, "ControlArchive"},
445 {0x080E0080, CloseArchive, "CloseArchive"},
446 {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
447 {0x08100200, nullptr, "CreateSystemSaveData"},
448 {0x08110040, nullptr, "DeleteSystemSaveData"},
449 {0x08120080, nullptr, "GetFreeBytes"},
450 {0x08130000, nullptr, "GetCardType"},
451 {0x08140000, nullptr, "GetSdmcArchiveResource"},
452 {0x08150000, nullptr, "GetNandArchiveResource"},
453 {0x08160000, nullptr, "GetSdmcFatfsErro"},
454 {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
455 {0x08180000, nullptr, "IsSdmcWritable"},
456 {0x08190042, nullptr, "GetSdmcCid"},
457 {0x081A0042, nullptr, "GetNandCid"},
458 {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
459 {0x081C0000, nullptr, "GetNandSpeedInfo"},
460 {0x081D0042, nullptr, "GetSdmcLog"},
461 {0x081E0042, nullptr, "GetNandLog"},
462 {0x081F0000, nullptr, "ClearSdmcLog"},
463 {0x08200000, nullptr, "ClearNandLog"},
464 {0x08210000, nullptr, "CardSlotIsInserted"},
465 {0x08220000, nullptr, "CardSlotPowerOn"},
466 {0x08230000, nullptr, "CardSlotPowerOff"},
467 {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
468 {0x08250040, nullptr, "CardNorDirectCommand"},
469 {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
470 {0x08270082, nullptr, "CardNorDirectRead"},
471 {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
472 {0x08290082, nullptr, "CardNorDirectWrite"},
473 {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
474 {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
475 {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
476 {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
477 {0x082E0040, nullptr, "GetProductInfo"},
478 {0x082F0040, nullptr, "GetProgramLaunchInfo"},
479 {0x08300182, nullptr, "CreateExtSaveData"},
480 {0x08310180, nullptr, "CreateSharedExtSaveData"},
481 {0x08320102, nullptr, "ReadExtSaveDataIcon"},
482 {0x08330082, nullptr, "EnumerateExtSaveData"},
483 {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
484 {0x08350080, nullptr, "DeleteExtSaveData"},
485 {0x08360080, nullptr, "DeleteSharedExtSaveData"},
486 {0x08370040, nullptr, "SetCardSpiBaudRate"},
487 {0x08380040, nullptr, "SetCardSpiBusMode"},
488 {0x08390000, nullptr, "SendInitializeInfoTo9"},
489 {0x083A0100, nullptr, "GetSpecialContentIndex"},
490 {0x083B00C2, nullptr, "GetLegacyRomHeader"},
491 {0x083C00C2, nullptr, "GetLegacyBannerData"},
492 {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
493 {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
494 {0x083F00C0, nullptr, "GetExtDataBlockSize"},
495 {0x08400040, nullptr, "AbnegateAccessRight"},
496 {0x08410000, nullptr, "DeleteSdmcRoot"},
497 {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
498 {0x08430000, nullptr, "InitializeCtrFileSystem"},
499 {0x08440000, nullptr, "CreateSeed"},
500 {0x084500C2, nullptr, "GetFormatInfo"},
501 {0x08460102, nullptr, "GetLegacyRomHeader2"},
502 {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
503 {0x08480042, nullptr, "GetSdmcCtrRootPath"},
504 {0x08490040, nullptr, "GetArchiveResource"},
505 {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
506 {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
507 {0x084C0242, FormatSaveData, "FormatSaveData"},
508 {0x084D0102, nullptr, "GetLegacySubBannerData"},
509 {0x084E0342, nullptr, "UpdateSha256Context"},
510 {0x084F0102, nullptr, "ReadSpecialFile"},
511 {0x08500040, nullptr, "GetSpecialFileSize"},
512 {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
513 {0x08610042, nullptr, "InitializeWithSdkVersion"},
514 {0x08620040, nullptr, "SetPriority"},
515 {0x08630000, nullptr, "GetPriority"},
516};
517
518////////////////////////////////////////////////////////////////////////////////////////////////////
519// Interface class
520
521FSUserInterface::FSUserInterface() {
522 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
523}
524
525FSUserInterface::~FSUserInterface() {
526}
527
528} // namespace FS
529} // namespace Service
diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h
new file mode 100644
index 000000000..80e3804e0
--- /dev/null
+++ b/src/core/hle/service/fs/fs_user.h
@@ -0,0 +1,33 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace FS_User
11
12namespace Service {
13namespace FS {
14
15/// Interface to "fs:USER" service
16class FSUserInterface : public Service::Interface {
17public:
18
19 FSUserInterface();
20
21 ~FSUserInterface();
22
23 /**
24 * Gets the string port name used by CTROS for the service
25 * @return Port name of service
26 */
27 std::string GetPortName() const override {
28 return "fs:USER";
29 }
30};
31
32} // namespace FS
33} // 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 48d806e2f..000000000
--- a/src/core/hle/service/fs_user.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common.h"
6
7#include "fs_user.h"
8#include "core/settings.h"
9#include "core/hle/kernel/archive.h"
10
11////////////////////////////////////////////////////////////////////////////////////////////////////
12// Namespace FS_User
13
14namespace FS_User {
15
16// Command to access archive file
17enum class LowPathType : u32 {
18 Invalid = 0,
19 Empty = 1,
20 Binary = 2,
21 Char = 3,
22 Wchar = 4
23};
24
25std::string GetStringFromCmdBuff(const u32 pointer, const u32 size) {
26 auto data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
27 return std::string(data, size - 1);
28}
29
30// We currently return 0 for success and -1 for failure in cmd_buff[1]. -1 was chosen because it
31// puts all the sections of the http://3dbrew.org/wiki/Error_codes to something non-zero, to make
32// sure we don't mislead the application into thinking something worked.
33
34void Initialize(Service::Interface* self) {
35 u32* cmd_buff = Service::GetCommandBuffer();
36
37 // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
38 // http://3dbrew.org/wiki/FS:Initialize#Request
39 cmd_buff[1] = 0;
40
41 DEBUG_LOG(KERNEL, "called");
42}
43
44void OpenFile(Service::Interface* self) {
45 u32* cmd_buff = Service::GetCommandBuffer();
46
47 u32 transaction = cmd_buff[1];
48 // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
49 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
50 Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
51 LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
52 u32 size = cmd_buff[5];
53 FileSys::Mode mode; mode.hex = cmd_buff[6];
54 u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes.
55 u32 pointer = cmd_buff[9];
56
57 if (type != LowPathType::Char) {
58 ERROR_LOG(KERNEL, "file LowPath type other than char is currently unsupported");
59 cmd_buff[1] = -1;
60 return;
61 }
62
63 std::string file_name = GetStringFromCmdBuff(pointer, size);
64
65 DEBUG_LOG(KERNEL, "type=%d size=%d mode=%d attrs=%d data=%s", type, size, mode, attributes, file_name.c_str());
66
67 Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
68 if (handle) {
69 cmd_buff[1] = 0;
70 cmd_buff[3] = handle;
71 } else {
72 ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
73 // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
74 cmd_buff[1] = -1;
75 }
76
77 DEBUG_LOG(KERNEL, "called");
78}
79
80void OpenFileDirectly(Service::Interface* self) {
81 u32* cmd_buff = Service::GetCommandBuffer();
82
83 u32 transaction = cmd_buff[1];
84 auto archive_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[2]);
85 LowPathType archive_type = static_cast<LowPathType>(cmd_buff[3]);
86 u32 archive_size = cmd_buff[4];
87 LowPathType file_type = static_cast<LowPathType>(cmd_buff[5]);
88 u32 size = cmd_buff[6];
89 FileSys::Mode mode; mode.hex = cmd_buff[7];
90 u32 attributes = cmd_buff[8]; // TODO(Link Mauve): do something with those attributes.
91 u32 archive_pointer = cmd_buff[10];
92 u32 pointer = cmd_buff[12];
93
94 if (archive_type != LowPathType::Empty) {
95 ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
96 cmd_buff[1] = -1;
97 return;
98 }
99
100 std::string archive_name = GetStringFromCmdBuff(archive_pointer, archive_size);
101 std::string file_name = GetStringFromCmdBuff(pointer, size);
102
103 DEBUG_LOG(KERNEL, "archive_type=%d archive_size=%d archive_data=%s "
104 "file_type=%d file_size=%d file_mode=%d file_attrs=%d file_data=%s",
105 archive_type, archive_size, archive_name.c_str(),
106 file_type, size, mode, attributes, file_name.c_str());
107
108 // TODO(Link Mauve): check if we should even get a handle for the archive, and don't leak it.
109 Handle archive_handle = Kernel::OpenArchive(archive_id);
110 if (archive_handle) {
111 cmd_buff[1] = 0;
112 // cmd_buff[2] isn't used according to 3dmoo's implementation.
113 cmd_buff[3] = archive_handle;
114 } else {
115 ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
116 // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
117 cmd_buff[1] = -1;
118 return;
119 }
120
121 if (file_type != LowPathType::Char) {
122 WARN_LOG(KERNEL, "file LowPath type other than char is currently unsupported; returning archive handle instead");
123 return;
124 }
125
126 Handle handle = Kernel::OpenFileFromArchive(archive_handle, file_name, mode);
127 if (handle) {
128 cmd_buff[1] = 0;
129 cmd_buff[3] = handle;
130 } else {
131 ERROR_LOG(KERNEL, "failed to get a handle for file %s", file_name.c_str());
132 // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
133 cmd_buff[1] = -1;
134 }
135
136 DEBUG_LOG(KERNEL, "called");
137}
138
139/*
140 * FS_User::CreateDirectory service function
141 * Inputs:
142 * 2 : Archive handle lower word
143 * 3 : Archive handle upper word
144 * 4 : Directory path string type
145 * 5 : Directory path string size
146 * 8 : Directory path string data
147 * Outputs:
148 * 1 : Result of function, 0 on success, otherwise error code
149 */
150void CreateDirectory(Service::Interface* self) {
151 u32* cmd_buff = Service::GetCommandBuffer();
152
153 // TODO: cmd_buff[2], aka archive handle lower word, isn't used according to
154 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
155 Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
156 LowPathType type = static_cast<LowPathType>(cmd_buff[4]);
157 u32 name_size = cmd_buff[5];
158 u32 name_offset = cmd_buff[8];
159
160 if (type != LowPathType::Char) {
161 ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
162 cmd_buff[1] = -1;
163 return;
164 }
165
166 std::string dir_name = GetStringFromCmdBuff(name_offset, name_size);
167
168 DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, name_size, dir_name.c_str());
169
170 cmd_buff[1] = Kernel::CreateDirectoryFromArchive(archive_handle, dir_name);
171
172 DEBUG_LOG(KERNEL, "called");
173}
174
175void OpenDirectory(Service::Interface* self) {
176 u32* cmd_buff = Service::GetCommandBuffer();
177
178 // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
179 // 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
180 Handle archive_handle = static_cast<Handle>(cmd_buff[2]);
181 LowPathType type = static_cast<LowPathType>(cmd_buff[3]);
182 u32 size = cmd_buff[4];
183 u32 pointer = cmd_buff[6];
184
185 if (type != LowPathType::Char) {
186 ERROR_LOG(KERNEL, "directory LowPath type other than char is currently unsupported");
187 cmd_buff[1] = -1;
188 return;
189 }
190
191 std::string dir_name = GetStringFromCmdBuff(pointer, size);
192
193 DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, dir_name.c_str());
194
195 Handle handle = Kernel::OpenDirectoryFromArchive(archive_handle, dir_name);
196 if (handle) {
197 cmd_buff[1] = 0;
198 cmd_buff[3] = handle;
199 } else {
200 ERROR_LOG(KERNEL, "failed to get a handle for directory %s", dir_name.c_str());
201 // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
202 cmd_buff[1] = -1;
203 }
204
205 DEBUG_LOG(KERNEL, "called");
206}
207
208void OpenArchive(Service::Interface* self) {
209 u32* cmd_buff = Service::GetCommandBuffer();
210
211 auto arch_id = static_cast<FileSys::Archive::IdCode>(cmd_buff[1]);
212 LowPathType type = static_cast<LowPathType>(cmd_buff[2]);
213 u32 size = cmd_buff[3];
214 u32 pointer = cmd_buff[5];
215
216 if (type != LowPathType::Empty) {
217 ERROR_LOG(KERNEL, "archive LowPath type other than empty is currently unsupported");
218 cmd_buff[1] = -1;
219 return;
220 }
221
222 std::string archive_name = GetStringFromCmdBuff(pointer, size);
223
224 DEBUG_LOG(KERNEL, "type=%d size=%d data=%s", type, size, archive_name.c_str());
225
226 Handle handle = Kernel::OpenArchive(arch_id);
227 if (handle) {
228 cmd_buff[1] = 0;
229 // cmd_buff[2] isn't used according to 3dmoo's implementation.
230 cmd_buff[3] = handle;
231 } else {
232 ERROR_LOG(KERNEL, "failed to get a handle for archive %s", archive_name.c_str());
233 // TODO(Link Mauve): check for the actual error values, this one was just chosen arbitrarily.
234 cmd_buff[1] = -1;
235 }
236
237 DEBUG_LOG(KERNEL, "called");
238}
239
240/*
241* FS_User::IsSdmcDetected service function
242* Outputs:
243* 1 : Result of function, 0 on success, otherwise error code
244* 2 : Whether the Sdmc could be detected
245*/
246void IsSdmcDetected(Service::Interface* self) {
247 u32* cmd_buff = Service::GetCommandBuffer();
248
249 cmd_buff[1] = 0;
250 cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
251
252 DEBUG_LOG(KERNEL, "called");
253}
254
255const Interface::FunctionInfo FunctionTable[] = {
256 {0x000100C6, nullptr, "Dummy1"},
257 {0x040100C4, nullptr, "Control"},
258 {0x08010002, Initialize, "Initialize"},
259 {0x080201C2, OpenFile, "OpenFile"},
260 {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
261 {0x08040142, nullptr, "DeleteFile"},
262 {0x08050244, nullptr, "RenameFile"},
263 {0x08060142, nullptr, "DeleteDirectory"},
264 {0x08070142, nullptr, "DeleteDirectoryRecursively"},
265 {0x08080202, nullptr, "CreateFile"},
266 {0x08090182, CreateDirectory, "CreateDirectory"},
267 {0x080A0244, nullptr, "RenameDirectory"},
268 {0x080B0102, OpenDirectory, "OpenDirectory"},
269 {0x080C00C2, OpenArchive, "OpenArchive"},
270 {0x080D0144, nullptr, "ControlArchive"},
271 {0x080E0080, nullptr, "CloseArchive"},
272 {0x080F0180, nullptr, "FormatThisUserSaveData"},
273 {0x08100200, nullptr, "CreateSystemSaveData"},
274 {0x08110040, nullptr, "DeleteSystemSaveData"},
275 {0x08120080, nullptr, "GetFreeBytes"},
276 {0x08130000, nullptr, "GetCardType"},
277 {0x08140000, nullptr, "GetSdmcArchiveResource"},
278 {0x08150000, nullptr, "GetNandArchiveResource"},
279 {0x08160000, nullptr, "GetSdmcFatfsErro"},
280 {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
281 {0x08180000, nullptr, "IsSdmcWritable"},
282 {0x08190042, nullptr, "GetSdmcCid"},
283 {0x081A0042, nullptr, "GetNandCid"},
284 {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
285 {0x081C0000, nullptr, "GetNandSpeedInfo"},
286 {0x081D0042, nullptr, "GetSdmcLog"},
287 {0x081E0042, nullptr, "GetNandLog"},
288 {0x081F0000, nullptr, "ClearSdmcLog"},
289 {0x08200000, nullptr, "ClearNandLog"},
290 {0x08210000, nullptr, "CardSlotIsInserted"},
291 {0x08220000, nullptr, "CardSlotPowerOn"},
292 {0x08230000, nullptr, "CardSlotPowerOff"},
293 {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
294 {0x08250040, nullptr, "CardNorDirectCommand"},
295 {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
296 {0x08270082, nullptr, "CardNorDirectRead"},
297 {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
298 {0x08290082, nullptr, "CardNorDirectWrite"},
299 {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
300 {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
301 {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
302 {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
303 {0x082E0040, nullptr, "GetProductInfo"},
304 {0x082F0040, nullptr, "GetProgramLaunchInfo"},
305 {0x08300182, nullptr, "CreateExtSaveData"},
306 {0x08310180, nullptr, "CreateSharedExtSaveData"},
307 {0x08320102, nullptr, "ReadExtSaveDataIcon"},
308 {0x08330082, nullptr, "EnumerateExtSaveData"},
309 {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
310 {0x08350080, nullptr, "DeleteExtSaveData"},
311 {0x08360080, nullptr, "DeleteSharedExtSaveData"},
312 {0x08370040, nullptr, "SetCardSpiBaudRate"},
313 {0x08380040, nullptr, "SetCardSpiBusMode"},
314 {0x08390000, nullptr, "SendInitializeInfoTo9"},
315 {0x083A0100, nullptr, "GetSpecialContentIndex"},
316 {0x083B00C2, nullptr, "GetLegacyRomHeader"},
317 {0x083C00C2, nullptr, "GetLegacyBannerData"},
318 {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
319 {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
320 {0x083F00C0, nullptr, "GetExtDataBlockSize"},
321 {0x08400040, nullptr, "AbnegateAccessRight"},
322 {0x08410000, nullptr, "DeleteSdmcRoot"},
323 {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
324 {0x08430000, nullptr, "InitializeCtrFileSystem"},
325 {0x08440000, nullptr, "CreateSeed"},
326 {0x084500C2, nullptr, "GetFormatInfo"},
327 {0x08460102, nullptr, "GetLegacyRomHeader2"},
328 {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
329 {0x08480042, nullptr, "GetSdmcCtrRootPath"},
330 {0x08490040, nullptr, "GetArchiveResource"},
331 {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
332 {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
333 {0x084C0242, nullptr, "FormatSaveData"},
334 {0x084D0102, nullptr, "GetLegacySubBannerData"},
335 {0x084E0342, nullptr, "UpdateSha256Context"},
336 {0x084F0102, nullptr, "ReadSpecialFile"},
337 {0x08500040, nullptr, "GetSpecialFileSize"},
338 {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
339 {0x08610042, nullptr, "InitializeWithSdkVersion"},
340 {0x08620040, nullptr, "SetPriority"},
341 {0x08630000, nullptr, "GetPriority"},
342};
343
344////////////////////////////////////////////////////////////////////////////////////////////////////
345// Interface class
346
347Interface::Interface() {
348 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
349}
350
351Interface::~Interface() {
352}
353
354} // namespace
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 6119e6300..db8027142 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
28 28
29/// Gets a pointer to a thread command buffer in GSP shared memory 29/// Gets a pointer to a thread command buffer in GSP shared memory
30static inline u8* GetCommandBuffer(u32 thread_id) { 30static inline u8* GetCommandBuffer(u32 thread_id) {
31 if (0 == g_shared_memory) 31 ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer)));
32 return nullptr; 32 return ptr.ValueOr(nullptr);
33
34 return Kernel::GetSharedMemoryPointer(g_shared_memory,
35 0x800 + (thread_id * sizeof(CommandBuffer)));
36} 33}
37 34
38static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { 35static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
39 if (0 == g_shared_memory) 36 _dbg_assert_msg_(Service_GSP, screen_index < 2, "Invalid screen index");
40 return nullptr;
41
42 _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
43 37
44 // For each thread there are two FrameBufferUpdate fields 38 // For each thread there are two FrameBufferUpdate fields
45 u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); 39 u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
46 return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset); 40 ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
41 return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr));
47} 42}
48 43
49/// Gets a pointer to the interrupt relay queue for a given thread index 44/// Gets a pointer to the interrupt relay queue for a given thread index
50static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { 45static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
51 return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory, 46 ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id);
52 sizeof(InterruptRelayQueue) * thread_id); 47 return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr));
53} 48}
54 49
55void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { 50static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
56 // TODO: Return proper error codes 51 // TODO: Return proper error codes
57 if (base_address + size_in_bytes >= 0x420000) { 52 if (base_address + size_in_bytes >= 0x420000) {
58 ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)", 53 LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)",
59 base_address, size_in_bytes); 54 base_address, size_in_bytes);
60 return; 55 return;
61 } 56 }
62 57
63 // size should be word-aligned 58 // size should be word-aligned
64 if ((size_in_bytes % 4) != 0) { 59 if ((size_in_bytes % 4) != 0) {
65 ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes); 60 LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes);
66 return; 61 return;
67 } 62 }
68 63
@@ -76,8 +71,8 @@ void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
76} 71}
77 72
78/// Write a GSP GPU hardware register 73/// Write a GSP GPU hardware register
79void WriteHWRegs(Service::Interface* self) { 74static void WriteHWRegs(Service::Interface* self) {
80 u32* cmd_buff = Service::GetCommandBuffer(); 75 u32* cmd_buff = Kernel::GetCommandBuffer();
81 u32 reg_addr = cmd_buff[1]; 76 u32 reg_addr = cmd_buff[1];
82 u32 size = cmd_buff[2]; 77 u32 size = cmd_buff[2];
83 78
@@ -87,20 +82,20 @@ void WriteHWRegs(Service::Interface* self) {
87} 82}
88 83
89/// Read a GSP GPU hardware register 84/// Read a GSP GPU hardware register
90void ReadHWRegs(Service::Interface* self) { 85static void ReadHWRegs(Service::Interface* self) {
91 u32* cmd_buff = Service::GetCommandBuffer(); 86 u32* cmd_buff = Kernel::GetCommandBuffer();
92 u32 reg_addr = cmd_buff[1]; 87 u32 reg_addr = cmd_buff[1];
93 u32 size = cmd_buff[2]; 88 u32 size = cmd_buff[2];
94 89
95 // TODO: Return proper error codes 90 // TODO: Return proper error codes
96 if (reg_addr + size >= 0x420000) { 91 if (reg_addr + size >= 0x420000) {
97 ERROR_LOG(GPU, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size); 92 LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size);
98 return; 93 return;
99 } 94 }
100 95
101 // size should be word-aligned 96 // size should be word-aligned
102 if ((size % 4) != 0) { 97 if ((size % 4) != 0) {
103 ERROR_LOG(GPU, "Invalid size 0x%08x", size); 98 LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size);
104 return; 99 return;
105 } 100 }
106 101
@@ -115,7 +110,7 @@ void ReadHWRegs(Service::Interface* self) {
115 } 110 }
116} 111}
117 112
118void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { 113static void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
119 u32 base_address = 0x400000; 114 u32 base_address = 0x400000;
120 if (info.active_fb == 0) { 115 if (info.active_fb == 0) {
121 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left1), 4, &info.address_left); 116 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) {
140 * Outputs: 135 * Outputs:
141 * 1: Result code 136 * 1: Result code
142 */ 137 */
143void SetBufferSwap(Service::Interface* self) { 138static void SetBufferSwap(Service::Interface* self) {
144 u32* cmd_buff = Service::GetCommandBuffer(); 139 u32* cmd_buff = Kernel::GetCommandBuffer();
145 u32 screen_id = cmd_buff[1]; 140 u32 screen_id = cmd_buff[1];
146 FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; 141 FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
147 SetBufferSwap(screen_id, *fb_info); 142 SetBufferSwap(screen_id, *fb_info);
@@ -159,15 +154,16 @@ void SetBufferSwap(Service::Interface* self) {
159 * 2 : Thread index into GSP command buffer 154 * 2 : Thread index into GSP command buffer
160 * 4 : Handle to GSP shared memory 155 * 4 : Handle to GSP shared memory
161 */ 156 */
162void RegisterInterruptRelayQueue(Service::Interface* self) { 157static void RegisterInterruptRelayQueue(Service::Interface* self) {
163 u32* cmd_buff = Service::GetCommandBuffer(); 158 u32* cmd_buff = Kernel::GetCommandBuffer();
164 u32 flags = cmd_buff[1]; 159 u32 flags = cmd_buff[1];
165 g_interrupt_event = cmd_buff[3]; 160 g_interrupt_event = cmd_buff[3];
166 g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); 161 g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem");
167 162
168 _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); 163 _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!");
169 164
170 cmd_buff[2] = g_thread_id++; // ThreadID 165 cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init
166 cmd_buff[2] = g_thread_id++; // Thread ID
171 cmd_buff[4] = g_shared_memory; // GSP shared memory 167 cmd_buff[4] = g_shared_memory; // GSP shared memory
172 168
173 Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? 169 Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct?
@@ -177,14 +173,15 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
177 * Signals that the specified interrupt type has occurred to userland code 173 * Signals that the specified interrupt type has occurred to userland code
178 * @param interrupt_id ID of interrupt that is being signalled 174 * @param interrupt_id ID of interrupt that is being signalled
179 * @todo This should probably take a thread_id parameter and only signal this thread? 175 * @todo This should probably take a thread_id parameter and only signal this thread?
176 * @todo This probably does not belong in the GSP module, instead move to video_core
180 */ 177 */
181void SignalInterrupt(InterruptId interrupt_id) { 178void SignalInterrupt(InterruptId interrupt_id) {
182 if (0 == g_interrupt_event) { 179 if (0 == g_interrupt_event) {
183 WARN_LOG(GSP, "cannot synchronize until GSP event has been created!"); 180 LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
184 return; 181 return;
185 } 182 }
186 if (0 == g_shared_memory) { 183 if (0 == g_shared_memory) {
187 WARN_LOG(GSP, "cannot synchronize until GSP shared memory has been created!"); 184 LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
188 return; 185 return;
189 } 186 }
190 for (int thread_id = 0; thread_id < 0x4; ++thread_id) { 187 for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -202,7 +199,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
202} 199}
203 200
204/// Executes the next GSP command 201/// Executes the next GSP command
205void ExecuteCommand(const Command& command, u32 thread_id) { 202static void ExecuteCommand(const Command& command, u32 thread_id) {
206 // Utility function to convert register ID to address 203 // Utility function to convert register ID to address
207 auto WriteGPURegister = [](u32 id, u32 data) { 204 auto WriteGPURegister = [](u32 id, u32 data) {
208 GPU::Write<u32>(0x1EF00000 + 4 * id, data); 205 GPU::Write<u32>(0x1EF00000 + 4 * id, data);
@@ -215,6 +212,7 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
215 memcpy(Memory::GetPointer(command.dma_request.dest_address), 212 memcpy(Memory::GetPointer(command.dma_request.dest_address),
216 Memory::GetPointer(command.dma_request.source_address), 213 Memory::GetPointer(command.dma_request.source_address),
217 command.dma_request.size); 214 command.dma_request.size);
215 SignalInterrupt(InterruptId::DMA);
218 break; 216 break;
219 217
220 // ctrulib homebrew sends all relevant command list data with this command, 218 // ctrulib homebrew sends all relevant command list data with this command,
@@ -223,13 +221,13 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
223 case CommandId::SET_COMMAND_LIST_LAST: 221 case CommandId::SET_COMMAND_LIST_LAST:
224 { 222 {
225 auto& params = command.set_command_list_last; 223 auto& params = command.set_command_list_last;
224
226 WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3); 225 WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), Memory::VirtualToPhysicalAddress(params.address) >> 3);
227 WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3); 226 WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size);
228 227
229 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though 228 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
230 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); 229 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
231 230
232 SignalInterrupt(InterruptId::P3D);
233 break; 231 break;
234 } 232 }
235 233
@@ -247,6 +245,8 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
247 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); 245 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
248 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); 246 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
249 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); 247 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
248
249 SignalInterrupt(InterruptId::PSC0);
250 break; 250 break;
251 } 251 }
252 252
@@ -260,14 +260,9 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
260 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); 260 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
261 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); 261 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
262 262
263 // TODO(bunnei): Signalling all of these interrupts here is totally wrong, but it seems to 263 // TODO(bunnei): Determine if these interrupts should be signalled here.
264 // work well enough for running demos. Need to figure out how these all work and trigger
265 // them correctly.
266 SignalInterrupt(InterruptId::PSC0);
267 SignalInterrupt(InterruptId::PSC1); 264 SignalInterrupt(InterruptId::PSC1);
268 SignalInterrupt(InterruptId::PPF); 265 SignalInterrupt(InterruptId::PPF);
269 SignalInterrupt(InterruptId::P3D);
270 SignalInterrupt(InterruptId::DMA);
271 266
272 // Update framebuffer information if requested 267 // Update framebuffer information if requested
273 for (int screen_id = 0; screen_id < 2; ++screen_id) { 268 for (int screen_id = 0; screen_id < 2; ++screen_id) {
@@ -303,12 +298,14 @@ void ExecuteCommand(const Command& command, u32 thread_id) {
303 } 298 }
304 299
305 default: 300 default:
306 ERROR_LOG(GSP, "unknown command 0x%08X", (int)command.id.Value()); 301 LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value());
307 } 302 }
308} 303}
309 304
310/// This triggers handling of the GX command written to the command buffer in shared memory. 305/// This triggers handling of the GX command written to the command buffer in shared memory.
311void TriggerCmdReqQueue(Service::Interface* self) { 306static void TriggerCmdReqQueue(Service::Interface* self) {
307
308 LOG_TRACE(Service_GSP, "called");
312 309
313 // Iterate through each thread's command queue... 310 // Iterate through each thread's command queue...
314 for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { 311 for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) {
@@ -325,6 +322,9 @@ void TriggerCmdReqQueue(Service::Interface* self) {
325 command_buffer->number_commands = command_buffer->number_commands - 1; 322 command_buffer->number_commands = command_buffer->number_commands - 1;
326 } 323 }
327 } 324 }
325
326 u32* cmd_buff = Kernel::GetCommandBuffer();
327 cmd_buff[1] = 0; // No error
328} 328}
329 329
330const Interface::FunctionInfo FunctionTable[] = { 330const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
index 0eb32ba4a..eb2d35964 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;
34 * Gets a pointer to the PadData structure inside HID shared memory 34 * Gets a pointer to the PadData structure inside HID shared memory
35 */ 35 */
36static inline PadData* GetPadData() { 36static inline PadData* GetPadData() {
37 if (0 == shared_mem) 37 return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr));
38 return nullptr;
39
40 return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0));
41} 38}
42 39
43/** 40/**
@@ -47,7 +44,7 @@ static inline PadData* GetPadData() {
47 * 44 *
48 * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions. 45 * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions.
49 */ 46 */
50void UpdateNextCirclePadState() { 47static void UpdateNextCirclePadState() {
51 static const s16 max_value = 0x9C; 48 static const s16 max_value = 0x9C;
52 next_circle_x = next_state.circle_left ? -max_value : 0x0; 49 next_circle_x = next_state.circle_left ? -max_value : 0x0;
53 next_circle_x += next_state.circle_right ? max_value : 0x0; 50 next_circle_x += next_state.circle_right ? max_value : 0x0;
@@ -58,7 +55,7 @@ void UpdateNextCirclePadState() {
58/** 55/**
59 * Sets a Pad state (button or button combo) as pressed 56 * Sets a Pad state (button or button combo) as pressed
60 */ 57 */
61void PadButtonPress(PadState pad_state) { 58void PadButtonPress(const PadState& pad_state) {
62 next_state.hex |= pad_state.hex; 59 next_state.hex |= pad_state.hex;
63 UpdateNextCirclePadState(); 60 UpdateNextCirclePadState();
64} 61}
@@ -66,7 +63,7 @@ void PadButtonPress(PadState pad_state) {
66/** 63/**
67 * Sets a Pad state (button or button combo) as released 64 * Sets a Pad state (button or button combo) as released
68 */ 65 */
69void PadButtonRelease(PadState pad_state) { 66void PadButtonRelease(const PadState& pad_state) {
70 next_state.hex &= ~pad_state.hex; 67 next_state.hex &= ~pad_state.hex;
71 UpdateNextCirclePadState(); 68 UpdateNextCirclePadState();
72} 69}
@@ -155,8 +152,8 @@ void PadUpdateComplete() {
155 * 7 : Gyroscope event 152 * 7 : Gyroscope event
156 * 8 : Event signaled by HID_User 153 * 8 : Event signaled by HID_User
157 */ 154 */
158void GetIPCHandles(Service::Interface* self) { 155static void GetIPCHandles(Service::Interface* self) {
159 u32* cmd_buff = Service::GetCommandBuffer(); 156 u32* cmd_buff = Kernel::GetCommandBuffer();
160 157
161 cmd_buff[1] = 0; // No error 158 cmd_buff[1] = 0; // No error
162 cmd_buff[3] = shared_mem; 159 cmd_buff[3] = shared_mem;
@@ -166,7 +163,7 @@ void GetIPCHandles(Service::Interface* self) {
166 cmd_buff[7] = event_gyroscope; 163 cmd_buff[7] = event_gyroscope;
167 cmd_buff[8] = event_debug_pad; 164 cmd_buff[8] = event_debug_pad;
168 165
169 DEBUG_LOG(KERNEL, "called"); 166 LOG_TRACE(Service_HID, "called");
170} 167}
171 168
172const Interface::FunctionInfo FunctionTable[] = { 169const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid_user.h
index 9f6c4d5ed..8f53befdb 100644
--- a/src/core/hle/service/hid_user.h
+++ b/src/core/hle/service/hid_user.h
@@ -15,7 +15,7 @@
15 15
16namespace HID_User { 16namespace HID_User {
17 17
18/** 18/**
19 * Structure of a Pad controller state. 19 * Structure of a Pad controller state.
20 */ 20 */
21struct PadState { 21struct PadState {
@@ -93,8 +93,8 @@ const PadState PAD_CIRCLE_UP = {{1u << 30}};
93const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; 93const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
94 94
95// Methods for updating the HID module's state 95// Methods for updating the HID module's state
96void PadButtonPress(PadState pad_state); 96void PadButtonPress(const PadState& pad_state);
97void PadButtonRelease(PadState pad_state); 97void PadButtonRelease(const PadState& pad_state);
98void PadUpdateComplete(); 98void PadUpdateComplete();
99 99
100/** 100/**
diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp
new file mode 100644
index 000000000..be15db231
--- /dev/null
+++ b/src/core/hle/service/ir_rst.cpp
@@ -0,0 +1,36 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/ir_rst.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace IR_RST
11
12namespace IR_RST {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010000, nullptr, "GetHandles"},
16 {0x00020080, nullptr, "Initialize"},
17 {0x00030000, nullptr, "Shutdown"},
18 {0x00040000, nullptr, "Unknown"},
19 {0x00050000, nullptr, "Unknown"},
20 {0x00060000, nullptr, "Unknown"},
21 {0x00070080, nullptr, "Unknown"},
22 {0x00080000, nullptr, "Unknown"},
23 {0x00090000, nullptr, "Unknown"},
24};
25
26////////////////////////////////////////////////////////////////////////////////////////////////////
27// Interface class
28
29Interface::Interface() {
30 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
31}
32
33Interface::~Interface() {
34}
35
36} // namespace
diff --git a/src/core/hle/service/ir_rst.h b/src/core/hle/service/ir_rst.h
new file mode 100644
index 000000000..73effd7e3
--- /dev/null
+++ b/src/core/hle/service/ir_rst.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace IR_RST
11
12namespace IR_RST {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17 ~Interface();
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "ir:rst";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp
new file mode 100644
index 000000000..aa9db6f6d
--- /dev/null
+++ b/src/core/hle/service/ir_u.cpp
@@ -0,0 +1,45 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/ir_u.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace IR_U
11
12namespace IR_U {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010000, nullptr, "Initialize"},
16 {0x00020000, nullptr, "Shutdown"},
17 {0x00030042, nullptr, "StartSendTransfer"},
18 {0x00040000, nullptr, "WaitSendTransfer"},
19 {0x000500C2, nullptr, "StartRecvTransfer"},
20 {0x00060000, nullptr, "WaitRecvTransfer"},
21 {0x00070080, nullptr, "GetRecvTransferCount"},
22 {0x00080000, nullptr, "GetSendState"},
23 {0x00090040, nullptr, "SetBitRate"},
24 {0x000A0000, nullptr, "GetBitRate"},
25 {0x000B0040, nullptr, "SetIRLEDState"},
26 {0x000C0000, nullptr, "GetIRLEDRecvState"},
27 {0x000D0000, nullptr, "GetSendFinishedEvent"},
28 {0x000E0000, nullptr, "GetRecvFinishedEvent"},
29 {0x000F0000, nullptr, "GetTransferState"},
30 {0x00100000, nullptr, "GetErrorStatus"},
31 {0x00110040, nullptr, "SetSleepModeActive"},
32 {0x00120040, nullptr, "SetSleepModeState"},
33};
34
35////////////////////////////////////////////////////////////////////////////////////////////////////
36// Interface class
37
38Interface::Interface() {
39 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
40}
41
42Interface::~Interface() {
43}
44
45} // namespace
diff --git a/src/core/hle/service/ir_u.h b/src/core/hle/service/ir_u.h
new file mode 100644
index 000000000..86d98d079
--- /dev/null
+++ b/src/core/hle/service/ir_u.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace IR_U
11
12namespace IR_U {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17 ~Interface();
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "ir:u";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
new file mode 100644
index 000000000..91b1a6fc5
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -0,0 +1,28 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/ldr_ro.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace LDR_RO
11
12namespace LDR_RO {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x000100C2, nullptr, "Initialize"},
16 {0x00020082, nullptr, "CRR_Load"},
17 {0x00030042, nullptr, "CRR_Unload"},
18 {0x000402C2, nullptr, "CRO_LoadAndFix"},
19 {0x000500C2, nullptr, "CRO_ApplyRelocationPatchesAndLink"}
20};
21////////////////////////////////////////////////////////////////////////////////////////////////////
22// Interface class
23
24Interface::Interface() {
25 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
26}
27
28} // namespace
diff --git a/src/core/hle/service/ldr_ro.h b/src/core/hle/service/ldr_ro.h
new file mode 100644
index 000000000..32d7c29cf
--- /dev/null
+++ b/src/core/hle/service/ldr_ro.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace LDR_RO
11
12namespace LDR_RO {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "ldr:ro";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 58051f133..d6f30e9ae 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[] = {
27 {0x000D0040, nullptr, "SetClamp"}, 27 {0x000D0040, nullptr, "SetClamp"},
28 {0x000E0000, nullptr, "GetClamp"}, 28 {0x000E0000, nullptr, "GetClamp"},
29 {0x000F0040, nullptr, "unknown_input1"}, 29 {0x000F0040, nullptr, "unknown_input1"},
30 {0x00100040, nullptr, "unknown_input2"}, 30 {0x00100040, nullptr, "unknown_input2"},
31}; 31};
32 32
33//////////////////////////////////////////////////////////////////////////////////////////////////// 33////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index 72ba048ef..2a495f3a9 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -21,7 +21,7 @@ public:
21 * Gets the string port name used by CTROS for the service 21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service 22 * @return Port name of service
23 */ 23 */
24 std::string GetPortName() const { 24 std::string GetPortName() const override {
25 return "mic:u"; 25 return "mic:u";
26 } 26 }
27}; 27};
diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp
new file mode 100644
index 000000000..04c1e0cf3
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.cpp
@@ -0,0 +1,31 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/nim_aoc.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace NIM_AOC
11
12namespace NIM_AOC {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00030042, nullptr, "SetApplicationId"},
16 {0x00040042, nullptr, "SetTin"},
17 {0x000902D0, nullptr, "ListContentSetsEx"},
18 {0x00180000, nullptr, "GetBalance"},
19 {0x001D0000, nullptr, "GetCustomerSupportCode"},
20 {0x00210000, nullptr, "Initialize"},
21 {0x00240282, nullptr, "CalculateContentsRequiredSize"},
22 {0x00250000, nullptr, "RefreshServerTime"},
23};
24////////////////////////////////////////////////////////////////////////////////////////////////////
25// Interface class
26
27Interface::Interface() {
28 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
29}
30
31} // namespace
diff --git a/src/core/hle/service/nim_aoc.h b/src/core/hle/service/nim_aoc.h
new file mode 100644
index 000000000..2cc673118
--- /dev/null
+++ b/src/core/hle/service/nim_aoc.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace NIM_AOC
11
12namespace NIM_AOC {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "nim:aoc";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/nwm_uds.h b/src/core/hle/service/nwm_uds.h
index a956ca812..69d2c2002 100644
--- a/src/core/hle/service/nwm_uds.h
+++ b/src/core/hle/service/nwm_uds.h
@@ -21,7 +21,7 @@ public:
21 * Gets the string port name used by CTROS for the service 21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service 22 * @return Port name of service
23 */ 23 */
24 std::string GetPortName() const { 24 std::string GetPortName() const override {
25 return "nwm:UDS"; 25 return "nwm:UDS";
26 } 26 }
27}; 27};
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
new file mode 100644
index 000000000..90e9b1bfa
--- /dev/null
+++ b/src/core/hle/service/pm_app.cpp
@@ -0,0 +1,35 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/log.h"
6#include "core/hle/hle.h"
7#include "core/hle/service/pm_app.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace PM_APP
11
12namespace PM_APP {
13
14const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010140, nullptr, "LaunchTitle"},
16 {0x00020082, nullptr, "LaunchFIRMSetParams"},
17 {0x00030080, nullptr, "TerminateProcesse"},
18 {0x00040100, nullptr, "TerminateProcessTID"},
19 {0x000500C0, nullptr, "TerminateProcessTID_unknown"},
20 {0x00070042, nullptr, "GetFIRMLaunchParams"},
21 {0x00080100, nullptr, "GetTitleExheaderFlags"},
22 {0x00090042, nullptr, "SetFIRMLaunchParams"},
23};
24
25////////////////////////////////////////////////////////////////////////////////////////////////////
26// Interface class
27
28Interface::Interface() {
29 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
30}
31
32Interface::~Interface() {
33}
34
35} // namespace
diff --git a/src/core/hle/service/pm_app.h b/src/core/hle/service/pm_app.h
new file mode 100644
index 000000000..28c38f582
--- /dev/null
+++ b/src/core/hle/service/pm_app.h
@@ -0,0 +1,27 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace PM_APP
11
12namespace PM_APP {
13
14class Interface : public Service::Interface {
15public:
16 Interface();
17 ~Interface();
18 /**
19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service
21 */
22 std::string GetPortName() const override {
23 return "pm:app";
24 }
25};
26
27} // namespace
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index f6a14d509..b8c0f6da8 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -11,19 +11,105 @@
11 11
12namespace PTM_U { 12namespace PTM_U {
13 13
14/// Charge levels used by PTM functions
15enum class ChargeLevels : u32 {
16 CriticalBattery = 1,
17 LowBattery = 2,
18 HalfFull = 3,
19 MostlyFull = 4,
20 CompletelyFull = 5,
21};
22
23static bool shell_open = true;
24
25static bool battery_is_charging = true;
26
27/**
28 * It is unknown if GetAdapterState is the same as GetBatteryChargeState,
29 * it is likely to just be a duplicate function of GetBatteryChargeState
30 * that controls another part of the HW.
31 * PTM_U::GetAdapterState service function
32 * Outputs:
33 * 1 : Result of function, 0 on success, otherwise error code
34 * 2 : Output of function, 0 = not charging, 1 = charging.
35 */
36static void GetAdapterState(Service::Interface* self) {
37 u32* cmd_buff = Kernel::GetCommandBuffer();
38
39 // TODO(purpasmart96): This function is only a stub,
40 // it returns a valid result without implementing full functionality.
41
42 cmd_buff[1] = 0; // No error
43 cmd_buff[2] = battery_is_charging ? 1 : 0;
44
45 LOG_WARNING(Service_PTM, "(STUBBED) called");
46}
47
48/*
49 * PTM_User::GetShellState service function.
50 * Outputs:
51 * 1 : Result of function, 0 on success, otherwise error code
52 * 2 : Whether the 3DS's physical shell casing is open (1) or closed (0)
53 */
54static void GetShellState(Service::Interface* self) {
55 u32* cmd_buff = Kernel::GetCommandBuffer();
56
57 cmd_buff[1] = 0;
58 cmd_buff[2] = shell_open ? 1 : 0;
59
60 LOG_TRACE(Service_PTM, "PTM_U::GetShellState called");
61}
62
63/**
64 * PTM_U::GetBatteryLevel service function
65 * Outputs:
66 * 1 : Result of function, 0 on success, otherwise error code
67 * 2 : Battery level, 5 = completely full battery, 4 = mostly full battery,
68 * 3 = half full battery, 2 = low battery, 1 = critical battery.
69 */
70static void GetBatteryLevel(Service::Interface* self) {
71 u32* cmd_buff = Kernel::GetCommandBuffer();
72
73 // TODO(purpasmart96): This function is only a stub,
74 // it returns a valid result without implementing full functionality.
75
76 cmd_buff[1] = 0; // No error
77 cmd_buff[2] = static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery
78
79 LOG_WARNING(Service_PTM, "(STUBBED) called");
80}
81
82/**
83 * PTM_U::GetBatteryChargeState service function
84 * Outputs:
85 * 1 : Result of function, 0 on success, otherwise error code
86 * 2 : Output of function, 0 = not charging, 1 = charging.
87 */
88static void GetBatteryChargeState(Service::Interface* self) {
89 u32* cmd_buff = Kernel::GetCommandBuffer();
90
91 // TODO(purpasmart96): This function is only a stub,
92 // it returns a valid result without implementing full functionality.
93
94 cmd_buff[1] = 0; // No error
95 cmd_buff[2] = battery_is_charging ? 1 : 0;
96
97 LOG_WARNING(Service_PTM, "(STUBBED) called");
98}
99
14const Interface::FunctionInfo FunctionTable[] = { 100const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010002, nullptr, "RegisterAlarmClient"}, 101 {0x00010002, nullptr, "RegisterAlarmClient"},
16 {0x00020080, nullptr, "SetRtcAlarm"}, 102 {0x00020080, nullptr, "SetRtcAlarm"},
17 {0x00030000, nullptr, "GetRtcAlarm"}, 103 {0x00030000, nullptr, "GetRtcAlarm"},
18 {0x00040000, nullptr, "CancelRtcAlarm"}, 104 {0x00040000, nullptr, "CancelRtcAlarm"},
19 {0x00050000, nullptr, "GetAdapterState"}, 105 {0x00050000, GetAdapterState, "GetAdapterState"},
20 {0x00060000, nullptr, "GetShellState "}, 106 {0x00060000, GetShellState, "GetShellState"},
21 {0x00070000, nullptr, "GetBatteryLevel"}, 107 {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
22 {0x00080000, nullptr, "GetBatteryChargeState"}, 108 {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
23 {0x00090000, nullptr, "GetPedometerState"}, 109 {0x00090000, nullptr, "GetPedometerState"},
24 {0x000A0042, nullptr, "GetStepHistoryEntry"}, 110 {0x000A0042, nullptr, "GetStepHistoryEntry"},
25 {0x000B00C2, nullptr, "GetStepHistory "}, 111 {0x000B00C2, nullptr, "GetStepHistory"},
26 {0x000C0000, nullptr, "GetTotalStepCount "}, 112 {0x000C0000, nullptr, "GetTotalStepCount"},
27 {0x000D0040, nullptr, "SetPedometerRecordingMode"}, 113 {0x000D0040, nullptr, "SetPedometerRecordingMode"},
28 {0x000E0000, nullptr, "GetPedometerRecordingMode"}, 114 {0x000E0000, nullptr, "GetPedometerRecordingMode"},
29 {0x000F0084, nullptr, "GetStepHistoryAll"}, 115 {0x000F0084, nullptr, "GetStepHistoryAll"},
diff --git a/src/core/hle/service/ptm_u.h b/src/core/hle/service/ptm_u.h
index 82749fa39..f8d9f57be 100644
--- a/src/core/hle/service/ptm_u.h
+++ b/src/core/hle/service/ptm_u.h
@@ -21,7 +21,7 @@ public:
21 * Gets the string port name used by CTROS for the service 21 * Gets the string port name used by CTROS for the service
22 * @return Port name of service 22 * @return Port name of service
23 */ 23 */
24 std::string GetPortName() const { 24 std::string GetPortName() const override {
25 return "ptm:u"; 25 return "ptm:u";
26 } 26 }
27}; 27};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b144a77d4..2230045e3 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,16 +7,28 @@
7 7
8#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
9#include "core/hle/service/ac_u.h" 9#include "core/hle/service/ac_u.h"
10#include "core/hle/service/am_app.h"
11#include "core/hle/service/am_net.h"
10#include "core/hle/service/apt_u.h" 12#include "core/hle/service/apt_u.h"
13#include "core/hle/service/boss_u.h"
14#include "core/hle/service/cecd_u.h"
15#include "core/hle/service/cfg_i.h"
11#include "core/hle/service/cfg_u.h" 16#include "core/hle/service/cfg_u.h"
17#include "core/hle/service/csnd_snd.h"
12#include "core/hle/service/dsp_dsp.h" 18#include "core/hle/service/dsp_dsp.h"
13#include "core/hle/service/err_f.h" 19#include "core/hle/service/err_f.h"
14#include "core/hle/service/fs_user.h" 20#include "core/hle/service/fs/fs_user.h"
21#include "core/hle/service/frd_u.h"
15#include "core/hle/service/gsp_gpu.h" 22#include "core/hle/service/gsp_gpu.h"
16#include "core/hle/service/hid_user.h" 23#include "core/hle/service/hid_user.h"
24#include "core/hle/service/ir_rst.h"
25#include "core/hle/service/ir_u.h"
26#include "core/hle/service/ldr_ro.h"
17#include "core/hle/service/mic_u.h" 27#include "core/hle/service/mic_u.h"
28#include "core/hle/service/nim_aoc.h"
18#include "core/hle/service/ndm_u.h" 29#include "core/hle/service/ndm_u.h"
19#include "core/hle/service/nwm_uds.h" 30#include "core/hle/service/nwm_uds.h"
31#include "core/hle/service/pm_app.h"
20#include "core/hle/service/ptm_u.h" 32#include "core/hle/service/ptm_u.h"
21#include "core/hle/service/soc_u.h" 33#include "core/hle/service/soc_u.h"
22#include "core/hle/service/srv.h" 34#include "core/hle/service/srv.h"
@@ -54,7 +66,7 @@ void Manager::DeleteService(const std::string& port_name) {
54 66
55/// Get a Service Interface from its Handle 67/// Get a Service Interface from its Handle
56Interface* Manager::FetchFromHandle(Handle handle) { 68Interface* Manager::FetchFromHandle(Handle handle) {
57 return Kernel::g_object_pool.GetFast<Interface>(handle); 69 return Kernel::g_object_pool.Get<Interface>(handle);
58} 70}
59 71
60/// Get a Service Interface from its port 72/// Get a Service Interface from its port
@@ -73,30 +85,42 @@ Interface* Manager::FetchFromPortName(const std::string& port_name) {
73/// Initialize ServiceManager 85/// Initialize ServiceManager
74void Init() { 86void Init() {
75 g_manager = new Manager; 87 g_manager = new Manager;
76 88
77 g_manager->AddService(new SRV::Interface); 89 g_manager->AddService(new SRV::Interface);
78 g_manager->AddService(new AC_U::Interface); 90 g_manager->AddService(new AC_U::Interface);
91 g_manager->AddService(new AM_APP::Interface);
92 g_manager->AddService(new AM_NET::Interface);
79 g_manager->AddService(new APT_U::Interface); 93 g_manager->AddService(new APT_U::Interface);
94 g_manager->AddService(new BOSS_U::Interface);
95 g_manager->AddService(new CECD_U::Interface);
96 g_manager->AddService(new CFG_I::Interface);
80 g_manager->AddService(new CFG_U::Interface); 97 g_manager->AddService(new CFG_U::Interface);
98 g_manager->AddService(new CSND_SND::Interface);
81 g_manager->AddService(new DSP_DSP::Interface); 99 g_manager->AddService(new DSP_DSP::Interface);
82 g_manager->AddService(new ERR_F::Interface); 100 g_manager->AddService(new ERR_F::Interface);
83 g_manager->AddService(new FS_User::Interface); 101 g_manager->AddService(new FRD_U::Interface);
102 g_manager->AddService(new FS::FSUserInterface);
84 g_manager->AddService(new GSP_GPU::Interface); 103 g_manager->AddService(new GSP_GPU::Interface);
85 g_manager->AddService(new HID_User::Interface); 104 g_manager->AddService(new HID_User::Interface);
105 g_manager->AddService(new IR_RST::Interface);
106 g_manager->AddService(new IR_U::Interface);
107 g_manager->AddService(new LDR_RO::Interface);
86 g_manager->AddService(new MIC_U::Interface); 108 g_manager->AddService(new MIC_U::Interface);
109 g_manager->AddService(new NIM_AOC::Interface);
87 g_manager->AddService(new NDM_U::Interface); 110 g_manager->AddService(new NDM_U::Interface);
88 g_manager->AddService(new NWM_UDS::Interface); 111 g_manager->AddService(new NWM_UDS::Interface);
112 g_manager->AddService(new PM_APP::Interface);
89 g_manager->AddService(new PTM_U::Interface); 113 g_manager->AddService(new PTM_U::Interface);
90 g_manager->AddService(new SOC_U::Interface); 114 g_manager->AddService(new SOC_U::Interface);
91 g_manager->AddService(new SSL_C::Interface); 115 g_manager->AddService(new SSL_C::Interface);
92 116
93 NOTICE_LOG(HLE, "initialized OK"); 117 LOG_DEBUG(Service, "initialized OK");
94} 118}
95 119
96/// Shutdown ServiceManager 120/// Shutdown ServiceManager
97void Shutdown() { 121void Shutdown() {
98 delete g_manager; 122 delete g_manager;
99 NOTICE_LOG(HLE, "shutdown OK"); 123 LOG_DEBUG(Service, "shutdown OK");
100} 124}
101 125
102 126
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2f5a866c9..9cd906150 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -10,9 +10,11 @@
10#include <string> 10#include <string>
11 11
12#include "common/common.h" 12#include "common/common.h"
13#include "common/string_util.h"
13#include "core/mem_map.h" 14#include "core/mem_map.h"
14 15
15#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/session.h"
16#include "core/hle/svc.h" 18#include "core/hle/svc.h"
17 19
18//////////////////////////////////////////////////////////////////////////////////////////////////// 20////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -20,30 +22,19 @@
20 22
21namespace Service { 23namespace Service {
22 24
23static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) 25static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
24static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
25
26/**
27 * Returns a pointer to the command buffer in kernel memory
28 * @param offset Optional offset into command buffer
29 * @return Pointer to command buffer
30 */
31inline static u32* GetCommandBuffer(const int offset=0) {
32 return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
33}
34 26
35class Manager; 27class Manager;
36 28
37/// Interface to a CTROS service 29/// Interface to a CTROS service
38class Interface : public Kernel::Object { 30class Interface : public Kernel::Session {
31 // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
32 // just something that encapsulates a session and acts as a helper to implement service
33 // processes.
34
39 friend class Manager; 35 friend class Manager;
40public: 36public:
41
42 std::string GetName() const override { return GetPortName(); } 37 std::string GetName() const override { return GetPortName(); }
43 std::string GetTypeName() const override { return GetPortName(); }
44
45 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; }
46 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Service; }
47 38
48 typedef void (*Function)(Interface*); 39 typedef void (*Function)(Interface*);
49 40
@@ -75,48 +66,31 @@ public:
75 m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); 66 m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end());
76 } 67 }
77 68
78 /** 69 ResultVal<bool> SyncRequest() override {
79 * Synchronize kernel object 70 u32* cmd_buff = Kernel::GetCommandBuffer();
80 * @param wait Boolean wait set if current thread should wait as a result of sync operation
81 * @return Result of operation, 0 on success, otherwise error code
82 */
83 Result SyncRequest(bool* wait) override {
84 u32* cmd_buff = GetCommandBuffer();
85 auto itr = m_functions.find(cmd_buff[0]); 71 auto itr = m_functions.find(cmd_buff[0]);
86 72
87 if (itr == m_functions.end()) { 73 if (itr == m_functions.end() || itr->second.func == nullptr) {
88 ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X", 74 // Number of params == bits 0-5 + bits 6-11
89 GetPortName().c_str(), cmd_buff[0]); 75 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
90 76
91 // TODO(bunnei): Hack - ignore error 77 std::string error = "unknown/unimplemented function '%s': port=%s";
92 u32* cmd_buff = Service::GetCommandBuffer(); 78 for (int i = 1; i <= num_params; ++i) {
93 cmd_buff[1] = 0; 79 error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
94 return 0; 80 }
95 } 81
96 if (itr->second.func == nullptr) { 82 std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
97 ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s", 83
98 GetPortName().c_str(), itr->second.name.c_str()); 84 LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str());
99 85
100 // TODO(bunnei): Hack - ignore error 86 // TODO(bunnei): Hack - ignore error
101 u32* cmd_buff = Service::GetCommandBuffer();
102 cmd_buff[1] = 0; 87 cmd_buff[1] = 0;
103 return 0; 88 return MakeResult<bool>(false);
104 } 89 }
105 90
106 itr->second.func(this); 91 itr->second.func(this);
107 92
108 return 0; // TODO: Implement return from actual function 93 return MakeResult<bool>(false); // TODO: Implement return from actual function
109 }
110
111 /**
112 * Wait for kernel object to synchronize
113 * @param wait Boolean wait set if current thread should wait as a result of sync operation
114 * @return Result of operation, 0 on success, otherwise error code
115 */
116 Result WaitSynchronization(bool* wait) override {
117 // TODO(bunnei): ImplementMe
118 ERROR_LOG(OSHLE, "unimplemented function");
119 return 0;
120 } 94 }
121 95
122protected: 96protected:
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
index e27a2b1fe..d5590a683 100644
--- a/src/core/hle/service/soc_u.h
+++ b/src/core/hle/service/soc_u.h
@@ -19,7 +19,7 @@ public:
19 * Gets the string port name used by CTROS for the service 19 * Gets the string port name used by CTROS for the service
20 * @return Port name of service 20 * @return Port name of service
21 */ 21 */
22 std::string GetPortName() const { 22 std::string GetPortName() const override {
23 return "soc:U"; 23 return "soc:U";
24 } 24 }
25}; 25};
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 6c02a43d9..165fd7aac 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -11,20 +11,20 @@
11 11
12namespace SRV { 12namespace SRV {
13 13
14Handle g_event_handle = 0; 14static Handle g_event_handle = 0;
15 15
16void Initialize(Service::Interface* self) { 16static void Initialize(Service::Interface* self) {
17 DEBUG_LOG(OSHLE, "called"); 17 LOG_DEBUG(Service_SRV, "called");
18 18
19 u32* cmd_buff = Service::GetCommandBuffer(); 19 u32* cmd_buff = Kernel::GetCommandBuffer();
20 20
21 cmd_buff[1] = 0; // No error 21 cmd_buff[1] = 0; // No error
22} 22}
23 23
24void GetProcSemaphore(Service::Interface* self) { 24static void GetProcSemaphore(Service::Interface* self) {
25 DEBUG_LOG(OSHLE, "called"); 25 LOG_TRACE(Service_SRV, "called");
26 26
27 u32* cmd_buff = Service::GetCommandBuffer(); 27 u32* cmd_buff = Kernel::GetCommandBuffer();
28 28
29 // TODO(bunnei): Change to a semaphore once these have been implemented 29 // TODO(bunnei): Change to a semaphore once these have been implemented
30 g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); 30 g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event");
@@ -34,21 +34,21 @@ void GetProcSemaphore(Service::Interface* self) {
34 cmd_buff[3] = g_event_handle; 34 cmd_buff[3] = g_event_handle;
35} 35}
36 36
37void GetServiceHandle(Service::Interface* self) { 37static void GetServiceHandle(Service::Interface* self) {
38 Result res = 0; 38 ResultCode res = RESULT_SUCCESS;
39 u32* cmd_buff = Service::GetCommandBuffer(); 39 u32* cmd_buff = Kernel::GetCommandBuffer();
40 40
41 std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); 41 std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize);
42 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); 42 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
43 43
44 if (nullptr != service) { 44 if (nullptr != service) {
45 cmd_buff[3] = service->GetHandle(); 45 cmd_buff[3] = service->GetHandle();
46 DEBUG_LOG(OSHLE, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); 46 LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]);
47 } else { 47 } else {
48 ERROR_LOG(OSHLE, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); 48 LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str());
49 res = -1; 49 res = UnimplementedFunction(ErrorModule::SRV);
50 } 50 }
51 cmd_buff[1] = res; 51 cmd_buff[1] = res.raw;
52} 52}
53 53
54const Interface::FunctionInfo FunctionTable[] = { 54const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 2aa1303f6..47e9bf77e 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
6 6
@@ -12,10 +12,12 @@
12#include "core/hle/kernel/address_arbiter.h" 12#include "core/hle/kernel/address_arbiter.h"
13#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
14#include "core/hle/kernel/mutex.h" 14#include "core/hle/kernel/mutex.h"
15#include "core/hle/kernel/semaphore.h"
15#include "core/hle/kernel/shared_memory.h" 16#include "core/hle/kernel/shared_memory.h"
16#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
17 18
18#include "core/hle/function_wrappers.h" 19#include "core/hle/function_wrappers.h"
20#include "core/hle/result.h"
19#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
20 22
21//////////////////////////////////////////////////////////////////////////////////////////////////// 23////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -29,8 +31,8 @@ enum ControlMemoryOperation {
29}; 31};
30 32
31/// Map application or GSP heap memory 33/// Map application or GSP heap memory
32Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { 34static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
33 DEBUG_LOG(SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", 35 LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X",
34 operation, addr0, addr1, size, permissions); 36 operation, addr0, addr1, size, permissions);
35 37
36 switch (operation) { 38 switch (operation) {
@@ -42,19 +44,19 @@ Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 siz
42 44
43 // Map GSP heap memory 45 // Map GSP heap memory
44 case MEMORY_OPERATION_GSP_HEAP: 46 case MEMORY_OPERATION_GSP_HEAP:
45 *out_addr = Memory::MapBlock_HeapGSP(size, operation, permissions); 47 *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions);
46 break; 48 break;
47 49
48 // Unknown ControlMemory operation 50 // Unknown ControlMemory operation
49 default: 51 default:
50 ERROR_LOG(SVC, "unknown operation=0x%08X", operation); 52 LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
51 } 53 }
52 return 0; 54 return 0;
53} 55}
54 56
55/// Maps a memory block to specified address 57/// Maps a memory block to specified address
56Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { 58static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
57 DEBUG_LOG(SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", 59 LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
58 handle, addr, permissions, other_permissions); 60 handle, addr, permissions, other_permissions);
59 61
60 Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions); 62 Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions);
@@ -67,20 +69,20 @@ Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permis
67 case Kernel::MemoryPermission::WriteExecute: 69 case Kernel::MemoryPermission::WriteExecute:
68 case Kernel::MemoryPermission::ReadWriteExecute: 70 case Kernel::MemoryPermission::ReadWriteExecute:
69 case Kernel::MemoryPermission::DontCare: 71 case Kernel::MemoryPermission::DontCare:
70 Kernel::MapSharedMemory(handle, addr, permissions_type, 72 Kernel::MapSharedMemory(handle, addr, permissions_type,
71 static_cast<Kernel::MemoryPermission>(other_permissions)); 73 static_cast<Kernel::MemoryPermission>(other_permissions));
72 break; 74 break;
73 default: 75 default:
74 ERROR_LOG(OSHLE, "unknown permissions=0x%08X", permissions); 76 LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
75 } 77 }
76 return 0; 78 return 0;
77} 79}
78 80
79/// Connect to an OS service given the port name, returns the handle to the port to out 81/// Connect to an OS service given the port name, returns the handle to the port to out
80Result ConnectToPort(Handle* out, const char* port_name) { 82static Result ConnectToPort(Handle* out, const char* port_name) {
81 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); 83 Service::Interface* service = Service::g_manager->FetchFromPortName(port_name);
82 84
83 DEBUG_LOG(SVC, "called port_name=%s", port_name); 85 LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
84 _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!"); 86 _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!");
85 87
86 *out = service->GetHandle(); 88 *out = service->GetHandle();
@@ -89,78 +91,80 @@ Result ConnectToPort(Handle* out, const char* port_name) {
89} 91}
90 92
91/// Synchronize to an OS service 93/// Synchronize to an OS service
92Result SendSyncRequest(Handle handle) { 94static Result SendSyncRequest(Handle handle) {
93 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); 95 Kernel::Session* session = Kernel::g_object_pool.Get<Kernel::Session>(handle);
96 if (session == nullptr) {
97 return InvalidHandle(ErrorModule::Kernel).raw;
98 }
94 99
95 _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!"); 100 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
96 DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName().c_str());
97 101
98 bool wait = false; 102 ResultVal<bool> wait = session->SyncRequest();
99 Result res = object->SyncRequest(&wait); 103 if (wait.Succeeded() && *wait) {
100 if (wait) {
101 Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? 104 Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
102 } 105 }
103 106
104 return res; 107 return wait.Code().raw;
105} 108}
106 109
107/// Close a handle 110/// Close a handle
108Result CloseHandle(Handle handle) { 111static Result CloseHandle(Handle handle) {
109 // ImplementMe 112 // ImplementMe
110 ERROR_LOG(SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle); 113 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle);
111 return 0; 114 return 0;
112} 115}
113 116
114/// Wait for a handle to synchronize, timeout after the specified nanoseconds 117/// Wait for a handle to synchronize, timeout after the specified nanoseconds
115Result WaitSynchronization1(Handle handle, s64 nano_seconds) { 118static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
116 // TODO(bunnei): Do something with nano_seconds, currently ignoring this 119 // TODO(bunnei): Do something with nano_seconds, currently ignoring this
117 bool wait = false;
118 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated 120 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
119 121
122 if (!Kernel::g_object_pool.IsValid(handle)) {
123 return InvalidHandle(ErrorModule::Kernel).raw;
124 }
120 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); 125 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
126 _dbg_assert_(Kernel, object != nullptr);
121 127
122 DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), 128 LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
123 object->GetName().c_str(), nano_seconds); 129 object->GetName().c_str(), nano_seconds);
124 130
125 _assert_msg_(KERNEL, (object != nullptr), "called, but kernel object is nullptr!"); 131 ResultVal<bool> wait = object->WaitSynchronization();
126
127 Result res = object->WaitSynchronization(&wait);
128 132
129 // Check for next thread to schedule 133 // Check for next thread to schedule
130 if (wait) { 134 if (wait.Succeeded() && *wait) {
131 HLE::Reschedule(__func__); 135 HLE::Reschedule(__func__);
132 return 0;
133 } 136 }
134 137
135 return res; 138 return wait.Code().raw;
136} 139}
137 140
138/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 141/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
139Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, 142static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
140 s64 nano_seconds) { 143 s64 nano_seconds) {
141 // TODO(bunnei): Do something with nano_seconds, currently ignoring this 144 // TODO(bunnei): Do something with nano_seconds, currently ignoring this
142 bool unlock_all = true; 145 bool unlock_all = true;
143 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated 146 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
144 147
145 DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", 148 LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld",
146 handle_count, (wait_all ? "true" : "false"), nano_seconds); 149 handle_count, (wait_all ? "true" : "false"), nano_seconds);
147 150
148 // Iterate through each handle, synchronize kernel object 151 // Iterate through each handle, synchronize kernel object
149 for (s32 i = 0; i < handle_count; i++) { 152 for (s32 i = 0; i < handle_count; i++) {
150 bool wait = false; 153 if (!Kernel::g_object_pool.IsValid(handles[i])) {
154 return InvalidHandle(ErrorModule::Kernel).raw;
155 }
151 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]); 156 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handles[i]);
152 157
153 _assert_msg_(KERNEL, (object != nullptr), "called handle=0x%08X, but kernel object " 158 LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
154 "is nullptr!", handles[i]);
155
156 DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
157 object->GetName().c_str()); 159 object->GetName().c_str());
158 160
159 Result res = object->WaitSynchronization(&wait); 161 // TODO(yuriks): Verify how the real function behaves when an error happens here
162 ResultVal<bool> wait_result = object->WaitSynchronization();
163 bool wait = wait_result.Succeeded() && *wait_result;
160 164
161 if (!wait && !wait_all) { 165 if (!wait && !wait_all) {
162 *out = i; 166 *out = i;
163 return 0; 167 return RESULT_SUCCESS.raw;
164 } else { 168 } else {
165 unlock_all = false; 169 unlock_all = false;
166 } 170 }
@@ -168,55 +172,57 @@ Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wa
168 172
169 if (wait_all && unlock_all) { 173 if (wait_all && unlock_all) {
170 *out = handle_count; 174 *out = handle_count;
171 return 0; 175 return RESULT_SUCCESS.raw;
172 } 176 }
173 177
174 // Check for next thread to schedule 178 // Check for next thread to schedule
175 HLE::Reschedule(__func__); 179 HLE::Reschedule(__func__);
176 180
177 return 0; 181 return RESULT_SUCCESS.raw;
178} 182}
179 183
180/// Create an address arbiter (to allocate access to shared resources) 184/// Create an address arbiter (to allocate access to shared resources)
181Result CreateAddressArbiter(u32* arbiter) { 185static Result CreateAddressArbiter(u32* arbiter) {
182 DEBUG_LOG(SVC, "called"); 186 LOG_TRACE(Kernel_SVC, "called");
183 Handle handle = Kernel::CreateAddressArbiter(); 187 Handle handle = Kernel::CreateAddressArbiter();
184 *arbiter = handle; 188 *arbiter = handle;
185 return 0; 189 return 0;
186} 190}
187 191
188/// Arbitrate address 192/// Arbitrate address
189Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) { 193static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) {
190 return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), address, 194 LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter,
191 value); 195 address, type, value);
196 return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
197 address, value).raw;
192} 198}
193 199
194/// Used to output a message on a debug hardware unit - does nothing on a retail unit 200/// Used to output a message on a debug hardware unit - does nothing on a retail unit
195void OutputDebugString(const char* string) { 201static void OutputDebugString(const char* string) {
196 OS_LOG(SVC, "%s", string); 202 LOG_DEBUG(Debug_Emulated, "%s", string);
197} 203}
198 204
199/// Get resource limit 205/// Get resource limit
200Result GetResourceLimit(Handle* resource_limit, Handle process) { 206static Result GetResourceLimit(Handle* resource_limit, Handle process) {
201 // With regards to proceess values: 207 // With regards to proceess values:
202 // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for 208 // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for
203 // the current KThread. 209 // the current KThread.
204 *resource_limit = 0xDEADBEEF; 210 *resource_limit = 0xDEADBEEF;
205 ERROR_LOG(SVC, "(UNIMPLEMENTED) called process=0x%08X", process); 211 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process);
206 return 0; 212 return 0;
207} 213}
208 214
209/// Get resource limit current values 215/// Get resource limit current values
210Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, 216static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names,
211 s32 name_count) { 217 s32 name_count) {
212 ERROR_LOG(SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", 218 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d",
213 resource_limit, names, name_count); 219 resource_limit, names, name_count);
214 Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now 220 Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now
215 return 0; 221 return 0;
216} 222}
217 223
218/// Creates a new thread 224/// Creates a new thread
219Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { 225static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
220 std::string name; 226 std::string name;
221 if (Symbols::HasSymbol(entry_point)) { 227 if (Symbols::HasSymbol(entry_point)) {
222 TSymbol symbol = Symbols::GetSymbol(entry_point); 228 TSymbol symbol = Symbols::GetSymbol(entry_point);
@@ -230,18 +236,18 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p
230 236
231 Core::g_app_core->SetReg(1, thread); 237 Core::g_app_core->SetReg(1, thread);
232 238
233 DEBUG_LOG(SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " 239 LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
234 "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, 240 "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
235 name.c_str(), arg, stack_top, priority, processor_id, thread); 241 name.c_str(), arg, stack_top, priority, processor_id, thread);
236 242
237 return 0; 243 return 0;
238} 244}
239 245
240/// Called when a thread exits 246/// Called when a thread exits
241u32 ExitThread() { 247static u32 ExitThread() {
242 Handle thread = Kernel::GetCurrentThreadHandle(); 248 Handle thread = Kernel::GetCurrentThreadHandle();
243 249
244 DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C 250 LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
245 251
246 Kernel::StopThread(thread, __func__); 252 Kernel::StopThread(thread, __func__);
247 HLE::Reschedule(__func__); 253 HLE::Reschedule(__func__);
@@ -249,55 +255,73 @@ u32 ExitThread() {
249} 255}
250 256
251/// Gets the priority for the specified thread 257/// Gets the priority for the specified thread
252Result GetThreadPriority(s32* priority, Handle handle) { 258static Result GetThreadPriority(s32* priority, Handle handle) {
253 *priority = Kernel::GetThreadPriority(handle); 259 ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle);
254 return 0; 260 if (priority_result.Succeeded()) {
261 *priority = *priority_result;
262 }
263 return priority_result.Code().raw;
255} 264}
256 265
257/// Sets the priority for the specified thread 266/// Sets the priority for the specified thread
258Result SetThreadPriority(Handle handle, s32 priority) { 267static Result SetThreadPriority(Handle handle, s32 priority) {
259 return Kernel::SetThreadPriority(handle, priority); 268 return Kernel::SetThreadPriority(handle, priority).raw;
260} 269}
261 270
262/// Create a mutex 271/// Create a mutex
263Result CreateMutex(Handle* mutex, u32 initial_locked) { 272static Result CreateMutex(Handle* mutex, u32 initial_locked) {
264 *mutex = Kernel::CreateMutex((initial_locked != 0)); 273 *mutex = Kernel::CreateMutex((initial_locked != 0));
265 DEBUG_LOG(SVC, "called initial_locked=%s : created handle=0x%08X", 274 LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
266 initial_locked ? "true" : "false", *mutex); 275 initial_locked ? "true" : "false", *mutex);
267 return 0; 276 return 0;
268} 277}
269 278
270/// Release a mutex 279/// Release a mutex
271Result ReleaseMutex(Handle handle) { 280static Result ReleaseMutex(Handle handle) {
272 DEBUG_LOG(SVC, "called handle=0x%08X", handle); 281 LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle);
273 _assert_msg_(KERNEL, (handle != 0), "called, but handle is nullptr!"); 282 ResultCode res = Kernel::ReleaseMutex(handle);
274 Kernel::ReleaseMutex(handle); 283 return res.raw;
275 return 0;
276} 284}
277 285
278/// Get current thread ID 286/// Get the ID for the specified thread.
279Result GetThreadId(u32* thread_id, Handle thread) { 287static Result GetThreadId(u32* thread_id, Handle handle) {
280 ERROR_LOG(SVC, "(UNIMPLEMENTED) called thread=0x%08X", thread); 288 LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
281 return 0; 289 ResultCode result = Kernel::GetThreadId(thread_id, handle);
290 return result.raw;
291}
292
293/// Creates a semaphore
294static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) {
295 ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count);
296 LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
297 initial_count, max_count, *semaphore);
298 return res.raw;
299}
300
301/// Releases a certain number of slots in a semaphore
302static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) {
303 LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore);
304 ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count);
305 return res.raw;
282} 306}
283 307
284/// Query memory 308/// Query memory
285Result QueryMemory(void* info, void* out, u32 addr) { 309static Result QueryMemory(void* info, void* out, u32 addr) {
286 ERROR_LOG(SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); 310 LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr);
287 return 0; 311 return 0;
288} 312}
289 313
290/// Create an event 314/// Create an event
291Result CreateEvent(Handle* evt, u32 reset_type) { 315static Result CreateEvent(Handle* evt, u32 reset_type) {
292 *evt = Kernel::CreateEvent((ResetType)reset_type); 316 *evt = Kernel::CreateEvent((ResetType)reset_type);
293 DEBUG_LOG(SVC, "called reset_type=0x%08X : created handle=0x%08X", 317 LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
294 reset_type, *evt); 318 reset_type, *evt);
295 return 0; 319 return 0;
296} 320}
297 321
298/// Duplicates a kernel handle 322/// Duplicates a kernel handle
299Result DuplicateHandle(Handle* out, Handle handle) { 323static Result DuplicateHandle(Handle* out, Handle handle) {
300 DEBUG_LOG(SVC, "called handle=0x%08X", handle); 324 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle=0x%08X", handle);
301 325
302 // Translate kernel handles -> real handles 326 // Translate kernel handles -> real handles
303 if (handle == Kernel::CurrentThread) { 327 if (handle == Kernel::CurrentThread) {
@@ -305,7 +329,7 @@ Result DuplicateHandle(Handle* out, Handle handle) {
305 } 329 }
306 _assert_msg_(KERNEL, (handle != Kernel::CurrentProcess), 330 _assert_msg_(KERNEL, (handle != Kernel::CurrentProcess),
307 "(UNIMPLEMENTED) process handle duplication!"); 331 "(UNIMPLEMENTED) process handle duplication!");
308 332
309 // TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate. 333 // TODO(bunnei): FixMe - This is a hack to return the handle that we were asked to duplicate.
310 *out = handle; 334 *out = handle;
311 335
@@ -313,26 +337,27 @@ Result DuplicateHandle(Handle* out, Handle handle) {
313} 337}
314 338
315/// Signals an event 339/// Signals an event
316Result SignalEvent(Handle evt) { 340static Result SignalEvent(Handle evt) {
317 Result res = Kernel::SignalEvent(evt); 341 LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
318 DEBUG_LOG(SVC, "called event=0x%08X", evt); 342 return Kernel::SignalEvent(evt).raw;
319 return res;
320} 343}
321 344
322/// Clears an event 345/// Clears an event
323Result ClearEvent(Handle evt) { 346static Result ClearEvent(Handle evt) {
324 Result res = Kernel::ClearEvent(evt); 347 LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt);
325 DEBUG_LOG(SVC, "called event=0x%08X", evt); 348 return Kernel::ClearEvent(evt).raw;
326 return res;
327} 349}
328 350
329/// Sleep the current thread 351/// Sleep the current thread
330void SleepThread(s64 nanoseconds) { 352static void SleepThread(s64 nanoseconds) {
331 DEBUG_LOG(SVC, "called nanoseconds=%lld", nanoseconds); 353 LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
354
355 // Check for next thread to schedule
356 HLE::Reschedule(__func__);
332} 357}
333 358
334/// This returns the total CPU ticks elapsed since the CPU was powered-on 359/// This returns the total CPU ticks elapsed since the CPU was powered-on
335s64 GetSystemTick() { 360static s64 GetSystemTick() {
336 return (s64)Core::g_app_core->GetTicks(); 361 return (s64)Core::g_app_core->GetTicks();
337} 362}
338 363
@@ -358,8 +383,8 @@ const HLE::FunctionDef SVC_Table[] = {
358 {0x12, nullptr, "Run"}, 383 {0x12, nullptr, "Run"},
359 {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"}, 384 {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
360 {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"}, 385 {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
361 {0x15, nullptr, "CreateSemaphore"}, 386 {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"},
362 {0x16, nullptr, "ReleaseSemaphore"}, 387 {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"},
363 {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, 388 {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
364 {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, 389 {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
365 {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, 390 {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 1d125faf6..6be393d0b 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -1,6 +1,6 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 3ad801c63..da78b85e5 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) {
35 35
36 // Reads other than u32 are untested, so I'd rather have them abort than silently fail 36 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
37 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { 37 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
38 ERROR_LOG(GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); 38 LOG_ERROR(HW_GPU, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
39 return; 39 return;
40 } 40 }
41 41
@@ -49,7 +49,7 @@ inline void Write(u32 addr, const T data) {
49 49
50 // Writes other than u32 are untested, so I'd rather have them abort than silently fail 50 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
51 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) { 51 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
52 ERROR_LOG(GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); 52 LOG_ERROR(HW_GPU, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
53 return; 53 return;
54 } 54 }
55 55
@@ -73,7 +73,7 @@ inline void Write(u32 addr, const T data) {
73 for (u32* ptr = start; ptr < end; ++ptr) 73 for (u32* ptr = start; ptr < end; ++ptr)
74 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation 74 *ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
75 75
76 DEBUG_LOG(GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); 76 LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
77 } 77 }
78 break; 78 break;
79 } 79 }
@@ -105,7 +105,7 @@ inline void Write(u32 addr, const T data) {
105 } 105 }
106 106
107 default: 107 default:
108 ERROR_LOG(GPU, "Unknown source framebuffer format %x", config.input_format.Value()); 108 LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", config.input_format.Value());
109 break; 109 break;
110 } 110 }
111 111
@@ -132,16 +132,16 @@ inline void Write(u32 addr, const T data) {
132 } 132 }
133 133
134 default: 134 default:
135 ERROR_LOG(GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); 135 LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value());
136 break; 136 break;
137 } 137 }
138 } 138 }
139 } 139 }
140 140
141 DEBUG_LOG(GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x", 141 LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
142 config.output_height * config.output_width * 4, 142 config.output_height * config.output_width * 4,
143 config.GetPhysicalInputAddress(), config.input_width, config.input_height, 143 config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
144 config.GetPhysicalOutputAddress(), config.output_width, config.output_height, 144 config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height,
145 config.output_format.Value()); 145 config.output_format.Value());
146 } 146 }
147 break; 147 break;
@@ -154,8 +154,7 @@ inline void Write(u32 addr, const T data) {
154 if (config.trigger & 1) 154 if (config.trigger & 1)
155 { 155 {
156 u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress())); 156 u32* buffer = (u32*)Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalAddress()));
157 u32 size = config.size << 3; 157 Pica::CommandProcessor::ProcessCommandList(buffer, config.size);
158 Pica::CommandProcessor::ProcessCommandList(buffer, size);
159 } 158 }
160 break; 159 break;
161 } 160 }
@@ -252,12 +251,12 @@ void Init() {
252 framebuffer_sub.color_format = Regs::PixelFormat::RGB8; 251 framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
253 framebuffer_sub.active_fb = 0; 252 framebuffer_sub.active_fb = 0;
254 253
255 NOTICE_LOG(GPU, "initialized OK"); 254 LOG_DEBUG(HW_GPU, "initialized OK");
256} 255}
257 256
258/// Shutdown hardware 257/// Shutdown hardware
259void Shutdown() { 258void Shutdown() {
260 NOTICE_LOG(GPU, "shutdown OK"); 259 LOG_DEBUG(HW_GPU, "shutdown OK");
261} 260}
262 261
263} // namespace 262} // namespace
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 3fa7b9ccf..86cd5e680 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -169,7 +169,7 @@ struct Regs {
169 INSERT_PADDING_WORDS(0x331); 169 INSERT_PADDING_WORDS(0x331);
170 170
171 struct { 171 struct {
172 // command list size 172 // command list size (in bytes)
173 u32 size; 173 u32 size;
174 174
175 INSERT_PADDING_WORDS(0x1); 175 INSERT_PADDING_WORDS(0x1);
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 33f75c50a..af42b41fb 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -6,7 +6,6 @@
6 6
7#include "core/hw/hw.h" 7#include "core/hw/hw.h"
8#include "core/hw/gpu.h" 8#include "core/hw/gpu.h"
9#include "core/hw/ndma.h"
10 9
11namespace HW { 10namespace HW {
12 11
@@ -39,36 +38,26 @@ enum {
39template <typename T> 38template <typename T>
40inline void Read(T &var, const u32 addr) { 39inline void Read(T &var, const u32 addr) {
41 switch (addr & 0xFFFFF000) { 40 switch (addr & 0xFFFFF000) {
42
43 // TODO(bunnei): What is the virtual address of NDMA?
44 // case VADDR_NDMA:
45 // NDMA::Read(var, addr);
46 // break;
47 41
48 case VADDR_GPU: 42 case VADDR_GPU:
49 GPU::Read(var, addr); 43 GPU::Read(var, addr);
50 break; 44 break;
51 45
52 default: 46 default:
53 ERROR_LOG(HW, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); 47 LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
54 } 48 }
55} 49}
56 50
57template <typename T> 51template <typename T>
58inline void Write(u32 addr, const T data) { 52inline void Write(u32 addr, const T data) {
59 switch (addr & 0xFFFFF000) { 53 switch (addr & 0xFFFFF000) {
60
61 // TODO(bunnei): What is the virtual address of NDMA?
62 // case VADDR_NDMA
63 // NDMA::Write(addr, data);
64 // break;
65 54
66 case VADDR_GPU: 55 case VADDR_GPU:
67 GPU::Write(addr, data); 56 GPU::Write(addr, data);
68 break; 57 break;
69 58
70 default: 59 default:
71 ERROR_LOG(HW, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); 60 LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, addr);
72 } 61 }
73} 62}
74 63
@@ -87,19 +76,17 @@ template void Write<u8>(u32 addr, const u8 data);
87/// Update hardware 76/// Update hardware
88void Update() { 77void Update() {
89 GPU::Update(); 78 GPU::Update();
90 NDMA::Update();
91} 79}
92 80
93/// Initialize hardware 81/// Initialize hardware
94void Init() { 82void Init() {
95 GPU::Init(); 83 GPU::Init();
96 NDMA::Init(); 84 LOG_DEBUG(HW, "initialized OK");
97 NOTICE_LOG(HW, "initialized OK");
98} 85}
99 86
100/// Shutdown hardware 87/// Shutdown hardware
101void Shutdown() { 88void Shutdown() {
102 NOTICE_LOG(HW, "shutdown OK"); 89 LOG_DEBUG(HW, "shutdown OK");
103} 90}
104 91
105} \ No newline at end of file 92} \ 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 e29a773f1..000000000
--- a/src/core/hw/ndma.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6
7#include "core/hw/ndma.h"
8
9namespace NDMA {
10
11template <typename T>
12inline void Read(T &var, const u32 addr) {
13 ERROR_LOG(NDMA, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr);
14}
15
16template <typename T>
17inline void Write(u32 addr, const T data) {
18 ERROR_LOG(NDMA, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
19}
20
21// Explicitly instantiate template functions because we aren't defining this in the header:
22
23template void Read<u64>(u64 &var, const u32 addr);
24template void Read<u32>(u32 &var, const u32 addr);
25template void Read<u16>(u16 &var, const u32 addr);
26template void Read<u8>(u8 &var, const u32 addr);
27
28template void Write<u64>(u32 addr, const u64 data);
29template void Write<u32>(u32 addr, const u32 data);
30template void Write<u16>(u32 addr, const u16 data);
31template void Write<u8>(u32 addr, const u8 data);
32
33/// Update hardware
34void Update() {
35}
36
37/// Initialize hardware
38void Init() {
39 NOTICE_LOG(GPU, "initialized OK");
40}
41
42/// Shutdown hardware
43void Shutdown() {
44 NOTICE_LOG(GPU, "shutdown OK");
45}
46
47} // namespace
diff --git a/src/core/hw/ndma.h b/src/core/hw/ndma.h
deleted file mode 100644
index d8fa3d40b..000000000
--- a/src/core/hw/ndma.h
+++ /dev/null
@@ -1,26 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace NDMA {
10
11template <typename T>
12inline void Read(T &var, const u32 addr);
13
14template <typename T>
15inline void Write(u32 addr, const T data);
16
17/// Update hardware
18void Update();
19
20/// Initialize hardware
21void Init();
22
23/// Shutdown hardware
24void Shutdown();
25
26} // namespace
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
new file mode 100644
index 000000000..0437e5374
--- /dev/null
+++ b/src/core/loader/3dsx.cpp
@@ -0,0 +1,236 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <vector>
7
8#include "core/file_sys/archive_romfs.h"
9#include "core/loader/elf.h"
10#include "core/loader/ncch.h"
11#include "core/hle/service/fs/archive.h"
12#include "core/mem_map.h"
13
14#include "3dsx.h"
15
16
17namespace Loader {
18
19
20/**
21 * File layout:
22 * - File header
23 * - Code, rodata and data relocation table headers
24 * - Code segment
25 * - Rodata segment
26 * - Loadable (non-BSS) part of the data segment
27 * - Code relocation table
28 * - Rodata relocation table
29 * - Data relocation table
30 *
31 * Memory layout before relocations are applied:
32 * [0..codeSegSize) -> code segment
33 * [codeSegSize..rodataSegSize) -> rodata segment
34 * [rodataSegSize..dataSegSize) -> data segment
35 *
36 * Memory layout after relocations are applied: well, however the loader sets it up :)
37 * The entrypoint is always the start of the code segment.
38 * The BSS section must be cleared manually by the application.
39 */
40enum THREEDSX_Error {
41 ERROR_NONE = 0,
42 ERROR_READ = 1,
43 ERROR_FILE = 2,
44 ERROR_ALLOC = 3
45};
46static const u32 RELOCBUFSIZE = 512;
47
48// File header
49static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX'
50#pragma pack(1)
51struct THREEDSX_Header
52{
53 u32 magic;
54 u16 header_size, reloc_hdr_size;
55 u32 format_ver;
56 u32 flags;
57
58 // Sizes of the code, rodata and data segments +
59 // size of the BSS section (uninitialized latter half of the data segment)
60 u32 code_seg_size, rodata_seg_size, data_seg_size, bss_size;
61};
62
63// Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts.
64struct THREEDSX_RelocHdr
65{
66 // # of absolute relocations (that is, fix address to post-relocation memory layout)
67 u32 cross_segment_absolute;
68 // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
69 u32 cross_segment_relative;
70 // more?
71
72 // Relocations are written in this order:
73 // - Absolute relocations
74 // - Relative relocations
75};
76
77// Relocation entry: from the current pointer, skip X words and patch Y words
78struct THREEDSX_Reloc
79{
80 u16 skip, patch;
81};
82#pragma pack()
83
84struct THREEloadinfo
85{
86 u8* seg_ptrs[3]; // code, rodata & data
87 u32 seg_addrs[3];
88 u32 seg_sizes[3];
89};
90
91class THREEDSXReader {
92public:
93 static int Load3DSXFile(const std::string& filename, u32 base_addr);
94};
95
96static u32 TranslateAddr(u32 addr, THREEloadinfo *loadinfo, u32* offsets)
97{
98 if (addr < offsets[0])
99 return loadinfo->seg_addrs[0] + addr;
100 if (addr < offsets[1])
101 return loadinfo->seg_addrs[1] + addr - offsets[0];
102 return loadinfo->seg_addrs[2] + addr - offsets[1];
103}
104
105int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr)
106{
107 FileUtil::IOFile file(filename, "rb");
108 if (!file.IsOpen()) {
109 return ERROR_FILE;
110 }
111 THREEDSX_Header hdr;
112 if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr))
113 return ERROR_READ;
114
115 THREEloadinfo loadinfo;
116 //loadinfo segments must be a multiple of 0x1000
117 loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) &~0xFFF;
118 loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) &~0xFFF;
119 loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) &~0xFFF;
120 u32 offsets[2] = { loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] };
121 u32 data_load_size = (hdr.data_seg_size - hdr.bss_size + 0xFFF) &~0xFFF;
122 u32 bss_load_size = loadinfo.seg_sizes[2] - data_load_size;
123 u32 n_reloc_tables = hdr.reloc_hdr_size / 4;
124 std::vector<u8> all_mem(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2] + 3 * n_reloc_tables);
125
126 loadinfo.seg_addrs[0] = base_addr;
127 loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0];
128 loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1];
129 loadinfo.seg_ptrs[0] = &all_mem[0];
130 loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0];
131 loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1];
132
133 // Skip header for future compatibility
134 file.Seek(hdr.header_size, SEEK_SET);
135
136 // Read the relocation headers
137 u32* relocs = (u32*)(loadinfo.seg_ptrs[2] + hdr.data_seg_size);
138
139 for (u32 current_segment = 0; current_segment < 3; current_segment++) {
140 if (file.ReadBytes(&relocs[current_segment*n_reloc_tables], n_reloc_tables * 4) != n_reloc_tables * 4)
141 return ERROR_READ;
142 }
143
144 // Read the segments
145 if (file.ReadBytes(loadinfo.seg_ptrs[0], hdr.code_seg_size) != hdr.code_seg_size)
146 return ERROR_READ;
147 if (file.ReadBytes(loadinfo.seg_ptrs[1], hdr.rodata_seg_size) != hdr.rodata_seg_size)
148 return ERROR_READ;
149 if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) != hdr.data_seg_size - hdr.bss_size)
150 return ERROR_READ;
151
152 // BSS clear
153 memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size);
154
155 // Relocate the segments
156 for (u32 current_segment = 0; current_segment < 3; current_segment++) {
157 for (u32 current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; current_segment_reloc_table++) {
158 u32 n_relocs = relocs[current_segment*n_reloc_tables + current_segment_reloc_table];
159 if (current_segment_reloc_table >= 2) {
160 // We are not using this table - ignore it because we don't know what it dose
161 file.Seek(n_relocs*sizeof(THREEDSX_Reloc), SEEK_CUR);
162 continue;
163 }
164 static THREEDSX_Reloc reloc_table[RELOCBUFSIZE];
165
166 u32* pos = (u32*)loadinfo.seg_ptrs[current_segment];
167 u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4);
168
169 while (n_relocs) {
170 u32 remaining = std::min(RELOCBUFSIZE, n_relocs);
171 n_relocs -= remaining;
172
173 if (file.ReadBytes(reloc_table, remaining*sizeof(THREEDSX_Reloc)) != remaining*sizeof(THREEDSX_Reloc))
174 return ERROR_READ;
175
176 for (u32 current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
177 LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n",
178 current_segment_reloc_table, (u32)reloc_table[current_inprogress].skip, (u32)reloc_table[current_inprogress].patch);
179 pos += reloc_table[current_inprogress].skip;
180 s32 num_patches = reloc_table[current_inprogress].patch;
181 while (0 < num_patches && pos < end_pos) {
182 u32 in_addr = (char*)pos - (char*)&all_mem[0];
183 u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
184 LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n",
185 base_addr + in_addr, addr, current_segment_reloc_table, *pos);
186 switch (current_segment_reloc_table) {
187 case 0: *pos = (addr); break;
188 case 1: *pos = (addr - in_addr); break;
189 default: break; //this should never happen
190 }
191 pos++;
192 num_patches--;
193 }
194 }
195 }
196 }
197 }
198
199 // Write the data
200 memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]);
201
202 LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000);
203 LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000);
204 LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000);
205 LOG_DEBUG(Loader, "BSS: %u pages\n", bss_load_size / 0x1000);
206
207 return ERROR_NONE;
208}
209
210 /// AppLoader_DSX constructor
211 AppLoader_THREEDSX::AppLoader_THREEDSX(const std::string& filename) : filename(filename) {
212 }
213
214 /// AppLoader_DSX destructor
215 AppLoader_THREEDSX::~AppLoader_THREEDSX() {
216 }
217
218 /**
219 * Loads a 3DSX file
220 * @return Success on success, otherwise Error
221 */
222 ResultStatus AppLoader_THREEDSX::Load() {
223 LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str());
224 FileUtil::IOFile file(filename, "rb");
225 if (file.IsOpen()) {
226
227 THREEDSXReader reader;
228 reader.Load3DSXFile(filename, 0x00100000);
229 Kernel::LoadExec(0x00100000);
230 } else {
231 return ResultStatus::Error;
232 }
233 return ResultStatus::Success;
234 }
235
236} // namespace Loader
diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h
new file mode 100644
index 000000000..848d3ef8a
--- /dev/null
+++ b/src/core/loader/3dsx.h
@@ -0,0 +1,32 @@
1// Copyright 2014 Dolphin Emulator Project / Citra Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/loader/loader.h"
9
10////////////////////////////////////////////////////////////////////////////////////////////////////
11// Loader namespace
12
13namespace Loader {
14
15/// Loads an 3DSX file
16class AppLoader_THREEDSX final : public AppLoader {
17public:
18 AppLoader_THREEDSX(const std::string& filename);
19 ~AppLoader_THREEDSX() override;
20
21 /**
22 * Load the bootable file
23 * @return ResultStatus result of function
24 */
25 ResultStatus Load() override;
26
27private:
28 std::string filename;
29 bool is_loaded;
30};
31
32} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 389d5a8c9..c95882f4a 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -254,18 +254,18 @@ const char *ElfReader::GetSectionName(int section) const {
254} 254}
255 255
256bool ElfReader::LoadInto(u32 vaddr) { 256bool ElfReader::LoadInto(u32 vaddr) {
257 DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); 257 LOG_DEBUG(Loader, "String section: %i", header->e_shstrndx);
258 258
259 // Should we relocate? 259 // Should we relocate?
260 relocate = (header->e_type != ET_EXEC); 260 relocate = (header->e_type != ET_EXEC);
261 261
262 if (relocate) { 262 if (relocate) {
263 DEBUG_LOG(MASTER_LOG, "Relocatable module"); 263 LOG_DEBUG(Loader, "Relocatable module");
264 entryPoint += vaddr; 264 entryPoint += vaddr;
265 } else { 265 } else {
266 DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); 266 LOG_DEBUG(Loader, "Prerelocated executable");
267 } 267 }
268 INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum); 268 LOG_DEBUG(Loader, "%i segments:", header->e_phnum);
269 269
270 // First pass : Get the bits into RAM 270 // First pass : Get the bits into RAM
271 u32 segment_addr[32]; 271 u32 segment_addr[32];
@@ -273,17 +273,17 @@ bool ElfReader::LoadInto(u32 vaddr) {
273 273
274 for (int i = 0; i < header->e_phnum; i++) { 274 for (int i = 0; i < header->e_phnum; i++) {
275 Elf32_Phdr *p = segments + i; 275 Elf32_Phdr *p = segments + i;
276 INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, 276 LOG_DEBUG(Loader, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr,
277 p->p_filesz, p->p_memsz); 277 p->p_filesz, p->p_memsz);
278 278
279 if (p->p_type == PT_LOAD) { 279 if (p->p_type == PT_LOAD) {
280 segment_addr[i] = base_addr + p->p_vaddr; 280 segment_addr[i] = base_addr + p->p_vaddr;
281 memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); 281 memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz);
282 INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], 282 LOG_DEBUG(Loader, "Loadable Segment Copied to %08x, size %08x", segment_addr[i],
283 p->p_memsz); 283 p->p_memsz);
284 } 284 }
285 } 285 }
286 INFO_LOG(MASTER_LOG, "Done loading."); 286 LOG_DEBUG(Loader, "Done loading.");
287 return true; 287 return true;
288} 288}
289 289
@@ -346,7 +346,7 @@ AppLoader_ELF::~AppLoader_ELF() {
346 * @return True on success, otherwise false 346 * @return True on success, otherwise false
347 */ 347 */
348ResultStatus AppLoader_ELF::Load() { 348ResultStatus AppLoader_ELF::Load() {
349 INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str()); 349 LOG_INFO(Loader, "Loading ELF file %s...", filename.c_str());
350 350
351 if (is_loaded) 351 if (is_loaded)
352 return ResultStatus::ErrorAlreadyLoaded; 352 return ResultStatus::ErrorAlreadyLoaded;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index a268e021a..480274d23 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -5,9 +5,10 @@
5#include <memory> 5#include <memory>
6 6
7#include "core/file_sys/archive_romfs.h" 7#include "core/file_sys/archive_romfs.h"
8#include "core/loader/3dsx.h"
8#include "core/loader/elf.h" 9#include "core/loader/elf.h"
9#include "core/loader/ncch.h" 10#include "core/loader/ncch.h"
10#include "core/hle/kernel/archive.h" 11#include "core/hle/service/fs/archive.h"
11#include "core/mem_map.h" 12#include "core/mem_map.h"
12 13
13//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -22,7 +23,7 @@ namespace Loader {
22 */ 23 */
23FileType IdentifyFile(const std::string &filename) { 24FileType IdentifyFile(const std::string &filename) {
24 if (filename.size() == 0) { 25 if (filename.size() == 0) {
25 ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); 26 LOG_ERROR(Loader, "invalid filename %s", filename.c_str());
26 return FileType::Error; 27 return FileType::Error;
27 } 28 }
28 29
@@ -42,6 +43,8 @@ FileType IdentifyFile(const std::string &filename) {
42 return FileType::CCI; 43 return FileType::CCI;
43 } else if (extension == ".bin") { 44 } else if (extension == ".bin") {
44 return FileType::BIN; 45 return FileType::BIN;
46 } else if (extension == ".3dsx") {
47 return FileType::THREEDSX;
45 } 48 }
46 return FileType::Unknown; 49 return FileType::Unknown;
47} 50}
@@ -52,10 +55,14 @@ FileType IdentifyFile(const std::string &filename) {
52 * @return ResultStatus result of function 55 * @return ResultStatus result of function
53 */ 56 */
54ResultStatus LoadFile(const std::string& filename) { 57ResultStatus LoadFile(const std::string& filename) {
55 INFO_LOG(LOADER, "Loading file %s...", filename.c_str()); 58 LOG_INFO(Loader, "Loading file %s...", filename.c_str());
56 59
57 switch (IdentifyFile(filename)) { 60 switch (IdentifyFile(filename)) {
58 61
62 //3DSX file format...
63 case FileType::THREEDSX:
64 return AppLoader_THREEDSX(filename).Load();
65
59 // Standard ELF file format... 66 // Standard ELF file format...
60 case FileType::ELF: 67 case FileType::ELF:
61 return AppLoader_ELF(filename).Load(); 68 return AppLoader_ELF(filename).Load();
@@ -67,7 +74,8 @@ ResultStatus LoadFile(const std::string& filename) {
67 74
68 // Load application and RomFS 75 // Load application and RomFS
69 if (ResultStatus::Success == app_loader.Load()) { 76 if (ResultStatus::Success == app_loader.Load()) {
70 Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS"); 77 Kernel::g_program_id = app_loader.GetProgramId();
78 Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
71 return ResultStatus::Success; 79 return ResultStatus::Success;
72 } 80 }
73 break; 81 break;
@@ -76,7 +84,7 @@ ResultStatus LoadFile(const std::string& filename) {
76 // Raw BIN file format... 84 // Raw BIN file format...
77 case FileType::BIN: 85 case FileType::BIN:
78 { 86 {
79 INFO_LOG(LOADER, "Loading BIN file %s...", filename.c_str()); 87 LOG_INFO(Loader, "Loading BIN file %s...", filename.c_str());
80 88
81 FileUtil::IOFile file(filename, "rb"); 89 FileUtil::IOFile file(filename, "rb");
82 90
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 68f843005..0f836d285 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -22,6 +22,7 @@ enum class FileType {
22 CIA, 22 CIA,
23 ELF, 23 ELF,
24 BIN, 24 BIN,
25 THREEDSX, //3DSX
25}; 26};
26 27
27/// Return type for functions in Loader namespace 28/// Return type for functions in Loader namespace
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 1e5501e6d..4d23656ec 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)
24 * @param size Size of compressed buffer 24 * @param size Size of compressed buffer
25 * @return Size of decompressed buffer 25 * @return Size of decompressed buffer
26 */ 26 */
27u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { 27static u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
28 u32 offset_size = *(u32*)(buffer + size - 4); 28 u32 offset_size = *(u32*)(buffer + size - 4);
29 return offset_size + size; 29 return offset_size + size;
30} 30}
@@ -37,7 +37,7 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
37 * @param decompressed_size Size of decompressed buffer 37 * @param decompressed_size Size of decompressed buffer
38 * @return True on success, otherwise false 38 * @return True on success, otherwise false
39 */ 39 */
40bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { 40static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
41 u8* footer = compressed + compressed_size - 8; 41 u8* footer = compressed + compressed_size - 8;
42 u32 buffer_top_and_bottom = *(u32*)footer; 42 u32 buffer_top_and_bottom = *(u32*)footer;
43 u32 out = decompressed_size; 43 u32 out = decompressed_size;
@@ -118,7 +118,7 @@ AppLoader_NCCH::~AppLoader_NCCH() {
118 * @return ResultStatus result of function 118 * @return ResultStatus result of function
119 */ 119 */
120ResultStatus AppLoader_NCCH::LoadExec() const { 120ResultStatus AppLoader_NCCH::LoadExec() const {
121 if (!is_loaded) 121 if (!is_loaded)
122 return ResultStatus::ErrorNotLoaded; 122 return ResultStatus::ErrorNotLoaded;
123 123
124 std::vector<u8> code; 124 std::vector<u8> code;
@@ -140,13 +140,13 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
140 // Iterate through the ExeFs archive until we find the .code file... 140 // Iterate through the ExeFs archive until we find the .code file...
141 FileUtil::IOFile file(filename, "rb"); 141 FileUtil::IOFile file(filename, "rb");
142 if (file.IsOpen()) { 142 if (file.IsOpen()) {
143 LOG_DEBUG(Loader, "%d sections:", kMaxSections);
143 for (int i = 0; i < kMaxSections; i++) { 144 for (int i = 0; i < kMaxSections; i++) {
144 // Load the specified section... 145 // Load the specified section...
145 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { 146 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) {
146 INFO_LOG(LOADER, "ExeFS section %d:", i); 147 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", i,
147 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); 148 exefs_header.section[i].offset, exefs_header.section[i].size,
148 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); 149 exefs_header.section[i].name);
149 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
150 150
151 s64 section_offset = (exefs_header.section[i].offset + exefs_offset + 151 s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
152 sizeof(ExeFs_Header)+ncch_offset); 152 sizeof(ExeFs_Header)+ncch_offset);
@@ -181,11 +181,11 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
181 } 181 }
182 } 182 }
183 } else { 183 } else {
184 ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str()); 184 LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
185 return ResultStatus::Error; 185 return ResultStatus::Error;
186 } 186 }
187 return ResultStatus::ErrorNotUsed; 187 return ResultStatus::ErrorNotUsed;
188} 188}
189 189
190/** 190/**
191 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) 191 * 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>&
194 * @return True on success, otherwise false 194 * @return True on success, otherwise false
195 */ 195 */
196ResultStatus AppLoader_NCCH::Load() { 196ResultStatus AppLoader_NCCH::Load() {
197 INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); 197 LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str());
198 198
199 if (is_loaded) 199 if (is_loaded)
200 return ResultStatus::ErrorAlreadyLoaded; 200 return ResultStatus::ErrorAlreadyLoaded;
@@ -205,12 +205,12 @@ ResultStatus AppLoader_NCCH::Load() {
205 205
206 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... 206 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
207 if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { 207 if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
208 WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); 208 LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
209 ncch_offset = 0x4000; 209 ncch_offset = 0x4000;
210 file.Seek(ncch_offset, 0); 210 file.Seek(ncch_offset, 0);
211 file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); 211 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
212 } 212 }
213 213
214 // Verify we are loading the correct file type... 214 // Verify we are loading the correct file type...
215 if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) 215 if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
216 return ResultStatus::ErrorInvalidFormat; 216 return ResultStatus::ErrorInvalidFormat;
@@ -222,17 +222,17 @@ ResultStatus AppLoader_NCCH::Load() {
222 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; 222 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
223 entry_point = exheader_header.codeset_info.text.address; 223 entry_point = exheader_header.codeset_info.text.address;
224 224
225 INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name); 225 LOG_INFO(Loader, "Name: %s", exheader_header.codeset_info.name);
226 INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); 226 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
227 INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point); 227 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
228 228
229 // Read ExeFS... 229 // Read ExeFS...
230 230
231 exefs_offset = ncch_header.exefs_offset * kBlockSize; 231 exefs_offset = ncch_header.exefs_offset * kBlockSize;
232 u32 exefs_size = ncch_header.exefs_size * kBlockSize; 232 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
233 233
234 INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); 234 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
235 INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); 235 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
236 236
237 file.Seek(exefs_offset + ncch_offset, 0); 237 file.Seek(exefs_offset + ncch_offset, 0);
238 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); 238 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
@@ -243,7 +243,7 @@ ResultStatus AppLoader_NCCH::Load() {
243 243
244 return ResultStatus::Success; 244 return ResultStatus::Success;
245 } else { 245 } else {
246 ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str()); 246 LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
247 } 247 }
248 return ResultStatus::Error; 248 return ResultStatus::Error;
249} 249}
@@ -297,8 +297,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
297 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; 297 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
298 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; 298 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
299 299
300 INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset); 300 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset);
301 INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size); 301 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size);
302 302
303 buffer.resize(romfs_size); 303 buffer.resize(romfs_size);
304 304
@@ -307,12 +307,16 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
307 307
308 return ResultStatus::Success; 308 return ResultStatus::Success;
309 } 309 }
310 NOTICE_LOG(LOADER, "RomFS unused"); 310 LOG_DEBUG(Loader, "NCCH has no RomFS");
311 return ResultStatus::ErrorNotUsed; 311 return ResultStatus::ErrorNotUsed;
312 } else { 312 } else {
313 ERROR_LOG(LOADER, "Unable to read file %s!", filename.c_str()); 313 LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
314 } 314 }
315 return ResultStatus::Error; 315 return ResultStatus::Error;
316} 316}
317 317
318u64 AppLoader_NCCH::GetProgramId() const {
319 return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]);
320}
321
318} // namespace Loader 322} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index f40a258b7..2fe2a7d82 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -191,6 +191,12 @@ public:
191 */ 191 */
192 ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; 192 ResultStatus ReadRomFS(std::vector<u8>& buffer) const override;
193 193
194 /*
195 * Gets the program id from the NCCH header
196 * @return u64 Program id
197 */
198 u64 GetProgramId() const;
199
194private: 200private:
195 201
196 /** 202 /**
@@ -215,7 +221,7 @@ private:
215 u32 entry_point; 221 u32 entry_point;
216 u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header 222 u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
217 u32 exefs_offset; 223 u32 exefs_offset;
218 224
219 NCCH_Header ncch_header; 225 NCCH_Header ncch_header;
220 ExeFs_Header exefs_header; 226 ExeFs_Header exefs_header;
221 ExHeader_Header exheader_header; 227 ExHeader_Header exheader_header;
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp
index cf12f24d9..d1c44ed24 100644
--- a/src/core/mem_map.cpp
+++ b/src/core/mem_map.cpp
@@ -11,38 +11,38 @@
11 11
12namespace Memory { 12namespace Memory {
13 13
14u8* g_base = NULL; ///< The base pointer to the auto-mirrored arena. 14u8* g_base = nullptr; ///< The base pointer to the auto-mirrored arena.
15 15
16MemArena g_arena; ///< The MemArena class 16static MemArena arena; ///< The MemArena class
17 17
18u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here 18u8* g_exefs_code = nullptr; ///< ExeFS:/.code is loaded here
19u8* g_system_mem = NULL; ///< System memory 19u8* g_system_mem = nullptr; ///< System memory
20u8* g_heap = NULL; ///< Application heap (main memory) 20u8* g_heap = nullptr; ///< Application heap (main memory)
21u8* g_heap_gsp = NULL; ///< GSP heap (main memory) 21u8* g_heap_linear = nullptr; ///< Linear heap
22u8* g_vram = NULL; ///< Video memory (VRAM) pointer 22u8* g_vram = nullptr; ///< Video memory (VRAM) pointer
23u8* g_shared_mem = NULL; ///< Shared memory 23u8* g_shared_mem = nullptr; ///< Shared memory
24u8* g_kernel_mem; ///< Kernel memory 24u8* g_kernel_mem; ///< Kernel memory
25 25
26u8* g_physical_bootrom = NULL; ///< Bootrom physical memory 26static u8* physical_bootrom = nullptr; ///< Bootrom physical memory
27u8* g_uncached_bootrom = NULL; 27static u8* uncached_bootrom = nullptr;
28 28
29u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here 29static u8* physical_exefs_code = nullptr; ///< Phsical ExeFS:/.code is loaded here
30u8* g_physical_system_mem = NULL; ///< System physical memory 30static u8* physical_system_mem = nullptr; ///< System physical memory
31u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM) 31static u8* physical_fcram = nullptr; ///< Main physical memory (FCRAM)
32u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory 32static u8* physical_heap_gsp = nullptr; ///< GSP heap physical memory
33u8* g_physical_vram = NULL; ///< Video physical memory (VRAM) 33static u8* physical_vram = nullptr; ///< Video physical memory (VRAM)
34u8* g_physical_shared_mem = NULL; ///< Physical shared memory 34static u8* physical_shared_mem = nullptr; ///< Physical shared memory
35u8* g_physical_kernel_mem; ///< Kernel memory 35static u8* physical_kernel_mem; ///< Kernel memory
36 36
37// We don't declare the IO region in here since its handled by other means. 37// We don't declare the IO region in here since its handled by other means.
38static MemoryView g_views[] = { 38static MemoryView g_views[] = {
39 {&g_exefs_code, &g_physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0}, 39 {&g_exefs_code, &physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0},
40 {&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0}, 40 {&g_vram, &physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
41 {&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM}, 41 {&g_heap, &physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
42 {&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0}, 42 {&g_shared_mem, &physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
43 {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0}, 43 {&g_system_mem, &physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
44 {&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0}, 44 {&g_kernel_mem, &physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
45 {&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0}, 45 {&g_heap_linear, &physical_heap_gsp, HEAP_LINEAR_VADDR, HEAP_LINEAR_SIZE, 0},
46}; 46};
47 47
48/*static MemoryView views[] = 48/*static MemoryView views[] =
@@ -69,20 +69,20 @@ void Init() {
69 g_views[i].size = FCRAM_SIZE; 69 g_views[i].size = FCRAM_SIZE;
70 } 70 }
71 71
72 g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &g_arena); 72 g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena);
73 73
74 NOTICE_LOG(MEMMAP, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap, 74 LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap,
75 g_physical_fcram); 75 physical_fcram);
76} 76}
77 77
78void Shutdown() { 78void Shutdown() {
79 u32 flags = 0; 79 u32 flags = 0;
80 MemoryMap_Shutdown(g_views, kNumMemViews, flags, &g_arena); 80 MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena);
81 81
82 g_arena.ReleaseSpace(); 82 arena.ReleaseSpace();
83 g_base = NULL; 83 g_base = nullptr;
84 84
85 NOTICE_LOG(MEMMAP, "shutdown OK"); 85 LOG_DEBUG(HW_Memory, "shutdown OK");
86} 86}
87 87
88} // namespace 88} // namespace
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index eed445046..7b750f848 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.
16 16
17//////////////////////////////////////////////////////////////////////////////////////////////////// 17////////////////////////////////////////////////////////////////////////////////////////////////////
18 18
19enum { 19enum : u32 {
20 BOOTROM_SIZE = 0x00010000, ///< Bootrom (super secret code/data @ 0x8000) size 20 BOOTROM_SIZE = 0x00010000, ///< Bootrom (super secret code/data @ 0x8000) size
21 MPCORE_PRIV_SIZE = 0x00002000, ///< MPCore private memory region size 21 MPCORE_PRIV_SIZE = 0x00002000, ///< MPCore private memory region size
22 DSP_SIZE = 0x00080000, ///< DSP memory size
23 AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size 22 AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size
24 23
25 FCRAM_SIZE = 0x08000000, ///< FCRAM size 24 FCRAM_SIZE = 0x08000000, ///< FCRAM size
@@ -27,47 +26,42 @@ enum {
27 FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space 26 FCRAM_PADDR_END = (FCRAM_PADDR + FCRAM_SIZE), ///< FCRAM end of physical space
28 FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address 27 FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address
29 FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space 28 FCRAM_VADDR_END = (FCRAM_VADDR + FCRAM_SIZE), ///< FCRAM end of virtual space
30 FCRAM_MASK = (FCRAM_SIZE - 1), ///< FCRAM mask
31 29
32 SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size 30 SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size
33 SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory 31 SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory
34 SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE), 32 SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE),
35 SHARED_MEMORY_MASK = (SHARED_MEMORY_SIZE - 1), 33
34 DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size
35 DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address
36 36
37 CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size 37 CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size
38 CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address 38 CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address
39 CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE), 39 CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE),
40 CONFIG_MEMORY_MASK = (CONFIG_MEMORY_SIZE - 1),
41 40
42 KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size 41 KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size
43 KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are 42 KERNEL_MEMORY_VADDR = 0xFFFF0000, ///< Kernel memory where the kthread objects etc are
44 KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE), 43 KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE),
45 KERNEL_MEMORY_MASK = (KERNEL_MEMORY_SIZE - 1),
46 44
47 EXEFS_CODE_SIZE = 0x03F00000, 45 EXEFS_CODE_SIZE = 0x03F00000,
48 EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here 46 EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here
49 EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), 47 EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE),
50 EXEFS_CODE_MASK = 0x03FFFFFF,
51 48
52 // Region of FCRAM used by system 49 // Region of FCRAM used by system
53 SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB 50 SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB
54 SYSTEM_MEMORY_VADDR = 0x04000000, 51 SYSTEM_MEMORY_VADDR = 0x04000000,
55 SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), 52 SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE),
56 SYSTEM_MEMORY_MASK = 0x03FFFFFF,
57 53
58 HEAP_SIZE = FCRAM_SIZE, ///< Application heap size 54 HEAP_SIZE = FCRAM_SIZE, ///< Application heap size
59 //HEAP_PADDR = HEAP_GSP_SIZE, 55 //HEAP_PADDR = HEAP_GSP_SIZE,
60 //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), 56 //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE),
61 HEAP_VADDR = 0x08000000, 57 HEAP_VADDR = 0x08000000,
62 HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE), 58 HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE),
63 HEAP_MASK = (HEAP_SIZE - 1),
64 59
65 HEAP_GSP_SIZE = 0x02000000, ///< GSP heap size... TODO: Define correctly? 60 HEAP_LINEAR_SIZE = 0x08000000, ///< Linear heap size... TODO: Define correctly?
66 HEAP_GSP_VADDR = 0x14000000, 61 HEAP_LINEAR_VADDR = 0x14000000,
67 HEAP_GSP_VADDR_END = (HEAP_GSP_VADDR + HEAP_GSP_SIZE), 62 HEAP_LINEAR_VADDR_END = (HEAP_LINEAR_VADDR + HEAP_LINEAR_SIZE),
68 HEAP_GSP_PADDR = 0x00000000, 63 HEAP_LINEAR_PADDR = 0x00000000,
69 HEAP_GSP_PADDR_END = (HEAP_GSP_PADDR + HEAP_GSP_SIZE), 64 HEAP_LINEAR_PADDR_END = (HEAP_LINEAR_PADDR + HEAP_LINEAR_SIZE),
70 HEAP_GSP_MASK = (HEAP_GSP_SIZE - 1),
71 65
72 HARDWARE_IO_SIZE = 0x01000000, 66 HARDWARE_IO_SIZE = 0x01000000,
73 HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start 67 HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start
@@ -80,12 +74,10 @@ enum {
80 VRAM_VADDR = 0x1F000000, 74 VRAM_VADDR = 0x1F000000,
81 VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE), 75 VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE),
82 VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE), 76 VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE),
83 VRAM_MASK = 0x007FFFFF,
84 77
85 SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader 78 SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader
86 SCRATCHPAD_VADDR_END = 0x10000000, 79 SCRATCHPAD_VADDR_END = 0x10000000,
87 SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space 80 SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space
88 SCRATCHPAD_MASK = (SCRATCHPAD_SIZE - 1), ///< Scratchpad memory mask
89}; 81};
90 82
91//////////////////////////////////////////////////////////////////////////////////////////////////// 83////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -120,7 +112,7 @@ extern u8 *g_base;
120// These are guaranteed to point to "low memory" addresses (sub-32-bit). 112// These are guaranteed to point to "low memory" addresses (sub-32-bit).
121// 64-bit: Pointers to low-mem (sub-0x10000000) mirror 113// 64-bit: Pointers to low-mem (sub-0x10000000) mirror
122// 32-bit: Same as the corresponding physical/virtual pointers. 114// 32-bit: Same as the corresponding physical/virtual pointers.
123extern u8* g_heap_gsp; ///< GSP heap (main memory) 115extern u8* g_heap_linear; ///< Linear heap (main memory)
124extern u8* g_heap; ///< Application heap (main memory) 116extern u8* g_heap; ///< Application heap (main memory)
125extern u8* g_vram; ///< Video memory (VRAM) 117extern u8* g_vram; ///< Video memory (VRAM)
126extern u8* g_shared_mem; ///< Shared memory 118extern u8* g_shared_mem; ///< Shared memory
@@ -147,6 +139,7 @@ u32 Read16_ZX(VAddr addr);
147void Write8(VAddr addr, u8 data); 139void Write8(VAddr addr, u8 data);
148void Write16(VAddr addr, u16 data); 140void Write16(VAddr addr, u16 data);
149void Write32(VAddr addr, u32 data); 141void Write32(VAddr addr, u32 data);
142void Write64(VAddr addr, u64 data);
150 143
151void WriteBlock(VAddr addr, const u8* data, size_t size); 144void WriteBlock(VAddr addr, const u8* data, size_t size);
152 145
@@ -156,7 +149,7 @@ u8* GetPointer(VAddr virtual_address);
156 * Maps a block of memory on the heap 149 * Maps a block of memory on the heap
157 * @param size Size of block in bytes 150 * @param size Size of block in bytes
158 * @param operation Memory map operation type 151 * @param operation Memory map operation type
159 * @param flags Memory allocation flags 152 * @param permissions Memory allocation permissions
160 */ 153 */
161u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions); 154u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
162 155
@@ -166,7 +159,7 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions);
166 * @param operation Memory map operation type 159 * @param operation Memory map operation type
167 * @param permissions Control memory permissions 160 * @param permissions Control memory permissions
168 */ 161 */
169u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions); 162u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions);
170 163
171inline const char* GetCharPointer(const VAddr address) { 164inline const char* GetCharPointer(const VAddr address) {
172 return (const char *)GetPointer(address); 165 return (const char *)GetPointer(address);
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index 90951812b..7f7e77233 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -12,9 +12,9 @@
12 12
13namespace Memory { 13namespace Memory {
14 14
15std::map<u32, MemoryBlock> g_heap_map; 15static std::map<u32, MemoryBlock> heap_map;
16std::map<u32, MemoryBlock> g_heap_gsp_map; 16static std::map<u32, MemoryBlock> heap_linear_map;
17std::map<u32, MemoryBlock> g_shared_map; 17static std::map<u32, MemoryBlock> shared_map;
18 18
19/// Convert a physical address to virtual address 19/// Convert a physical address to virtual address
20VAddr PhysicalToVirtualAddress(const PAddr addr) { 20VAddr PhysicalToVirtualAddress(const PAddr addr) {
@@ -28,7 +28,7 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) {
28 return addr - FCRAM_PADDR + FCRAM_VADDR; 28 return addr - FCRAM_PADDR + FCRAM_VADDR;
29 } 29 }
30 30
31 ERROR_LOG(MEMMAP, "Unknown physical address @ 0x%08x", addr); 31 LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr);
32 return addr; 32 return addr;
33} 33}
34 34
@@ -44,7 +44,7 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) {
44 return addr - FCRAM_VADDR + FCRAM_PADDR; 44 return addr - FCRAM_VADDR + FCRAM_PADDR;
45 } 45 }
46 46
47 ERROR_LOG(MEMMAP, "Unknown virtual address @ 0x%08x", addr); 47 LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr);
48 return addr; 48 return addr;
49} 49}
50 50
@@ -56,7 +56,7 @@ inline void Read(T &var, const VAddr vaddr) {
56 56
57 // Kernel memory command buffer 57 // Kernel memory command buffer
58 if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { 58 if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
59 var = *((const T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK]); 59 var = *((const T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR]);
60 60
61 // Hardware I/O register reads 61 // Hardware I/O register reads
62 // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space 62 // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
@@ -65,23 +65,23 @@ inline void Read(T &var, const VAddr vaddr) {
65 65
66 // ExeFS:/.code is loaded here 66 // ExeFS:/.code is loaded here
67 } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { 67 } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
68 var = *((const T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK]); 68 var = *((const T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR]);
69 69
70 // FCRAM - GSP heap 70 // FCRAM - linear heap
71 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { 71 } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
72 var = *((const T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK]); 72 var = *((const T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR]);
73 73
74 // FCRAM - application heap 74 // FCRAM - application heap
75 } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { 75 } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
76 var = *((const T*)&g_heap[vaddr & HEAP_MASK]); 76 var = *((const T*)&g_heap[vaddr - HEAP_VADDR]);
77 77
78 // Shared memory 78 // Shared memory
79 } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { 79 } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
80 var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]); 80 var = *((const T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR]);
81 81
82 // System memory 82 // System memory
83 } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { 83 } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
84 var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]); 84 var = *((const T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR]);
85 85
86 // Config memory 86 // Config memory
87 } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { 87 } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) {
@@ -89,10 +89,10 @@ inline void Read(T &var, const VAddr vaddr) {
89 89
90 // VRAM 90 // VRAM
91 } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { 91 } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
92 var = *((const T*)&g_vram[vaddr & VRAM_MASK]); 92 var = *((const T*)&g_vram[vaddr - VRAM_VADDR]);
93 93
94 } else { 94 } else {
95 ERROR_LOG(MEMMAP, "unknown Read%d @ 0x%08X", sizeof(var) * 8, vaddr); 95 LOG_ERROR(HW_Memory, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, vaddr);
96 } 96 }
97} 97}
98 98
@@ -101,7 +101,7 @@ inline void Write(const VAddr vaddr, const T data) {
101 101
102 // Kernel memory command buffer 102 // Kernel memory command buffer
103 if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { 103 if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
104 *(T*)&g_kernel_mem[vaddr & KERNEL_MEMORY_MASK] = data; 104 *(T*)&g_kernel_mem[vaddr - KERNEL_MEMORY_VADDR] = data;
105 105
106 // Hardware I/O register writes 106 // Hardware I/O register writes
107 // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space 107 // 0x10XXXXXX- is physical address space, 0x1EXXXXXX is virtual address space
@@ -110,27 +110,27 @@ inline void Write(const VAddr vaddr, const T data) {
110 110
111 // ExeFS:/.code is loaded here 111 // ExeFS:/.code is loaded here
112 } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { 112 } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
113 *(T*)&g_exefs_code[vaddr & EXEFS_CODE_MASK] = data; 113 *(T*)&g_exefs_code[vaddr - EXEFS_CODE_VADDR] = data;
114 114
115 // FCRAM - GSP heap 115 // FCRAM - linear heap
116 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { 116 } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
117 *(T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK] = data; 117 *(T*)&g_heap_linear[vaddr - HEAP_LINEAR_VADDR] = data;
118 118
119 // FCRAM - application heap 119 // FCRAM - application heap
120 } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { 120 } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
121 *(T*)&g_heap[vaddr & HEAP_MASK] = data; 121 *(T*)&g_heap[vaddr - HEAP_VADDR] = data;
122 122
123 // Shared memory 123 // Shared memory
124 } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { 124 } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
125 *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data; 125 *(T*)&g_shared_mem[vaddr - SHARED_MEMORY_VADDR] = data;
126 126
127 // System memory 127 // System memory
128 } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { 128 } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
129 *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data; 129 *(T*)&g_system_mem[vaddr - SYSTEM_MEMORY_VADDR] = data;
130 130
131 // VRAM 131 // VRAM
132 } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { 132 } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
133 *(T*)&g_vram[vaddr & VRAM_MASK] = data; 133 *(T*)&g_vram[vaddr - VRAM_VADDR] = data;
134 134
135 //} else if ((vaddr & 0xFFF00000) == 0x1FF00000) { 135 //} else if ((vaddr & 0xFFF00000) == 0x1FF00000) {
136 // _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory"); 136 // _assert_msg_(MEMMAP, false, "umimplemented write to DSP memory");
@@ -141,41 +141,41 @@ inline void Write(const VAddr vaddr, const T data) {
141 141
142 // Error out... 142 // Error out...
143 } else { 143 } else {
144 ERROR_LOG(MEMMAP, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, vaddr); 144 LOG_ERROR(HW_Memory, "unknown Write%lu 0x%08X @ 0x%08X", sizeof(data) * 8, (u32)data, vaddr);
145 } 145 }
146} 146}
147 147
148u8 *GetPointer(const VAddr vaddr) { 148u8 *GetPointer(const VAddr vaddr) {
149 // Kernel memory command buffer 149 // Kernel memory command buffer
150 if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) { 150 if (vaddr >= KERNEL_MEMORY_VADDR && vaddr < KERNEL_MEMORY_VADDR_END) {
151 return g_kernel_mem + (vaddr & KERNEL_MEMORY_MASK); 151 return g_kernel_mem + (vaddr - KERNEL_MEMORY_VADDR);
152 152
153 // ExeFS:/.code is loaded here 153 // ExeFS:/.code is loaded here
154 } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) { 154 } else if ((vaddr >= EXEFS_CODE_VADDR) && (vaddr < EXEFS_CODE_VADDR_END)) {
155 return g_exefs_code + (vaddr & EXEFS_CODE_MASK); 155 return g_exefs_code + (vaddr - EXEFS_CODE_VADDR);
156 156
157 // FCRAM - GSP heap 157 // FCRAM - linear heap
158 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { 158 } else if ((vaddr >= HEAP_LINEAR_VADDR) && (vaddr < HEAP_LINEAR_VADDR_END)) {
159 return g_heap_gsp + (vaddr & HEAP_GSP_MASK); 159 return g_heap_linear + (vaddr - HEAP_LINEAR_VADDR);
160 160
161 // FCRAM - application heap 161 // FCRAM - application heap
162 } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) { 162 } else if ((vaddr >= HEAP_VADDR) && (vaddr < HEAP_VADDR_END)) {
163 return g_heap + (vaddr & HEAP_MASK); 163 return g_heap + (vaddr - HEAP_VADDR);
164 164
165 // Shared memory 165 // Shared memory
166 } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { 166 } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) {
167 return g_shared_mem + (vaddr & SHARED_MEMORY_MASK); 167 return g_shared_mem + (vaddr - SHARED_MEMORY_VADDR);
168 168
169 // System memory 169 // System memory
170 } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { 170 } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) {
171 return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK); 171 return g_system_mem + (vaddr - SYSTEM_MEMORY_VADDR);
172 172
173 // VRAM 173 // VRAM
174 } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { 174 } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) {
175 return g_vram + (vaddr & VRAM_MASK); 175 return g_vram + (vaddr - VRAM_VADDR);
176 176
177 } else { 177 } else {
178 ERROR_LOG(MEMMAP, "unknown GetPointer @ 0x%08x", vaddr); 178 LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr);
179 return 0; 179 return 0;
180 } 180 }
181} 181}
@@ -194,34 +194,34 @@ u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) {
194 block.operation = operation; 194 block.operation = operation;
195 block.permissions = permissions; 195 block.permissions = permissions;
196 196
197 if (g_heap_map.size() > 0) { 197 if (heap_map.size() > 0) {
198 const MemoryBlock last_block = g_heap_map.rbegin()->second; 198 const MemoryBlock last_block = heap_map.rbegin()->second;
199 block.address = last_block.address + last_block.size; 199 block.address = last_block.address + last_block.size;
200 } 200 }
201 g_heap_map[block.GetVirtualAddress()] = block; 201 heap_map[block.GetVirtualAddress()] = block;
202 202
203 return block.GetVirtualAddress(); 203 return block.GetVirtualAddress();
204} 204}
205 205
206/** 206/**
207 * Maps a block of memory on the GSP heap 207 * Maps a block of memory on the linear heap
208 * @param size Size of block in bytes 208 * @param size Size of block in bytes
209 * @param operation Memory map operation type 209 * @param operation Memory map operation type
210 * @param flags Memory allocation flags 210 * @param flags Memory allocation flags
211 */ 211 */
212u32 MapBlock_HeapGSP(u32 size, u32 operation, u32 permissions) { 212u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) {
213 MemoryBlock block; 213 MemoryBlock block;
214 214
215 block.base_address = HEAP_GSP_VADDR; 215 block.base_address = HEAP_LINEAR_VADDR;
216 block.size = size; 216 block.size = size;
217 block.operation = operation; 217 block.operation = operation;
218 block.permissions = permissions; 218 block.permissions = permissions;
219 219
220 if (g_heap_gsp_map.size() > 0) { 220 if (heap_linear_map.size() > 0) {
221 const MemoryBlock last_block = g_heap_gsp_map.rbegin()->second; 221 const MemoryBlock last_block = heap_linear_map.rbegin()->second;
222 block.address = last_block.address + last_block.size; 222 block.address = last_block.address + last_block.size;
223 } 223 }
224 g_heap_gsp_map[block.GetVirtualAddress()] = block; 224 heap_linear_map[block.GetVirtualAddress()] = block;
225 225
226 return block.GetVirtualAddress(); 226 return block.GetVirtualAddress();
227} 227}
@@ -239,7 +239,7 @@ u16 Read16(const VAddr addr) {
239 // Check for 16-bit unaligned memory reads... 239 // Check for 16-bit unaligned memory reads...
240 if (addr & 1) { 240 if (addr & 1) {
241 // TODO(bunnei): Implement 16-bit unaligned memory reads 241 // TODO(bunnei): Implement 16-bit unaligned memory reads
242 ERROR_LOG(MEMMAP, "16-bit unaligned memory reads are not implemented!"); 242 LOG_ERROR(HW_Memory, "16-bit unaligned memory reads are not implemented!");
243 } 243 }
244 244
245 return (u16)data; 245 return (u16)data;
diff --git a/src/core/settings.h b/src/core/settings.h
index 6a6265e18..138ffc615 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string>
8
7namespace Settings { 9namespace Settings {
8 10
9struct Values { 11struct Values {
@@ -32,6 +34,8 @@ struct Values {
32 34
33 // Data Storage 35 // Data Storage
34 bool use_virtual_sd; 36 bool use_virtual_sd;
37
38 std::string log_filter;
35} extern values; 39} extern values;
36 40
37} 41}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 43d0eef2c..2885ff45f 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -23,10 +23,10 @@ void Init(EmuWindow* emu_window) {
23 Core::Init(); 23 Core::Init();
24 Memory::Init(); 24 Memory::Init();
25 HW::Init(); 25 HW::Init();
26 Kernel::Init();
26 HLE::Init(); 27 HLE::Init();
27 CoreTiming::Init(); 28 CoreTiming::Init();
28 VideoCore::Init(emu_window); 29 VideoCore::Init(emu_window);
29 Kernel::Init();
30} 30}
31 31
32void RunLoopFor(int cycles) { 32void RunLoopFor(int cycles) {
@@ -37,13 +37,13 @@ void RunLoopUntil(u64 global_cycles) {
37} 37}
38 38
39void Shutdown() { 39void Shutdown() {
40 Core::Shutdown();
41 Memory::Shutdown();
42 HW::Shutdown();
43 HLE::Shutdown();
44 CoreTiming::Shutdown();
45 VideoCore::Shutdown(); 40 VideoCore::Shutdown();
41 CoreTiming::Shutdown();
42 HLE::Shutdown();
46 Kernel::Shutdown(); 43 Kernel::Shutdown();
44 HW::Shutdown();
45 Memory::Shutdown();
46 Core::Shutdown();
47} 47}
48 48
49} // namespace 49} // namespace
diff --git a/src/core/system.h b/src/core/system.h
index 8f8ddf87b..2bc2edc75 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -11,16 +11,16 @@
11namespace System { 11namespace System {
12 12
13// State of the full emulator 13// State of the full emulator
14typedef enum { 14enum State {
15 STATE_NULL = 0, ///< System is in null state, nothing initialized 15 STATE_NULL = 0, ///< System is in null state, nothing initialized
16 STATE_IDLE, ///< System is in an initialized state, but not running 16 STATE_IDLE, ///< System is in an initialized state, but not running
17 STATE_RUNNING, ///< System is running 17 STATE_RUNNING, ///< System is running
18 STATE_LOADING, ///< System is loading a ROM 18 STATE_LOADING, ///< System is loading a ROM
19 STATE_HALTED, ///< System is halted (error) 19 STATE_HALTED, ///< System is halted (error)
20 STATE_STALLED, ///< System is stalled (unused) 20 STATE_STALLED, ///< System is stalled (unused)
21 STATE_DEBUG, ///< System is in a special debug mode (unused) 21 STATE_DEBUG, ///< System is in a special debug mode (unused)
22 STATE_DIE ///< System is shutting down 22 STATE_DIE ///< System is shutting down
23} State; 23};
24 24
25extern volatile State g_state; 25extern volatile State g_state;
26 26
@@ -30,4 +30,4 @@ void RunLoopFor(int cycles);
30void RunLoopUntil(u64 global_cycles); 30void RunLoopUntil(u64 global_cycles);
31void Shutdown(); 31void Shutdown();
32 32
33}; 33}
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp
index 96d3dabe2..632fb959a 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) {
157 157
158 InitScreenCoordinates(vtx2); 158 InitScreenCoordinates(vtx2);
159 159
160 DEBUG_LOG(GPU, 160 LOG_TRACE(Render_Software,
161 "Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), " 161 "Triangle %lu/%lu (%lu buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), "
162 "(%.3lu, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " 162 "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and "
163 "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", 163 "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)",
164 i,output_list.size(), buffer_vertices.size(), 164 i,output_list.size(), buffer_vertices.size(),
165 vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),output_list.size(), 165 vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),
166 vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), 166 vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(),
167 vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), 167 vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(),
168 vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(), 168 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 1ec727698..b74cd3261 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -8,6 +8,7 @@
8#include "pica.h" 8#include "pica.h"
9#include "primitive_assembly.h" 9#include "primitive_assembly.h"
10#include "vertex_shader.h" 10#include "vertex_shader.h"
11#include "core/hle/service/gsp_gpu.h"
11 12
12#include "debug_utils/debug_utils.h" 13#include "debug_utils/debug_utils.h"
13 14
@@ -34,15 +35,26 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
34 u32 old_value = registers[id]; 35 u32 old_value = registers[id];
35 registers[id] = (old_value & ~mask) | (value & mask); 36 registers[id] = (old_value & ~mask) | (value & mask);
36 37
38 if (g_debug_context)
39 g_debug_context->OnEvent(DebugContext::Event::CommandLoaded, reinterpret_cast<void*>(&id));
40
37 DebugUtils::OnPicaRegWrite(id, registers[id]); 41 DebugUtils::OnPicaRegWrite(id, registers[id]);
38 42
39 switch(id) { 43 switch(id) {
44 // Trigger IRQ
45 case PICA_REG_INDEX(trigger_irq):
46 GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
47 return;
48
40 // It seems like these trigger vertex rendering 49 // It seems like these trigger vertex rendering
41 case PICA_REG_INDEX(trigger_draw): 50 case PICA_REG_INDEX(trigger_draw):
42 case PICA_REG_INDEX(trigger_draw_indexed): 51 case PICA_REG_INDEX(trigger_draw_indexed):
43 { 52 {
44 DebugUtils::DumpTevStageConfig(registers.GetTevStages()); 53 DebugUtils::DumpTevStageConfig(registers.GetTevStages());
45 54
55 if (g_debug_context)
56 g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr);
57
46 const auto& attribute_config = registers.vertex_attributes; 58 const auto& attribute_config = registers.vertex_attributes;
47 const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress()); 59 const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress());
48 60
@@ -60,7 +72,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
60 const u8* load_address = base_address + loader_config.data_offset; 72 const u8* load_address = base_address + loader_config.data_offset;
61 73
62 // TODO: What happens if a loader overwrites a previous one's data? 74 // TODO: What happens if a loader overwrites a previous one's data?
63 for (int component = 0; component < loader_config.component_count; ++component) { 75 for (unsigned component = 0; component < loader_config.component_count; ++component) {
64 u32 attribute_index = loader_config.GetComponent(component); 76 u32 attribute_index = loader_config.GetComponent(component);
65 vertex_attribute_sources[attribute_index] = load_address; 77 vertex_attribute_sources[attribute_index] = load_address;
66 vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count); 78 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) {
102 (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : 114 (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata :
103 *(float*)srcdata; 115 *(float*)srcdata;
104 input.attr[i][comp] = float24::FromFloat32(srcval); 116 input.attr[i][comp] = float24::FromFloat32(srcval);
105 DEBUG_LOG(GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", 117 LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f",
106 comp, i, vertex, index, 118 comp, i, vertex, index,
107 attribute_config.GetBaseAddress(), 119 attribute_config.GetBaseAddress(),
108 vertex_attribute_sources[i] - base_address, 120 vertex_attribute_sources[i] - base_address,
@@ -132,6 +144,10 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
132 clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle); 144 clipper_primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
133 } 145 }
134 geometry_dumper.Dump(); 146 geometry_dumper.Dump();
147
148 if (g_debug_context)
149 g_debug_context->OnEvent(DebugContext::Event::FinishedPrimitiveBatch, nullptr);
150
135 break; 151 break;
136 } 152 }
137 153
@@ -160,7 +176,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
160 auto& uniform = VertexShader::GetFloatUniform(uniform_setup.index); 176 auto& uniform = VertexShader::GetFloatUniform(uniform_setup.index);
161 177
162 if (uniform_setup.index > 95) { 178 if (uniform_setup.index > 95) {
163 ERROR_LOG(GPU, "Invalid VS uniform index %d", (int)uniform_setup.index); 179 LOG_ERROR(HW_GPU, "Invalid VS uniform index %d", (int)uniform_setup.index);
164 break; 180 break;
165 } 181 }
166 182
@@ -176,7 +192,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
176 uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF); 192 uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF);
177 } 193 }
178 194
179 DEBUG_LOG(GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index, 195 LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index,
180 uniform.x.ToFloat32(), uniform.y.ToFloat32(), uniform.z.ToFloat32(), 196 uniform.x.ToFloat32(), uniform.y.ToFloat32(), uniform.z.ToFloat32(),
181 uniform.w.ToFloat32()); 197 uniform.w.ToFloat32());
182 198
@@ -229,6 +245,9 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
229 default: 245 default:
230 break; 246 break;
231 } 247 }
248
249 if (g_debug_context)
250 g_debug_context->OnEvent(DebugContext::Event::CommandProcessed, reinterpret_cast<void*>(&id));
232} 251}
233 252
234static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { 253static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) {
@@ -259,8 +278,9 @@ static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) {
259 278
260void ProcessCommandList(const u32* list, u32 size) { 279void ProcessCommandList(const u32* list, u32 size) {
261 u32* read_pointer = (u32*)list; 280 u32* read_pointer = (u32*)list;
281 u32 list_length = size / sizeof(u32);
262 282
263 while (read_pointer < list + size) { 283 while (read_pointer < list + list_length) {
264 read_pointer += ExecuteCommandBlock(read_pointer); 284 read_pointer += ExecuteCommandBlock(read_pointer);
265 } 285 }
266} 286}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index 22b8e9950..1a20f19ec 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -3,6 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <condition_variable>
7#include <list>
6#include <map> 8#include <map>
7#include <fstream> 9#include <fstream>
8#include <mutex> 10#include <mutex>
@@ -12,14 +14,56 @@
12#include <png.h> 14#include <png.h>
13#endif 15#endif
14 16
17#include "common/log.h"
15#include "common/file_util.h" 18#include "common/file_util.h"
16 19
20#include "video_core/math.h"
17#include "video_core/pica.h" 21#include "video_core/pica.h"
18 22
19#include "debug_utils.h" 23#include "debug_utils.h"
20 24
21namespace Pica { 25namespace Pica {
22 26
27void DebugContext::OnEvent(Event event, void* data) {
28 if (!breakpoints[event].enabled)
29 return;
30
31 {
32 std::unique_lock<std::mutex> lock(breakpoint_mutex);
33
34 // TODO: Should stop the CPU thread here once we multithread emulation.
35
36 active_breakpoint = event;
37 at_breakpoint = true;
38
39 // Tell all observers that we hit a breakpoint
40 for (auto& breakpoint_observer : breakpoint_observers) {
41 breakpoint_observer->OnPicaBreakPointHit(event, data);
42 }
43
44 // Wait until another thread tells us to Resume()
45 resume_from_breakpoint.wait(lock, [&]{ return !at_breakpoint; });
46 }
47}
48
49void DebugContext::Resume() {
50 {
51 std::unique_lock<std::mutex> lock(breakpoint_mutex);
52
53 // Tell all observers that we are about to resume
54 for (auto& breakpoint_observer : breakpoint_observers) {
55 breakpoint_observer->OnPicaResume();
56 }
57
58 // Resume the waiting thread (i.e. OnEvent())
59 at_breakpoint = false;
60 }
61
62 resume_from_breakpoint.notify_one();
63}
64
65std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
66
23namespace DebugUtils { 67namespace DebugUtils {
24 68
25void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { 69void 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
155 199
156 // This is put into a try-catch block to make sure we notice unknown configurations. 200 // This is put into a try-catch block to make sure we notice unknown configurations.
157 std::vector<OutputRegisterInfo> output_info_table; 201 std::vector<OutputRegisterInfo> output_info_table;
158 for (int i = 0; i < 7; ++i) { 202 for (unsigned i = 0; i < 7; ++i) {
159 using OutputAttributes = Pica::Regs::VSOutputAttributes; 203 using OutputAttributes = Pica::Regs::VSOutputAttributes;
160 204
161 // TODO: It's still unclear how the attribute components map to the register! 205 // 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
204 it->component_mask = it->component_mask | component_mask; 248 it->component_mask = it->component_mask | component_mask;
205 } 249 }
206 } catch (const std::out_of_range& ) { 250 } catch (const std::out_of_range& ) {
207 _dbg_assert_msg_(GPU, 0, "Unknown output attribute mapping"); 251 _dbg_assert_msg_(HW_GPU, 0, "Unknown output attribute mapping");
208 ERROR_LOG(GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x", 252 LOG_ERROR(HW_GPU, "Unknown output attribute mapping: %03x, %03x, %03x, %03x",
209 (int)output_attributes[i].map_x.Value(), 253 (int)output_attributes[i].map_x.Value(),
210 (int)output_attributes[i].map_y.Value(), 254 (int)output_attributes[i].map_y.Value(),
211 (int)output_attributes[i].map_z.Value(), 255 (int)output_attributes[i].map_z.Value(),
@@ -265,7 +309,7 @@ static int is_pica_tracing = false;
265void StartPicaTracing() 309void StartPicaTracing()
266{ 310{
267 if (is_pica_tracing) { 311 if (is_pica_tracing) {
268 ERROR_LOG(GPU, "StartPicaTracing called even though tracing already running!"); 312 LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!");
269 return; 313 return;
270 } 314 }
271 315
@@ -298,7 +342,7 @@ void OnPicaRegWrite(u32 id, u32 value)
298std::unique_ptr<PicaTrace> FinishPicaTracing() 342std::unique_ptr<PicaTrace> FinishPicaTracing()
299{ 343{
300 if (!is_pica_tracing) { 344 if (!is_pica_tracing) {
301 ERROR_LOG(GPU, "FinishPicaTracing called even though tracing already running!"); 345 LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!");
302 return {}; 346 return {};
303 } 347 }
304 348
@@ -312,15 +356,51 @@ std::unique_ptr<PicaTrace> FinishPicaTracing()
312 return std::move(ret); 356 return std::move(ret);
313} 357}
314 358
359const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info) {
360 _dbg_assert_(Debug_GPU, info.format == Pica::Regs::TextureFormat::RGB8);
361
362 // Cf. rasterizer code for an explanation of this algorithm.
363 int texel_index_within_tile = 0;
364 for (int block_size_index = 0; block_size_index < 3; ++block_size_index) {
365 int sub_tile_width = 1 << block_size_index;
366 int sub_tile_height = 1 << block_size_index;
367
368 int sub_tile_index = (x & sub_tile_width) << block_size_index;
369 sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index);
370 texel_index_within_tile += sub_tile_index;
371 }
372
373 const int block_width = 8;
374 const int block_height = 8;
375
376 int coarse_x = (x / block_width) * block_width;
377 int coarse_y = (y / block_height) * block_height;
378
379 const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3;
380 return { source_ptr[2], source_ptr[1], source_ptr[0], 255 };
381}
382
383TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
384 const Regs::TextureFormat& format)
385{
386 TextureInfo info;
387 info.address = config.GetPhysicalAddress();
388 info.width = config.width;
389 info.height = config.height;
390 info.format = format;
391 info.stride = Pica::Regs::BytesPerPixel(info.format) * info.width;
392 return info;
393}
394
315void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { 395void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
316 // NOTE: Permanently enabling this just trashes hard disks for no reason. 396 // NOTE: Permanently enabling this just trashes hard disks for no reason.
317 // Hence, this is currently disabled. 397 // Hence, this is currently disabled.
318 return; 398 return;
319 399
320#ifndef HAVE_PNG 400#ifndef HAVE_PNG
321 return; 401 return;
322#else 402#else
323 if (!data) 403 if (!data)
324 return; 404 return;
325 405
326 // Write data to file 406 // Write data to file
@@ -341,7 +421,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
341 // Initialize write structure 421 // Initialize write structure
342 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 422 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
343 if (png_ptr == nullptr) { 423 if (png_ptr == nullptr) {
344 ERROR_LOG(GPU, "Could not allocate write struct\n"); 424 LOG_ERROR(Debug_GPU, "Could not allocate write struct\n");
345 goto finalise; 425 goto finalise;
346 426
347 } 427 }
@@ -349,13 +429,13 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
349 // Initialize info structure 429 // Initialize info structure
350 info_ptr = png_create_info_struct(png_ptr); 430 info_ptr = png_create_info_struct(png_ptr);
351 if (info_ptr == nullptr) { 431 if (info_ptr == nullptr) {
352 ERROR_LOG(GPU, "Could not allocate info struct\n"); 432 LOG_ERROR(Debug_GPU, "Could not allocate info struct\n");
353 goto finalise; 433 goto finalise;
354 } 434 }
355 435
356 // Setup Exception handling 436 // Setup Exception handling
357 if (setjmp(png_jmpbuf(png_ptr))) { 437 if (setjmp(png_jmpbuf(png_ptr))) {
358 ERROR_LOG(GPU, "Error during png creation\n"); 438 LOG_ERROR(Debug_GPU, "Error during png creation\n");
359 goto finalise; 439 goto finalise;
360 } 440 }
361 441
@@ -375,34 +455,22 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
375 png_write_info(png_ptr, info_ptr); 455 png_write_info(png_ptr, info_ptr);
376 456
377 buf = new u8[row_stride * texture_config.height]; 457 buf = new u8[row_stride * texture_config.height];
378 for (int y = 0; y < texture_config.height; ++y) { 458 for (unsigned y = 0; y < texture_config.height; ++y) {
379 for (int x = 0; x < texture_config.width; ++x) { 459 for (unsigned x = 0; x < texture_config.width; ++x) {
380 // Cf. rasterizer code for an explanation of this algorithm. 460 TextureInfo info;
381 int texel_index_within_tile = 0; 461 info.width = texture_config.width;
382 for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { 462 info.height = texture_config.height;
383 int sub_tile_width = 1 << block_size_index; 463 info.stride = row_stride;
384 int sub_tile_height = 1 << block_size_index; 464 info.format = registers.texture0_format;
385 465 Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info);
386 int sub_tile_index = (x & sub_tile_width) << block_size_index; 466 buf[3 * x + y * row_stride ] = texture_color.r();
387 sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index); 467 buf[3 * x + y * row_stride + 1] = texture_color.g();
388 texel_index_within_tile += sub_tile_index; 468 buf[3 * x + y * row_stride + 2] = texture_color.b();
389 }
390
391 const int block_width = 8;
392 const int block_height = 8;
393
394 int coarse_x = (x / block_width) * block_width;
395 int coarse_y = (y / block_height) * block_height;
396
397 u8* source_ptr = (u8*)data + coarse_x * block_height * 3 + coarse_y * row_stride + texel_index_within_tile * 3;
398 buf[3 * x + y * row_stride ] = source_ptr[2];
399 buf[3 * x + y * row_stride + 1] = source_ptr[1];
400 buf[3 * x + y * row_stride + 2] = source_ptr[0];
401 } 469 }
402 } 470 }
403 471
404 // Write image data 472 // Write image data
405 for (auto y = 0; y < texture_config.height; ++y) 473 for (unsigned y = 0; y < texture_config.height; ++y)
406 { 474 {
407 u8* row_ptr = (u8*)buf + y * row_stride; 475 u8* row_ptr = (u8*)buf + y * row_stride;
408 u8* ptr = row_ptr; 476 u8* ptr = row_ptr;
@@ -514,7 +582,7 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages)
514 stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n"; 582 stage_info += "Stage " + std::to_string(index) + ": " + GetColorCombinerStr(tev_stage) + " " + GetAlphaCombinerStr(tev_stage) + "\n";
515 } 583 }
516 584
517 DEBUG_LOG(GPU, "%s", stage_info.c_str()); 585 LOG_TRACE(HW_GPU, "%s", stage_info.c_str());
518} 586}
519 587
520} // namespace 588} // namespace
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 8b1499bf2..51f14f12f 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -5,13 +5,147 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <condition_variable>
9#include <list>
10#include <map>
8#include <memory> 11#include <memory>
12#include <mutex>
9#include <vector> 13#include <vector>
10 14
15#include "video_core/math.h"
11#include "video_core/pica.h" 16#include "video_core/pica.h"
12 17
13namespace Pica { 18namespace Pica {
14 19
20class DebugContext {
21public:
22 enum class Event {
23 FirstEvent = 0,
24
25 CommandLoaded = FirstEvent,
26 CommandProcessed,
27 IncomingPrimitiveBatch,
28 FinishedPrimitiveBatch,
29
30 NumEvents
31 };
32
33 /**
34 * Inherit from this class to be notified of events registered to some debug context.
35 * Most importantly this is used for our debugger GUI.
36 *
37 * To implement event handling, override the OnPicaBreakPointHit and OnPicaResume methods.
38 * @warning All BreakPointObservers need to be on the same thread to guarantee thread-safe state access
39 * @todo Evaluate an alternative interface, in which there is only one managing observer and multiple child observers running (by design) on the same thread.
40 */
41 class BreakPointObserver {
42 public:
43 /// Constructs the object such that it observes events of the given DebugContext.
44 BreakPointObserver(std::shared_ptr<DebugContext> debug_context) : context_weak(debug_context) {
45 std::unique_lock<std::mutex> lock(debug_context->breakpoint_mutex);
46 debug_context->breakpoint_observers.push_back(this);
47 }
48
49 virtual ~BreakPointObserver() {
50 auto context = context_weak.lock();
51 if (context) {
52 std::unique_lock<std::mutex> lock(context->breakpoint_mutex);
53 context->breakpoint_observers.remove(this);
54
55 // If we are the last observer to be destroyed, tell the debugger context that
56 // it is free to continue. In particular, this is required for a proper Citra
57 // shutdown, when the emulation thread is waiting at a breakpoint.
58 if (context->breakpoint_observers.empty())
59 context->Resume();
60 }
61 }
62
63 /**
64 * Action to perform when a breakpoint was reached.
65 * @param event Type of event which triggered the breakpoint
66 * @param data Optional data pointer (if unused, this is a nullptr)
67 * @note This function will perform nothing unless it is overridden in the child class.
68 */
69 virtual void OnPicaBreakPointHit(Event, void*) {
70 }
71
72 /**
73 * Action to perform when emulation is resumed from a breakpoint.
74 * @note This function will perform nothing unless it is overridden in the child class.
75 */
76 virtual void OnPicaResume() {
77 }
78
79 protected:
80 /**
81 * Weak context pointer. This need not be valid, so when requesting a shared_ptr via
82 * context_weak.lock(), always compare the result against nullptr.
83 */
84 std::weak_ptr<DebugContext> context_weak;
85 };
86
87 /**
88 * Simple structure defining a breakpoint state
89 */
90 struct BreakPoint {
91 bool enabled = false;
92 };
93
94 /**
95 * Static constructor used to create a shared_ptr of a DebugContext.
96 */
97 static std::shared_ptr<DebugContext> Construct() {
98 return std::shared_ptr<DebugContext>(new DebugContext);
99 }
100
101 /**
102 * Used by the emulation core when a given event has happened. If a breakpoint has been set
103 * for this event, OnEvent calls the event handlers of the registered breakpoint observers.
104 * The current thread then is halted until Resume() is called from another thread (or until
105 * emulation is stopped).
106 * @param event Event which has happened
107 * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called.
108 */
109 void OnEvent(Event event, void* data);
110
111 /**
112 * Resume from the current breakpoint.
113 * @warning Calling this from the same thread that OnEvent was called in will cause a deadlock. Calling from any other thread is safe.
114 */
115 void Resume();
116
117 /**
118 * Delete all set breakpoints and resume emulation.
119 */
120 void ClearBreakpoints() {
121 breakpoints.clear();
122 Resume();
123 }
124
125 // TODO: Evaluate if access to these members should be hidden behind a public interface.
126 std::map<Event, BreakPoint> breakpoints;
127 Event active_breakpoint;
128 bool at_breakpoint = false;
129
130private:
131 /**
132 * Private default constructor to make sure people always construct this through Construct()
133 * instead.
134 */
135 DebugContext() = default;
136
137 /// Mutex protecting current breakpoint state and the observer list.
138 std::mutex breakpoint_mutex;
139
140 /// Used by OnEvent to wait for resumption.
141 std::condition_variable resume_from_breakpoint;
142
143 /// List of registered observers
144 std::list<BreakPointObserver*> breakpoint_observers;
145};
146
147extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
148
15namespace DebugUtils { 149namespace DebugUtils {
16 150
17// Simple utility class for dumping geometry data to an OBJ file 151// 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
41// Utility class to log Pica commands. 175// Utility class to log Pica commands.
42struct PicaTrace { 176struct PicaTrace {
43 struct Write : public std::pair<u32,u32> { 177 struct Write : public std::pair<u32,u32> {
44 Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {} 178 Write(u32 id, u32 value) : std::pair<u32,u32>(id, value) {}
45 179
46 u32& Id() { return first; } 180 u32& Id() { return first; }
47 const u32& Id() const { return first; } 181 const u32& Id() const { return first; }
@@ -57,6 +191,18 @@ bool IsPicaTracing();
57void OnPicaRegWrite(u32 id, u32 value); 191void OnPicaRegWrite(u32 id, u32 value);
58std::unique_ptr<PicaTrace> FinishPicaTracing(); 192std::unique_ptr<PicaTrace> FinishPicaTracing();
59 193
194struct TextureInfo {
195 unsigned int address;
196 int width;
197 int height;
198 int stride;
199 Pica::Regs::TextureFormat format;
200
201 static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config,
202 const Pica::Regs::TextureFormat& format);
203};
204
205const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info);
60void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); 206void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
61 207
62void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); 208void 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 1242eb58f..16b1656bb 100644
--- a/src/video_core/gpu_debugger.h
+++ b/src/video_core/gpu_debugger.h
@@ -39,7 +39,7 @@ public:
39 virtual void GXCommandProcessed(int total_command_count) 39 virtual void GXCommandProcessed(int total_command_count)
40 { 40 {
41 const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1); 41 const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count-1);
42 ERROR_LOG(GSP, "Received command: id=%x", (int)cmd.id.Value()); 42 LOG_TRACE(Debug_GPU, "Received command: id=%x", (int)cmd.id.Value());
43 } 43 }
44 44
45 protected: 45 protected:
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 5fe15a218..4c3791ad9 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -45,10 +45,16 @@ struct Regs {
45#define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) 45#define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y)
46#define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]; 46#define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)];
47 47
48 INSERT_PADDING_WORDS(0x41); 48 INSERT_PADDING_WORDS(0x10);
49
50 u32 trigger_irq;
51
52 INSERT_PADDING_WORDS(0x30);
49 53
50 BitField<0, 24, u32> viewport_size_x; 54 BitField<0, 24, u32> viewport_size_x;
55
51 INSERT_PADDING_WORDS(0x1); 56 INSERT_PADDING_WORDS(0x1);
57
52 BitField<0, 24, u32> viewport_size_y; 58 BitField<0, 24, u32> viewport_size_y;
53 59
54 INSERT_PADDING_WORDS(0x9); 60 INSERT_PADDING_WORDS(0x9);
@@ -109,8 +115,8 @@ struct Regs {
109 115
110 u32 address; 116 u32 address;
111 117
112 u32 GetPhysicalAddress() { 118 u32 GetPhysicalAddress() const {
113 return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; 119 return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR;
114 } 120 }
115 121
116 // texture1 and texture2 store the texture format directly after the address 122 // texture1 and texture2 store the texture format directly after the address
@@ -130,7 +136,26 @@ struct Regs {
130 // Seems like they are luminance formats and compressed textures. 136 // Seems like they are luminance formats and compressed textures.
131 }; 137 };
132 138
133 BitField<0, 1, u32> texturing_enable; 139 static unsigned BytesPerPixel(TextureFormat format) {
140 switch (format) {
141 case TextureFormat::RGBA8:
142 return 4;
143
144 case TextureFormat::RGB8:
145 return 3;
146
147 case TextureFormat::RGBA5551:
148 case TextureFormat::RGB565:
149 case TextureFormat::RGBA4:
150 return 2;
151
152 default:
153 // placeholder for yet unknown formats
154 return 1;
155 }
156 }
157
158 BitField< 0, 1, u32> texturing_enable;
134 TextureConfig texture0; 159 TextureConfig texture0;
135 INSERT_PADDING_WORDS(0x8); 160 INSERT_PADDING_WORDS(0x8);
136 BitField<0, 4, TextureFormat> texture0_format; 161 BitField<0, 4, TextureFormat> texture0_format;
@@ -287,7 +312,7 @@ struct Regs {
287 312
288 inline u32 GetBaseAddress() const { 313 inline u32 GetBaseAddress() const {
289 // TODO: Ugly, should fix PhysicalToVirtualAddress instead 314 // TODO: Ugly, should fix PhysicalToVirtualAddress instead
290 return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; 315 return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR;
291 } 316 }
292 317
293 // Descriptor for internal vertex attributes 318 // Descriptor for internal vertex attributes
@@ -517,10 +542,6 @@ struct Regs {
517 static std::string GetCommandName(int index) { 542 static std::string GetCommandName(int index) {
518 std::map<u32, std::string> map; 543 std::map<u32, std::string> map;
519 544
520 // TODO: MSVC does not support using offsetof() on non-static data members even though this
521 // is technically allowed since C++11. Hence, this functionality is disabled until
522 // MSVC properly supports it.
523 #ifndef _MSC_VER
524 Regs regs; 545 Regs regs;
525 #define ADD_FIELD(name) \ 546 #define ADD_FIELD(name) \
526 do { \ 547 do { \
@@ -529,6 +550,7 @@ struct Regs {
529 map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \ 550 map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \
530 } while(false) 551 } while(false)
531 552
553 ADD_FIELD(trigger_irq);
532 ADD_FIELD(viewport_size_x); 554 ADD_FIELD(viewport_size_x);
533 ADD_FIELD(viewport_size_y); 555 ADD_FIELD(viewport_size_y);
534 ADD_FIELD(viewport_depth_range); 556 ADD_FIELD(viewport_depth_range);
@@ -557,7 +579,6 @@ struct Regs {
557 ADD_FIELD(vs_swizzle_patterns); 579 ADD_FIELD(vs_swizzle_patterns);
558 580
559 #undef ADD_FIELD 581 #undef ADD_FIELD
560 #endif // _MSC_VER
561 582
562 // Return empty string if no match is found 583 // Return empty string if no match is found
563 return map[index]; 584 return map[index];
@@ -593,6 +614,7 @@ private:
593#ifndef _MSC_VER 614#ifndef _MSC_VER
594#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position") 615#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position")
595 616
617ASSERT_REG_POSITION(trigger_irq, 0x10);
596ASSERT_REG_POSITION(viewport_size_x, 0x41); 618ASSERT_REG_POSITION(viewport_size_x, 0x41);
597ASSERT_REG_POSITION(viewport_size_y, 0x43); 619ASSERT_REG_POSITION(viewport_size_y, 0x43);
598ASSERT_REG_POSITION(viewport_depth_range, 0x4d); 620ASSERT_REG_POSITION(viewport_depth_range, 0x4d);
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index dabf2d1a3..102693ed9 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
43 break; 43 break;
44 44
45 default: 45 default:
46 ERROR_LOG(GPU, "Unknown triangle topology %x:", (int)topology); 46 LOG_ERROR(Render_Software, "Unknown triangle topology %x:", (int)topology);
47 break; 47 break;
48 } 48 }
49} 49}
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index a35f0c0d8..b7e04a560 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -252,7 +252,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
252 return combiner_output.rgb(); 252 return combiner_output.rgb();
253 253
254 default: 254 default:
255 ERROR_LOG(GPU, "Unknown color combiner source %d\n", (int)source); 255 LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source);
256 return {}; 256 return {};
257 } 257 }
258 }; 258 };
@@ -272,7 +272,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
272 return combiner_output.a(); 272 return combiner_output.a();
273 273
274 default: 274 default:
275 ERROR_LOG(GPU, "Unknown alpha combiner source %d\n", (int)source); 275 LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source);
276 return 0; 276 return 0;
277 } 277 }
278 }; 278 };
@@ -283,7 +283,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
283 case ColorModifier::SourceColor: 283 case ColorModifier::SourceColor:
284 return values; 284 return values;
285 default: 285 default:
286 ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); 286 LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor);
287 return {}; 287 return {};
288 } 288 }
289 }; 289 };
@@ -293,7 +293,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
293 case AlphaModifier::SourceAlpha: 293 case AlphaModifier::SourceAlpha:
294 return value; 294 return value;
295 default: 295 default:
296 ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); 296 LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor);
297 return 0; 297 return 0;
298 } 298 }
299 }; 299 };
@@ -307,7 +307,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
307 return ((input[0] * input[1]) / 255).Cast<u8>(); 307 return ((input[0] * input[1]) / 255).Cast<u8>();
308 308
309 default: 309 default:
310 ERROR_LOG(GPU, "Unknown color combiner operation %d\n", (int)op); 310 LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op);
311 return {}; 311 return {};
312 } 312 }
313 }; 313 };
@@ -321,7 +321,7 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
321 return input[0] * input[1] / 255; 321 return input[0] * input[1] / 255;
322 322
323 default: 323 default:
324 ERROR_LOG(GPU, "Unknown alpha combiner operation %d\n", (int)op); 324 LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op);
325 return 0; 325 return 0;
326 } 326 }
327 }; 327 };
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index f1dbc9d17..bce402b88 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -25,7 +25,7 @@ public:
25 /// Swap buffers (render frame) 25 /// Swap buffers (render frame)
26 virtual void SwapBuffers() = 0; 26 virtual void SwapBuffers() = 0;
27 27
28 /** 28 /**
29 * Set the emulator window to use for renderer 29 * Set the emulator window to use for renderer
30 * @param window EmuWindow handle to emulator window to use for rendering 30 * @param window EmuWindow handle to emulator window to use for rendering
31 */ 31 */
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index a0eb0418c..d0f82e6cd 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) {
20 int info_log_length; 20 int info_log_length;
21 21
22 // Compile Vertex Shader 22 // Compile Vertex Shader
23 DEBUG_LOG(GPU, "Compiling vertex shader."); 23 LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
24 24
25 glShaderSource(vertex_shader_id, 1, &vertex_shader, NULL); 25 glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
26 glCompileShader(vertex_shader_id); 26 glCompileShader(vertex_shader_id);
27 27
28 // Check Vertex Shader 28 // Check Vertex Shader
@@ -31,14 +31,18 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
31 31
32 if (info_log_length > 1) { 32 if (info_log_length > 1) {
33 std::vector<char> vertex_shader_error(info_log_length); 33 std::vector<char> vertex_shader_error(info_log_length);
34 glGetShaderInfoLog(vertex_shader_id, info_log_length, NULL, &vertex_shader_error[0]); 34 glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
35 DEBUG_LOG(GPU, "%s", &vertex_shader_error[0]); 35 if (result) {
36 LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
37 } else {
38 LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]);
39 }
36 } 40 }
37 41
38 // Compile Fragment Shader 42 // Compile Fragment Shader
39 DEBUG_LOG(GPU, "Compiling fragment shader."); 43 LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
40 44
41 glShaderSource(fragment_shader_id, 1, &fragment_shader, NULL); 45 glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
42 glCompileShader(fragment_shader_id); 46 glCompileShader(fragment_shader_id);
43 47
44 // Check Fragment Shader 48 // Check Fragment Shader
@@ -47,12 +51,16 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
47 51
48 if (info_log_length > 1) { 52 if (info_log_length > 1) {
49 std::vector<char> fragment_shader_error(info_log_length); 53 std::vector<char> fragment_shader_error(info_log_length);
50 glGetShaderInfoLog(fragment_shader_id, info_log_length, NULL, &fragment_shader_error[0]); 54 glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
51 DEBUG_LOG(GPU, "%s", &fragment_shader_error[0]); 55 if (result) {
56 LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
57 } else {
58 LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s", &fragment_shader_error[0]);
59 }
52 } 60 }
53 61
54 // Link the program 62 // Link the program
55 DEBUG_LOG(GPU, "Linking program."); 63 LOG_DEBUG(Render_OpenGL, "Linking program...");
56 64
57 GLuint program_id = glCreateProgram(); 65 GLuint program_id = glCreateProgram();
58 glAttachShader(program_id, vertex_shader_id); 66 glAttachShader(program_id, vertex_shader_id);
@@ -65,8 +73,12 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
65 73
66 if (info_log_length > 1) { 74 if (info_log_length > 1) {
67 std::vector<char> program_error(info_log_length); 75 std::vector<char> program_error(info_log_length);
68 glGetProgramInfoLog(program_id, info_log_length, NULL, &program_error[0]); 76 glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]);
69 DEBUG_LOG(GPU, "%s", &program_error[0]); 77 if (result) {
78 LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]);
79 } else {
80 LOG_ERROR(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]);
81 }
70 } 82 }
71 83
72 glDeleteShader(vertex_shader_id); 84 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 8483f79be..e2caeeb8f 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() {
61 for(int i : {0, 1}) { 61 for(int i : {0, 1}) {
62 const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; 62 const auto& framebuffer = GPU::g_regs.framebuffer_config[i];
63 63
64 if (textures[i].width != framebuffer.width || textures[i].height != framebuffer.height) { 64 if (textures[i].width != (GLsizei)framebuffer.width || textures[i].height != (GLsizei)framebuffer.height) {
65 // Reallocate texture if the framebuffer size has changed. 65 // Reallocate texture if the framebuffer size has changed.
66 // This is expected to not happen very often and hence should not be a 66 // This is expected to not happen very often and hence should not be a
67 // performance problem. 67 // performance problem.
@@ -90,7 +90,7 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
90 const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress( 90 const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress(
91 framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1); 91 framebuffer.active_fb == 1 ? framebuffer.address_left2 : framebuffer.address_left1);
92 92
93 DEBUG_LOG(GPU, "0x%08x bytes from 0x%08x(%dx%d), fmt %x", 93 LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
94 framebuffer.stride * framebuffer.height, 94 framebuffer.stride * framebuffer.height,
95 framebuffer_vaddr, (int)framebuffer.width, 95 framebuffer_vaddr, (int)framebuffer.width,
96 (int)framebuffer.height, (int)framebuffer.format); 96 (int)framebuffer.height, (int)framebuffer.format);
@@ -98,15 +98,15 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig&
98 const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr); 98 const u8* framebuffer_data = Memory::GetPointer(framebuffer_vaddr);
99 99
100 // TODO: Handle other pixel formats 100 // TODO: Handle other pixel formats
101 _dbg_assert_msg_(RENDER, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8, 101 _dbg_assert_msg_(Render_OpenGL, framebuffer.color_format == GPU::Regs::PixelFormat::RGB8,
102 "Unsupported 3DS pixel format."); 102 "Unsupported 3DS pixel format.");
103 103
104 size_t pixel_stride = framebuffer.stride / 3; 104 size_t pixel_stride = framebuffer.stride / 3;
105 // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately 105 // OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately
106 _dbg_assert_(RENDER, pixel_stride * 3 == framebuffer.stride); 106 _dbg_assert_(Render_OpenGL, pixel_stride * 3 == framebuffer.stride);
107 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default 107 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
108 // only allows rows to have a memory alignement of 4. 108 // only allows rows to have a memory alignement of 4.
109 _dbg_assert_(RENDER, pixel_stride % 4 == 0); 109 _dbg_assert_(Render_OpenGL, pixel_stride % 4 == 0);
110 110
111 glBindTexture(GL_TEXTURE_2D, texture.handle); 111 glBindTexture(GL_TEXTURE_2D, texture.handle);
112 glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); 112 glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride);
@@ -191,7 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x
191 * Draws the emulated screens to the emulator window. 191 * Draws the emulated screens to the emulator window.
192 */ 192 */
193void RendererOpenGL::DrawScreens() { 193void RendererOpenGL::DrawScreens() {
194 glViewport(0, 0, resolution_width, resolution_height); 194 auto viewport_extent = GetViewportExtent();
195 glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom?
195 glClear(GL_COLOR_BUFFER_BIT); 196 glClear(GL_COLOR_BUFFER_BIT);
196 197
197 glUseProgram(program_id); 198 glUseProgram(program_id);
@@ -228,17 +229,45 @@ void RendererOpenGL::SetWindow(EmuWindow* window) {
228 render_window = window; 229 render_window = window;
229} 230}
230 231
232MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() {
233 unsigned framebuffer_width;
234 unsigned framebuffer_height;
235 std::tie(framebuffer_width, framebuffer_height) = render_window->GetFramebufferSize();
236
237 float window_aspect_ratio = static_cast<float>(framebuffer_height) / framebuffer_width;
238 float emulation_aspect_ratio = static_cast<float>(resolution_height) / resolution_width;
239
240 MathUtil::Rectangle<unsigned> viewport_extent;
241 if (window_aspect_ratio > emulation_aspect_ratio) {
242 // Window is narrower than the emulation content => apply borders to the top and bottom
243 unsigned viewport_height = std::round(emulation_aspect_ratio * framebuffer_width);
244 viewport_extent.left = 0;
245 viewport_extent.top = (framebuffer_height - viewport_height) / 2;
246 viewport_extent.right = viewport_extent.left + framebuffer_width;
247 viewport_extent.bottom = viewport_extent.top + viewport_height;
248 } else {
249 // Otherwise, apply borders to the left and right sides of the window.
250 unsigned viewport_width = std::round(framebuffer_height / emulation_aspect_ratio);
251 viewport_extent.left = (framebuffer_width - viewport_width) / 2;
252 viewport_extent.top = 0;
253 viewport_extent.right = viewport_extent.left + viewport_width;
254 viewport_extent.bottom = viewport_extent.top + framebuffer_height;
255 }
256
257 return viewport_extent;
258}
259
231/// Initialize the renderer 260/// Initialize the renderer
232void RendererOpenGL::Init() { 261void RendererOpenGL::Init() {
233 render_window->MakeCurrent(); 262 render_window->MakeCurrent();
234 263
235 int err = ogl_LoadFunctions(); 264 int err = ogl_LoadFunctions();
236 if (ogl_LOAD_SUCCEEDED != err) { 265 if (ogl_LOAD_SUCCEEDED != err) {
237 ERROR_LOG(RENDER, "Failed to initialize GL functions! Exiting..."); 266 LOG_CRITICAL(Render_OpenGL, "Failed to initialize GL functions! Exiting...");
238 exit(-1); 267 exit(-1);
239 } 268 }
240 269
241 NOTICE_LOG(RENDER, "GL_VERSION: %s\n", glGetString(GL_VERSION)); 270 LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION));
242 InitOpenGLObjects(); 271 InitOpenGLObjects();
243} 272}
244 273
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index eed201a95..7fdcec731 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -4,13 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8
7#include "generated/gl_3_2_core.h" 9#include "generated/gl_3_2_core.h"
8 10
9#include "common/common.h" 11#include "common/math_util.h"
12
10#include "core/hw/gpu.h" 13#include "core/hw/gpu.h"
11#include "video_core/renderer_base.h"
12 14
13#include <array> 15#include "video_core/renderer_base.h"
14 16
15class EmuWindow; 17class EmuWindow;
16 18
@@ -52,6 +54,9 @@ private:
52 static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, 54 static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
53 const TextureInfo& texture); 55 const TextureInfo& texture);
54 56
57 /// Computes the viewport rectangle
58 MathUtil::Rectangle<unsigned> GetViewportExtent();
59
55 EmuWindow* render_window; ///< Handle to render window 60 EmuWindow* render_window; ///< Handle to render window
56 u32 last_mode; ///< Last render mode 61 u32 last_mode; ///< Last render mode
57 62
diff --git a/src/video_core/utils.cpp b/src/video_core/utils.cpp
index c1848f923..f1156a493 100644
--- a/src/video_core/utils.cpp
+++ b/src/video_core/utils.cpp
@@ -20,7 +20,7 @@ namespace VideoCore {
20void DumpTGA(std::string filename, short width, short height, u8* raw_data) { 20void DumpTGA(std::string filename, short width, short height, u8* raw_data) {
21 TGAHeader hdr = {0, 0, 2, 0, 0, 0, 0, width, height, 24, 0}; 21 TGAHeader hdr = {0, 0, 2, 0, 0, 0, 0, width, height, 24, 0};
22 FILE* fout = fopen(filename.c_str(), "wb"); 22 FILE* fout = fopen(filename.c_str(), "wb");
23 23
24 fwrite(&hdr, sizeof(TGAHeader), 1, fout); 24 fwrite(&hdr, sizeof(TGAHeader), 1, fout);
25 25
26 for (int y = 0; y < height; y++) { 26 for (int y = 0; y < height; y++) {
@@ -30,7 +30,7 @@ void DumpTGA(std::string filename, short width, short height, u8* raw_data) {
30 putc(raw_data[(3 * (y * width)) + (3 * x) + 2], fout); // r 30 putc(raw_data[(3 * (y * width)) + (3 * x) + 2], fout); // r
31 } 31 }
32 } 32 }
33 33
34 fclose(fout); 34 fclose(fout);
35} 35}
36} // namespace 36} // namespace
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 9cb3d4d43..21380a908 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -12,24 +12,24 @@ namespace FormatPrecision {
12 12
13/// Adjust RGBA8 color with RGBA6 precision 13/// Adjust RGBA8 color with RGBA6 precision
14static inline u32 rgba8_with_rgba6(u32 src) { 14static inline u32 rgba8_with_rgba6(u32 src) {
15 u32 color = src; 15 u32 color = src;
16 color &= 0xFCFCFCFC; 16 color &= 0xFCFCFCFC;
17 color |= (color >> 6) & 0x03030303; 17 color |= (color >> 6) & 0x03030303;
18 return color; 18 return color;
19} 19}
20 20
21/// Adjust RGBA8 color with RGB565 precision 21/// Adjust RGBA8 color with RGB565 precision
22static inline u32 rgba8_with_rgb565(u32 src) { 22static inline u32 rgba8_with_rgb565(u32 src) {
23 u32 color = (src & 0xF8FCF8); 23 u32 color = (src & 0xF8FCF8);
24 color |= (color >> 5) & 0x070007; 24 color |= (color >> 5) & 0x070007;
25 color |= (color >> 6) & 0x000300; 25 color |= (color >> 6) & 0x000300;
26 color |= 0xFF000000; 26 color |= 0xFF000000;
27 return color; 27 return color;
28} 28}
29 29
30/// Adjust Z24 depth value with Z16 precision 30/// Adjust Z24 depth value with Z16 precision
31static inline u32 z24_with_z16(u32 src) { 31static inline u32 z24_with_z16(u32 src) {
32 return (src & 0xFFFF00) | (src >> 16); 32 return (src & 0xFFFF00) | (src >> 16);
33} 33}
34 34
35} // namespace 35} // namespace
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp
index 96625791c..477e78cfe 100644
--- a/src/video_core/vertex_shader.cpp
+++ b/src/video_core/vertex_shader.cpp
@@ -2,11 +2,16 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <boost/range/algorithm.hpp>
6
7#include <common/file_util.h>
8
9#include <core/mem_map.h>
10
11#include "debug_utils/debug_utils.h"
12
5#include "pica.h" 13#include "pica.h"
6#include "vertex_shader.h" 14#include "vertex_shader.h"
7#include "debug_utils/debug_utils.h"
8#include <core/mem_map.h>
9#include <common/file_util.h>
10 15
11namespace Pica { 16namespace Pica {
12 17
@@ -201,7 +206,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
201 case Instruction::OpCode::CALL: 206 case Instruction::OpCode::CALL:
202 increment_pc = false; 207 increment_pc = false;
203 208
204 _dbg_assert_(GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); 209 _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack));
205 210
206 *++state.call_stack_pointer = state.program_counter - shader_memory; 211 *++state.call_stack_pointer = state.program_counter - shader_memory;
207 // TODO: Does this offset refer to the beginning of shader memory? 212 // TODO: Does this offset refer to the beginning of shader memory?
@@ -213,7 +218,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
213 break; 218 break;
214 219
215 default: 220 default:
216 ERROR_LOG(GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", 221 LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x",
217 (int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex); 222 (int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex);
218 break; 223 break;
219 } 224 }
@@ -238,7 +243,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes)
238 // Setup input register table 243 // Setup input register table
239 const auto& attribute_register_map = registers.vs_input_register_map; 244 const auto& attribute_register_map = registers.vs_input_register_map;
240 float24 dummy_register; 245 float24 dummy_register;
241 std::fill(&state.input_register_table[0], &state.input_register_table[16], &dummy_register); 246 boost::fill(state.input_register_table, &dummy_register);
242 if(num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x; 247 if(num_attributes > 0) state.input_register_table[attribute_register_map.attribute0_register] = &input.attr[0].x;
243 if(num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x; 248 if(num_attributes > 1) state.input_register_table[attribute_register_map.attribute1_register] = &input.attr[1].x;
244 if(num_attributes > 2) state.input_register_table[attribute_register_map.attribute2_register] = &input.attr[2].x; 249 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)
272 277
273 state.status_registers[0] = false; 278 state.status_registers[0] = false;
274 state.status_registers[1] = false; 279 state.status_registers[1] = false;
275 std::fill(state.call_stack, state.call_stack + sizeof(state.call_stack) / sizeof(state.call_stack[0]), 280 boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS);
276 VertexShaderState::INVALID_ADDRESS);
277 state.call_stack_pointer = &state.call_stack[0]; 281 state.call_stack_pointer = &state.call_stack[0];
278 282
279 ProcessShaderCode(state); 283 ProcessShaderCode(state);
@@ -281,7 +285,7 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes)
281 state.debug.max_opdesc_id, registers.vs_main_offset, 285 state.debug.max_opdesc_id, registers.vs_main_offset,
282 registers.vs_output_attributes); 286 registers.vs_output_attributes);
283 287
284 DEBUG_LOG(GPU, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)", 288 LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
285 ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(), 289 ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
286 ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(), 290 ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),
287 ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32()); 291 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 607a8e803..bfb6fb6e3 100644
--- a/src/video_core/vertex_shader.h
+++ b/src/video_core/vertex_shader.h
@@ -141,7 +141,7 @@ union Instruction {
141 return BitFieldType::Value(); 141 return BitFieldType::Value();
142 else if (GetRegisterType() == Temporary) 142 else if (GetRegisterType() == Temporary)
143 return BitFieldType::Value() - 0x10; 143 return BitFieldType::Value() - 0x10;
144 else if (GetRegisterType() == FloatUniform) 144 else // if (GetRegisterType() == FloatUniform)
145 return BitFieldType::Value() - 0x20; 145 return BitFieldType::Value() - 0x20;
146 } 146 }
147 147
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index c779771c5..6791e4007 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -17,8 +17,8 @@
17 17
18namespace VideoCore { 18namespace VideoCore {
19 19
20EmuWindow* g_emu_window = NULL; ///< Frontend emulator window 20EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
21RendererBase* g_renderer = NULL; ///< Renderer plugin 21RendererBase* g_renderer = nullptr; ///< Renderer plugin
22int g_current_frame = 0; 22int g_current_frame = 0;
23 23
24/// Initialize the video core 24/// Initialize the video core
@@ -30,13 +30,13 @@ void Init(EmuWindow* emu_window) {
30 30
31 g_current_frame = 0; 31 g_current_frame = 0;
32 32
33 NOTICE_LOG(VIDEO, "initialized OK"); 33 LOG_DEBUG(Render, "initialized OK");
34} 34}
35 35
36/// Shutdown the video core 36/// Shutdown the video core
37void Shutdown() { 37void Shutdown() {
38 delete g_renderer; 38 delete g_renderer;
39 NOTICE_LOG(VIDEO, "shutdown OK"); 39 LOG_DEBUG(Render, "shutdown OK");
40} 40}
41 41
42} // namespace 42} // namespace