summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp6
-rw-r--r--src/citra_qt/CMakeLists.txt7
-rw-r--r--src/citra_qt/bootmanager.cpp15
-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/hotkeys.cpp4
-rw-r--r--src/citra_qt/main.cpp26
-rw-r--r--src/citra_qt/util/spinbox.cpp303
-rw-r--r--src/citra_qt/util/spinbox.hxx88
-rw-r--r--src/common/chunk_file.h24
-rw-r--r--src/common/common_funcs.h8
-rw-r--r--src/common/console_listener.cpp10
-rw-r--r--src/common/extended_trace.cpp24
-rw-r--r--src/common/fifo_queue.h4
-rw-r--r--src/common/file_util.cpp24
-rw-r--r--src/common/file_util.h4
-rw-r--r--src/common/linear_disk_cache.h2
-rw-r--r--src/common/log.h4
-rw-r--r--src/common/log_manager.cpp5
-rw-r--r--src/common/mem_arena.cpp20
-rw-r--r--src/common/memory_util.cpp10
-rw-r--r--src/common/misc.cpp4
-rw-r--r--src/common/platform.h2
-rw-r--r--src/common/string_util.cpp12
-rw-r--r--src/common/thread_queue_list.h12
-rw-r--r--src/common/timer.cpp6
-rw-r--r--src/common/utf8.cpp24
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/interpreter/armemu.cpp8
-rw-r--r--src/core/core_timing.cpp12
-rw-r--r--src/core/file_sys/archive_sdmc.cpp2
-rw-r--r--src/core/file_sys/directory.h6
-rw-r--r--src/core/file_sys/directory_romfs.cpp4
-rw-r--r--src/core/file_sys/directory_romfs.h6
-rw-r--r--src/core/file_sys/directory_sdmc.cpp13
-rw-r--r--src/core/file_sys/directory_sdmc.h7
-rw-r--r--src/core/file_sys/file_sdmc.cpp9
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/archive.cpp5
-rw-r--r--src/core/hle/kernel/thread.cpp29
-rw-r--r--src/core/hle/kernel/thread.h11
-rw-r--r--src/core/hle/service/cfg_u.cpp92
-rw-r--r--src/core/hle/service/gsp_gpu.cpp23
-rw-r--r--src/core/hle/service/service.h24
-rw-r--r--src/core/hw/gpu.cpp3
-rw-r--r--src/core/hw/gpu.h2
-rw-r--r--src/core/hw/hw.cpp13
-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/loader.cpp7
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/mem_map.h10
-rw-r--r--src/core/mem_map_funcs.cpp42
-rw-r--r--src/video_core/command_processor.cpp22
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp110
-rw-r--r--src/video_core/debug_utils/debug_utils.h146
-rw-r--r--src/video_core/pica.h38
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp10
-rw-r--r--src/video_core/video_core.cpp4
66 files changed, 2279 insertions, 379 deletions
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
index 697bf4693..982619126 100644
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ b/src/citra/emu_window/emu_window_glfw.cpp
@@ -76,9 +76,9 @@ EmuWindow_GLFW::EmuWindow_GLFW() {
76 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); 76 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
77 m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, 77 m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
78 (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), 78 (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
79 window_title.c_str(), NULL, NULL); 79 window_title.c_str(), nullptr, nullptr);
80 80
81 if (m_render_window == NULL) { 81 if (m_render_window == nullptr) {
82 ERROR_LOG(GUI, "Failed to create GLFW window! Exiting..."); 82 ERROR_LOG(GUI, "Failed to create GLFW window! Exiting...");
83 exit(1); 83 exit(1);
84 } 84 }
@@ -123,7 +123,7 @@ void EmuWindow_GLFW::MakeCurrent() {
123 123
124/// 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
125void EmuWindow_GLFW::DoneCurrent() { 125void EmuWindow_GLFW::DoneCurrent() {
126 glfwMakeContextCurrent(NULL); 126 glfwMakeContextCurrent(nullptr);
127} 127}
128 128
129void EmuWindow_GLFW::ReloadSetKeymaps() { 129void EmuWindow_GLFW::ReloadSetKeymaps() {
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 9bf079919..b53206be6 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -14,6 +14,8 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/settings.h" 15#include "core/settings.h"
16 16
17#include "video_core/debug_utils/debug_utils.h"
18
17#include "video_core/video_core.h" 19#include "video_core/video_core.h"
18 20
19#include "citra_qt/version.h" 21#include "citra_qt/version.h"
@@ -65,14 +67,21 @@ void EmuThread::Stop()
65 } 67 }
66 stop_run = true; 68 stop_run = true;
67 69
70 // Release emu threads from any breakpoints, so that this doesn't hang forever.
71 Pica::g_debug_context->ClearBreakpoints();
72
68 //core::g_state = core::SYS_DIE; 73 //core::g_state = core::SYS_DIE;
69 74
70 wait(500); 75 // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
76 wait(1000);
71 if (isRunning()) 77 if (isRunning())
72 { 78 {
73 WARN_LOG(MASTER_LOG, "EmuThread still running, terminating..."); 79 WARN_LOG(MASTER_LOG, "EmuThread still running, terminating...");
74 quit(); 80 quit();
75 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);
76 if (isRunning()) 85 if (isRunning())
77 { 86 {
78 WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here..."); 87 WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here...");
@@ -230,7 +239,7 @@ QByteArray GRenderWindow::saveGeometry()
230{ 239{
231 // If we are a top-level widget, store the current geometry 240 // If we are a top-level widget, store the current geometry
232 // otherwise, store the last backup 241 // otherwise, store the last backup
233 if (parent() == NULL) 242 if (parent() == nullptr)
234 return ((QGLWidget*)this)->saveGeometry(); 243 return ((QGLWidget*)this)->saveGeometry();
235 else 244 else
236 return geometry; 245 return geometry;
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp
new file mode 100644
index 000000000..df5579e15
--- /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_(GUI, 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/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 d5554d917..b4e3ad964 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -20,7 +20,9 @@
20#include "debugger/callstack.hxx" 20#include "debugger/callstack.hxx"
21#include "debugger/ramview.hxx" 21#include "debugger/ramview.hxx"
22#include "debugger/graphics.hxx" 22#include "debugger/graphics.hxx"
23#include "debugger/graphics_breakpoints.hxx"
23#include "debugger/graphics_cmdlists.hxx" 24#include "debugger/graphics_cmdlists.hxx"
25#include "debugger/graphics_framebuffer.hxx"
24 26
25#include "core/settings.h" 27#include "core/settings.h"
26#include "core/system.h" 28#include "core/system.h"
@@ -36,6 +38,8 @@ GMainWindow::GMainWindow()
36{ 38{
37 LogManager::Init(); 39 LogManager::Init();
38 40
41 Pica::g_debug_context = Pica::DebugContext::Construct();
42
39 Config config; 43 Config config;
40 44
41 if (!Settings::values.enable_log) 45 if (!Settings::values.enable_log)
@@ -67,12 +71,22 @@ GMainWindow::GMainWindow()
67 addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget); 71 addDockWidget(Qt::RightDockWidgetArea, graphicsCommandsWidget);
68 graphicsCommandsWidget->hide(); 72 graphicsCommandsWidget->hide();
69 73
74 auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(Pica::g_debug_context, this);
75 addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
76 graphicsBreakpointsWidget->hide();
77
78 auto graphicsFramebufferWidget = new GraphicsFramebufferWidget(Pica::g_debug_context, this);
79 addDockWidget(Qt::RightDockWidgetArea, graphicsFramebufferWidget);
80 graphicsFramebufferWidget->hide();
81
70 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); 82 QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging"));
71 debug_menu->addAction(disasmWidget->toggleViewAction()); 83 debug_menu->addAction(disasmWidget->toggleViewAction());
72 debug_menu->addAction(registersWidget->toggleViewAction()); 84 debug_menu->addAction(registersWidget->toggleViewAction());
73 debug_menu->addAction(callstackWidget->toggleViewAction()); 85 debug_menu->addAction(callstackWidget->toggleViewAction());
74 debug_menu->addAction(graphicsWidget->toggleViewAction()); 86 debug_menu->addAction(graphicsWidget->toggleViewAction());
75 debug_menu->addAction(graphicsCommandsWidget->toggleViewAction()); 87 debug_menu->addAction(graphicsCommandsWidget->toggleViewAction());
88 debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
89 debug_menu->addAction(graphicsFramebufferWidget->toggleViewAction());
76 90
77 // Set default UI state 91 // Set default UI state
78 // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half 92 // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
@@ -131,8 +145,10 @@ GMainWindow::GMainWindow()
131GMainWindow::~GMainWindow() 145GMainWindow::~GMainWindow()
132{ 146{
133 // will get automatically deleted otherwise 147 // will get automatically deleted otherwise
134 if (render_window->parent() == NULL) 148 if (render_window->parent() == nullptr)
135 delete render_window; 149 delete render_window;
150
151 Pica::g_debug_context.reset();
136} 152}
137 153
138void GMainWindow::BootGame(std::string filename) 154void GMainWindow::BootGame(std::string filename)
@@ -164,7 +180,7 @@ void GMainWindow::BootGame(std::string filename)
164 180
165void GMainWindow::OnMenuLoadFile() 181void GMainWindow::OnMenuLoadFile()
166{ 182{
167 QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.elf *.axf *.bin *.cci *.cxi)")); 183 QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS executable (*.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
168 if (filename.size()) 184 if (filename.size())
169 BootGame(filename.toLatin1().data()); 185 BootGame(filename.toLatin1().data());
170} 186}
@@ -213,14 +229,14 @@ void GMainWindow::OnOpenHotkeysDialog()
213void GMainWindow::ToggleWindowMode() 229void GMainWindow::ToggleWindowMode()
214{ 230{
215 bool enable = ui.action_Popout_Window_Mode->isChecked(); 231 bool enable = ui.action_Popout_Window_Mode->isChecked();
216 if (enable && render_window->parent() != NULL) 232 if (enable && render_window->parent() != nullptr)
217 { 233 {
218 ui.horizontalLayout->removeWidget(render_window); 234 ui.horizontalLayout->removeWidget(render_window);
219 render_window->setParent(NULL); 235 render_window->setParent(nullptr);
220 render_window->setVisible(true); 236 render_window->setVisible(true);
221 render_window->RestoreGeometry(); 237 render_window->RestoreGeometry();
222 } 238 }
223 else if (!enable && render_window->parent() == NULL) 239 else if (!enable && render_window->parent() == nullptr)
224 { 240 {
225 render_window->BackupGeometry(); 241 render_window->BackupGeometry();
226 ui.horizontalLayout->addWidget(render_window); 242 ui.horizontalLayout->addWidget(render_window);
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp
new file mode 100644
index 000000000..80dc67d7d
--- /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_(GUI, 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/chunk_file.h b/src/common/chunk_file.h
index 609784076..32af74594 100644
--- a/src/common/chunk_file.h
+++ b/src/common/chunk_file.h
@@ -204,11 +204,11 @@ public:
204 { 204 {
205 for (auto it = x.begin(), end = x.end(); it != end; ++it) 205 for (auto it = x.begin(), end = x.end(); it != end; ++it)
206 { 206 {
207 if (it->second != NULL) 207 if (it->second != nullptr)
208 delete it->second; 208 delete it->second;
209 } 209 }
210 } 210 }
211 T *dv = NULL; 211 T *dv = nullptr;
212 DoMap(x, dv); 212 DoMap(x, dv);
213 } 213 }
214 214
@@ -264,11 +264,11 @@ public:
264 { 264 {
265 for (auto it = x.begin(), end = x.end(); it != end; ++it) 265 for (auto it = x.begin(), end = x.end(); it != end; ++it)
266 { 266 {
267 if (it->second != NULL) 267 if (it->second != nullptr)
268 delete it->second; 268 delete it->second;
269 } 269 }
270 } 270 }
271 T *dv = NULL; 271 T *dv = nullptr;
272 DoMultimap(x, dv); 272 DoMultimap(x, dv);
273 } 273 }
274 274
@@ -320,7 +320,7 @@ public:
320 template<class T> 320 template<class T>
321 void Do(std::vector<T *> &x) 321 void Do(std::vector<T *> &x)
322 { 322 {
323 T *dv = NULL; 323 T *dv = nullptr;
324 DoVector(x, dv); 324 DoVector(x, dv);
325 } 325 }
326 326
@@ -369,7 +369,7 @@ public:
369 template<class T> 369 template<class T>
370 void Do(std::deque<T *> &x) 370 void Do(std::deque<T *> &x)
371 { 371 {
372 T *dv = NULL; 372 T *dv = nullptr;
373 DoDeque(x, dv); 373 DoDeque(x, dv);
374 } 374 }
375 375
@@ -395,7 +395,7 @@ public:
395 template<class T> 395 template<class T>
396 void Do(std::list<T *> &x) 396 void Do(std::list<T *> &x)
397 { 397 {
398 T *dv = NULL; 398 T *dv = nullptr;
399 Do(x, dv); 399 Do(x, dv);
400 } 400 }
401 401
@@ -433,7 +433,7 @@ public:
433 { 433 {
434 for (auto it = x.begin(), end = x.end(); it != end; ++it) 434 for (auto it = x.begin(), end = x.end(); it != end; ++it)
435 { 435 {
436 if (*it != NULL) 436 if (*it != nullptr)
437 delete *it; 437 delete *it;
438 } 438 }
439 } 439 }
@@ -518,7 +518,7 @@ public:
518 void DoClass(T *&x) { 518 void DoClass(T *&x) {
519 if (mode == MODE_READ) 519 if (mode == MODE_READ)
520 { 520 {
521 if (x != NULL) 521 if (x != nullptr)
522 delete x; 522 delete x;
523 x = new T(); 523 x = new T();
524 } 524 }
@@ -567,7 +567,7 @@ public:
567 { 567 {
568 if (mode == MODE_READ) 568 if (mode == MODE_READ)
569 { 569 {
570 cur->next = 0; 570 cur->next = nullptr;
571 list_cur = cur; 571 list_cur = cur;
572 if (prev) 572 if (prev)
573 prev->next = cur; 573 prev->next = cur;
@@ -586,13 +586,13 @@ public:
586 if (mode == MODE_READ) 586 if (mode == MODE_READ)
587 { 587 {
588 if (prev) 588 if (prev)
589 prev->next = 0; 589 prev->next = nullptr;
590 if (list_end) 590 if (list_end)
591 *list_end = prev; 591 *list_end = prev;
592 if (list_cur) 592 if (list_cur)
593 { 593 {
594 if (list_start == list_cur) 594 if (list_start == list_cur)
595 list_start = 0; 595 list_start = nullptr;
596 do 596 do
597 { 597 {
598 LinkedListItem<T>* next = list_cur->next; 598 LinkedListItem<T>* next = list_cur->next;
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index d84ec4c42..db041780a 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common_types.h"
8
7#ifdef _WIN32 9#ifdef _WIN32
8#define SLEEP(x) Sleep(x) 10#define SLEEP(x) Sleep(x)
9#else 11#else
@@ -37,6 +39,8 @@ template<> struct CompileTimeAssert<true> {};
37#include <sys/endian.h> 39#include <sys/endian.h>
38#endif 40#endif
39 41
42#include "common_types.h"
43
40// go to debugger mode 44// go to debugger mode
41 #ifdef GEKKO 45 #ifdef GEKKO
42 #define Crash() 46 #define Crash()
@@ -73,6 +77,8 @@ inline u64 _rotr64(u64 x, unsigned int shift){
73} 77}
74 78
75#else // WIN32 79#else // WIN32
80#include <locale.h>
81
76// Function Cross-Compatibility 82// Function Cross-Compatibility
77 #define strcasecmp _stricmp 83 #define strcasecmp _stricmp
78 #define strncasecmp _strnicmp 84 #define strncasecmp _strnicmp
@@ -106,7 +112,7 @@ inline u64 _rotr64(u64 x, unsigned int shift){
106 // Restore the global locale 112 // Restore the global locale
107 _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); 113 _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
108 } 114 }
109 else if(new_locale != NULL) 115 else if(new_locale != nullptr)
110 { 116 {
111 // Configure the thread to set the locale only for this thread 117 // Configure the thread to set the locale only for this thread
112 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); 118 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
diff --git a/src/common/console_listener.cpp b/src/common/console_listener.cpp
index d7f27c358..b6042796d 100644
--- a/src/common/console_listener.cpp
+++ b/src/common/console_listener.cpp
@@ -16,7 +16,7 @@
16ConsoleListener::ConsoleListener() 16ConsoleListener::ConsoleListener()
17{ 17{
18#ifdef _WIN32 18#ifdef _WIN32
19 hConsole = NULL; 19 hConsole = nullptr;
20 bUseColor = true; 20 bUseColor = true;
21#else 21#else
22 bUseColor = isatty(fileno(stdout)); 22 bUseColor = isatty(fileno(stdout));
@@ -66,19 +66,19 @@ void ConsoleListener::UpdateHandle()
66void ConsoleListener::Close() 66void ConsoleListener::Close()
67{ 67{
68#ifdef _WIN32 68#ifdef _WIN32
69 if (hConsole == NULL) 69 if (hConsole == nullptr)
70 return; 70 return;
71 FreeConsole(); 71 FreeConsole();
72 hConsole = NULL; 72 hConsole = nullptr;
73#else 73#else
74 fflush(NULL); 74 fflush(nullptr);
75#endif 75#endif
76} 76}
77 77
78bool ConsoleListener::IsOpen() 78bool ConsoleListener::IsOpen()
79{ 79{
80#ifdef _WIN32 80#ifdef _WIN32
81 return (hConsole != NULL); 81 return (hConsole != nullptr);
82#else 82#else
83 return true; 83 return true;
84#endif 84#endif
diff --git a/src/common/extended_trace.cpp b/src/common/extended_trace.cpp
index bf61ac1d1..cf7c346d4 100644
--- a/src/common/extended_trace.cpp
+++ b/src/common/extended_trace.cpp
@@ -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, ";" );
@@ -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
@@ -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
@@ -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 }
@@ -332,11 +332,11 @@ void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file )
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;
@@ -389,11 +389,11 @@ void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip,
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;
diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h
index 2c18285d4..b426e6596 100644
--- a/src/common/fifo_queue.h
+++ b/src/common/fifo_queue.h
@@ -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_util.cpp b/src/common/file_util.cpp
index b6dec838c..6c4860503 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -140,7 +140,7 @@ bool CreateDir(const std::string &path)
140{ 140{
141 INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); 141 INFO_LOG(COMMON, "CreateDir: 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)
@@ -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)
@@ -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;
@@ -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
@@ -586,11 +586,11 @@ std::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 ERROR_LOG(COMMON, "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);
@@ -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 }
@@ -826,7 +826,7 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
826} 826}
827 827
828IOFile::IOFile() 828IOFile::IOFile()
829 : m_file(NULL), m_good(true) 829 : m_file(nullptr), m_good(true)
830{} 830{}
831 831
832IOFile::IOFile(std::FILE* file) 832IOFile::IOFile(std::FILE* file)
@@ -834,7 +834,7 @@ IOFile::IOFile(std::FILE* file)
834{} 834{}
835 835
836IOFile::IOFile(const std::string& filename, const char openmode[]) 836IOFile::IOFile(const std::string& filename, const char openmode[])
837 : m_file(NULL), m_good(true) 837 : m_file(nullptr), m_good(true)
838{ 838{
839 Open(filename, openmode); 839 Open(filename, openmode);
840} 840}
@@ -845,7 +845,7 @@ IOFile::~IOFile()
845} 845}
846 846
847IOFile::IOFile(IOFile&& other) 847IOFile::IOFile(IOFile&& other)
848 : m_file(NULL), m_good(true) 848 : m_file(nullptr), m_good(true)
849{ 849{
850 Swap(other); 850 Swap(other);
851} 851}
@@ -880,14 +880,14 @@ bool IOFile::Close()
880 if (!IsOpen() || 0 != std::fclose(m_file)) 880 if (!IsOpen() || 0 != std::fclose(m_file))
881 m_good = false; 881 m_good = false;
882 882
883 m_file = NULL; 883 m_file = nullptr;
884 return m_good; 884 return m_good;
885} 885}
886 886
887std::FILE* IOFile::ReleaseHandle() 887std::FILE* IOFile::ReleaseHandle()
888{ 888{
889 std::FILE* const ret = m_file; 889 std::FILE* const ret = m_file;
890 m_file = NULL; 890 m_file = nullptr;
891 return ret; 891 return ret;
892} 892}
893 893
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 72b80be8a..beaf7174a 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -202,11 +202,11 @@ public:
202 return WriteArray(reinterpret_cast<const char*>(data), length); 202 return WriteArray(reinterpret_cast<const char*>(data), length);
203 } 203 }
204 204
205 bool IsOpen() { return NULL != m_file; } 205 bool IsOpen() { return nullptr != m_file; }
206 206
207 // m_good is set to false when a read, write or other function fails 207 // m_good is set to false when a read, write or other function fails
208 bool IsGood() { return m_good; } 208 bool IsGood() { return m_good; }
209 operator void*() { return m_good ? m_file : NULL; } 209 operator void*() { return m_good ? m_file : nullptr; }
210 210
211 std::FILE* ReleaseHandle(); 211 std::FILE* ReleaseHandle();
212 212
diff --git a/src/common/linear_disk_cache.h b/src/common/linear_disk_cache.h
index f4263f72a..bb1b5174f 100644
--- a/src/common/linear_disk_cache.h
+++ b/src/common/linear_disk_cache.h
@@ -70,7 +70,7 @@ public:
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
diff --git a/src/common/log.h b/src/common/log.h
index 14ad98c08..78f0dae4d 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_funcs.h"
8#include "common/msg_handler.h"
9
7#ifndef LOGGING 10#ifndef LOGGING
8#define LOGGING 11#define LOGGING
9#endif 12#endif
@@ -62,7 +65,6 @@ enum LOG_TYPE {
62 WII_IPC_HID, 65 WII_IPC_HID,
63 KERNEL, 66 KERNEL,
64 SVC, 67 SVC,
65 NDMA,
66 HLE, 68 HLE,
67 RENDER, 69 RENDER,
68 GPU, 70 GPU,
diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp
index 2ef7d98c0..128c15388 100644
--- a/src/common/log_manager.cpp
+++ b/src/common/log_manager.cpp
@@ -21,7 +21,7 @@ void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char*
21 va_end(args); 21 va_end(args);
22} 22}
23 23
24LogManager *LogManager::m_logManager = NULL; 24LogManager *LogManager::m_logManager = nullptr;
25 25
26LogManager::LogManager() 26LogManager::LogManager()
27{ 27{
@@ -68,7 +68,6 @@ LogManager::LogManager()
68 m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); 68 m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER");
69 m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU"); 69 m_Log[LogTypes::GPU] = new LogContainer("GPU", "GPU");
70 m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); 70 m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE");
71 m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA");
72 m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); 71 m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation");
73 m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); 72 m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware");
74 m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); 73 m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
@@ -141,7 +140,7 @@ void LogManager::Init()
141void LogManager::Shutdown() 140void LogManager::Shutdown()
142{ 141{
143 delete m_logManager; 142 delete m_logManager;
144 m_logManager = NULL; 143 m_logManager = nullptr;
145} 144}
146 145
147LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable) 146LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable)
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp
index 67dbaf509..7d4fda0e2 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
@@ -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)
@@ -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);
@@ -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) {
@@ -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 {
@@ -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 b6f66e4e1..93da5500b 100644
--- a/src/common/memory_util.cpp
+++ b/src/common/memory_util.cpp
@@ -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,7 +104,7 @@ 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
@@ -116,7 +116,7 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
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;
@@ -130,7 +130,7 @@ void FreeMemoryPages(void* ptr, size_t size)
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);
@@ -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/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/string_util.cpp b/src/common/string_util.cpp
index dcec9275f..19e162c27 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -31,7 +31,7 @@ std::string ToUpper(std::string str) {
31// faster than sscanf 31// faster than sscanf
32bool AsciiToHex(const char* _szValue, u32& result) 32bool AsciiToHex(const char* _szValue, u32& result)
33{ 33{
34 char *endptr = NULL; 34 char *endptr = nullptr;
35 const u32 value = strtoul(_szValue, &endptr, 16); 35 const u32 value = strtoul(_szValue, &endptr, 16);
36 36
37 if (!endptr || *endptr) 37 if (!endptr || *endptr)
@@ -69,7 +69,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar
69 // will be present in the middle of a multibyte sequence. 69 // will be present in the middle of a multibyte sequence.
70 // 70 //
71 // 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.
72 static locale_t c_locale = NULL; 72 static locale_t c_locale = nullptr;
73 if (!c_locale) 73 if (!c_locale)
74 c_locale = _create_locale(LC_ALL, ".1252"); 74 c_locale = _create_locale(LC_ALL, ".1252");
75 writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args); 75 writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
@@ -92,7 +92,7 @@ bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list ar
92std::string StringFromFormat(const char* format, ...) 92std::string StringFromFormat(const char* format, ...)
93{ 93{
94 va_list args; 94 va_list args;
95 char *buf = NULL; 95 char *buf = nullptr;
96#ifdef _WIN32 96#ifdef _WIN32
97 int required = 0; 97 int required = 0;
98 98
@@ -162,7 +162,7 @@ std::string StripQuotes(const std::string& s)
162 162
163bool TryParse(const std::string &str, u32 *const output) 163bool TryParse(const std::string &str, u32 *const output)
164{ 164{
165 char *endptr = NULL; 165 char *endptr = nullptr;
166 166
167 // Reset errno to a value other than ERANGE 167 // Reset errno to a value other than ERANGE
168 errno = 0; 168 errno = 0;
@@ -528,7 +528,7 @@ std::u16string UTF8ToUTF16(const std::string& input)
528{ 528{
529 std::u16string result; 529 std::u16string result;
530 530
531 iconv_t const conv_desc = iconv_open("UTF-16", "UTF-8"); 531 iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
532 if ((iconv_t)(-1) == conv_desc) 532 if ((iconv_t)(-1) == conv_desc)
533 { 533 {
534 ERROR_LOG(COMMON, "Iconv initialization failure [UTF-8]: %s", strerror(errno)); 534 ERROR_LOG(COMMON, "Iconv initialization failure [UTF-8]: %s", strerror(errno));
@@ -582,7 +582,7 @@ std::u16string UTF8ToUTF16(const std::string& input)
582 582
583std::string UTF16ToUTF8(const std::u16string& input) 583std::string UTF16ToUTF8(const std::u16string& input)
584{ 584{
585 return CodeToUTF8("UTF-16", input); 585 return CodeToUTF8("UTF-16LE", input);
586} 586}
587 587
588std::string CP1252ToUTF8(const std::string& input) 588std::string CP1252ToUTF8(const std::string& input)
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 59efbce4c..7e3b620c7 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -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 48241c3d4..8f6792791 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -59,10 +59,10 @@ set(SRCS
59 hle/svc.cpp 59 hle/svc.cpp
60 hw/gpu.cpp 60 hw/gpu.cpp
61 hw/hw.cpp 61 hw/hw.cpp
62 hw/ndma.cpp
63 loader/elf.cpp 62 loader/elf.cpp
64 loader/loader.cpp 63 loader/loader.cpp
65 loader/ncch.cpp 64 loader/ncch.cpp
65 loader/3dsx.cpp
66 core.cpp 66 core.cpp
67 core_timing.cpp 67 core_timing.cpp
68 mem_map.cpp 68 mem_map.cpp
@@ -139,10 +139,10 @@ set(HEADERS
139 hle/svc.h 139 hle/svc.h
140 hw/gpu.h 140 hw/gpu.h
141 hw/hw.h 141 hw/hw.h
142 hw/ndma.h
143 loader/elf.h 142 loader/elf.h
144 loader/loader.h 143 loader/loader.h
145 loader/ncch.h 144 loader/ncch.h
145 loader/3dsx.h
146 core.h 146 core.h
147 core_timing.h 147 core_timing.h
148 mem_map.h 148 mem_map.h
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp
index 73223874e..d717bd2c8 100644
--- a/src/core/arm/interpreter/armemu.cpp
+++ b/src/core/arm/interpreter/armemu.cpp
@@ -5724,7 +5724,7 @@ L_stm_s_takeabort:
5724 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5724 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5725 s16 b1 = (state->Reg[src2] & 0xFFFF); 5725 s16 b1 = (state->Reg[src2] & 0xFFFF);
5726 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5726 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
5727 state->Reg[tar] = (a1 - a2)&0xFFFF | (((b1 - b2)&0xFFFF)<< 0x10); 5727 state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2)&0xFFFF)<< 0x10);
5728 return 1; 5728 return 1;
5729 } 5729 }
5730 else if ((instr & 0xFF0) == 0xf10)//sadd16 5730 else if ((instr & 0xFF0) == 0xf10)//sadd16
@@ -5736,7 +5736,7 @@ L_stm_s_takeabort:
5736 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5736 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5737 s16 b1 = (state->Reg[src2] & 0xFFFF); 5737 s16 b1 = (state->Reg[src2] & 0xFFFF);
5738 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5738 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
5739 state->Reg[tar] = (a1 + a2)&0xFFFF | (((b1 + b2)&0xFFFF)<< 0x10); 5739 state->Reg[tar] = ((a1 + a2) & 0xFFFF) | (((b1 + b2)&0xFFFF)<< 0x10);
5740 return 1; 5740 return 1;
5741 } 5741 }
5742 else if ((instr & 0xFF0) == 0xf50)//ssax 5742 else if ((instr & 0xFF0) == 0xf50)//ssax
@@ -5748,7 +5748,7 @@ L_stm_s_takeabort:
5748 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5748 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5749 s16 b1 = (state->Reg[src2] & 0xFFFF); 5749 s16 b1 = (state->Reg[src2] & 0xFFFF);
5750 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5750 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
5751 state->Reg[tar] = (a1 - b2) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); 5751 state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10);
5752 return 1; 5752 return 1;
5753 } 5753 }
5754 else if ((instr & 0xFF0) == 0xf30)//sasx 5754 else if ((instr & 0xFF0) == 0xf30)//sasx
@@ -5760,7 +5760,7 @@ L_stm_s_takeabort:
5760 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); 5760 s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF);
5761 s16 b1 = (state->Reg[src2] & 0xFFFF); 5761 s16 b1 = (state->Reg[src2] & 0xFFFF);
5762 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); 5762 s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF);
5763 state->Reg[tar] = (a2 - b1) & 0xFFFF | (((a2 + b1) & 0xFFFF) << 0x10); 5763 state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10);
5764 return 1; 5764 return 1;
5765 } 5765 }
5766 else printf ("Unhandled v6 insn: sadd/ssub\n"); 5766 else printf ("Unhandled v6 insn: sadd/ssub\n");
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 558c6cbf7..bf8acf41f 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -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{
@@ -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 {
@@ -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;
@@ -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)
@@ -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);
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index 169ab0f1c..fc0b9b72d 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -100,6 +100,8 @@ bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys:
100std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const { 100std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const {
101 DEBUG_LOG(FILESYS, "called path=%s", path.DebugStr().c_str()); 101 DEBUG_LOG(FILESYS, "called path=%s", path.DebugStr().c_str());
102 Directory_SDMC* directory = new Directory_SDMC(this, path); 102 Directory_SDMC* directory = new Directory_SDMC(this, path);
103 if (!directory->Open())
104 return nullptr;
103 return std::unique_ptr<Directory>(directory); 105 return std::unique_ptr<Directory>(directory);
104} 106}
105 107
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index e10431337..1bb4101d6 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -42,6 +42,12 @@ public:
42 virtual ~Directory() { } 42 virtual ~Directory() { }
43 43
44 /** 44 /**
45 * Open the directory
46 * @return true if the directory opened correctly
47 */
48 virtual bool Open() = 0;
49
50 /**
45 * List files contained in the directory 51 * List files contained in the directory
46 * @param count Number of entries to return at once in entries 52 * @param count Number of entries to return at once in entries
47 * @param entries Buffer to read data into 53 * @param entries Buffer to read data into
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..e2944099e 100644
--- a/src/core/file_sys/directory_romfs.h
+++ b/src/core/file_sys/directory_romfs.h
@@ -20,6 +20,12 @@ public:
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
index 60a197ce9..0f156a127 100644
--- a/src/core/file_sys/directory_sdmc.cpp
+++ b/src/core/file_sys/directory_sdmc.cpp
@@ -19,15 +19,22 @@ Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const Path& path) {
19 // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass 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. 20 // the root directory we set while opening the archive.
21 // For example, opening /../../usr/bin can give the emulated program your installed programs. 21 // For example, opening /../../usr/bin can give the emulated program your installed programs.
22 std::string absolute_path = archive->GetMountPoint() + path.AsString(); 22 this->path = archive->GetMountPoint() + path.AsString();
23 FileUtil::ScanDirectoryTree(absolute_path, directory); 23
24 children_iterator = directory.children.begin();
25} 24}
26 25
27Directory_SDMC::~Directory_SDMC() { 26Directory_SDMC::~Directory_SDMC() {
28 Close(); 27 Close();
29} 28}
30 29
30bool Directory_SDMC::Open() {
31 if (!FileUtil::IsDirectory(path))
32 return false;
33 FileUtil::ScanDirectoryTree(path, directory);
34 children_iterator = directory.children.begin();
35 return true;
36}
37
31/** 38/**
32 * List files contained in the directory 39 * List files contained in the directory
33 * @param count Number of entries to return at once in entries 40 * @param count Number of entries to return at once in entries
diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h
index 4520d0401..4c08b0d61 100644
--- a/src/core/file_sys/directory_sdmc.h
+++ b/src/core/file_sys/directory_sdmc.h
@@ -23,6 +23,12 @@ public:
23 ~Directory_SDMC() override; 23 ~Directory_SDMC() override;
24 24
25 /** 25 /**
26 * Open the directory
27 * @return true if the directory opened correctly
28 */
29 bool Open() override;
30
31 /**
26 * List files contained in the directory 32 * List files contained in the directory
27 * @param count Number of entries to return at once in entries 33 * @param count Number of entries to return at once in entries
28 * @param entries Buffer to read data into 34 * @param entries Buffer to read data into
@@ -37,6 +43,7 @@ public:
37 bool Close() const override; 43 bool Close() const override;
38 44
39private: 45private:
46 std::string path;
40 u32 total_entries_in_directory; 47 u32 total_entries_in_directory;
41 FileUtil::FSTEntry directory; 48 FileUtil::FSTEntry directory;
42 49
diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp
index a4b90670a..b01d96e3d 100644
--- a/src/core/file_sys/file_sdmc.cpp
+++ b/src/core/file_sys/file_sdmc.cpp
@@ -38,12 +38,15 @@ bool File_SDMC::Open() {
38 } 38 }
39 39
40 std::string mode_string; 40 std::string mode_string;
41 if (mode.read_flag && mode.write_flag) 41 if (mode.create_flag)
42 mode_string = "w+"; 42 mode_string = "w+";
43 else if (mode.write_flag)
44 mode_string = "r+"; // Files opened with Write access can be read from
43 else if (mode.read_flag) 45 else if (mode.read_flag)
44 mode_string = "r"; 46 mode_string = "r";
45 else if (mode.write_flag) 47
46 mode_string = "w"; 48 // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
49 mode_string += "b";
47 50
48 file = new FileUtil::IOFile(path, mode_string.c_str()); 51 file = new FileUtil::IOFile(path, mode_string.c_str());
49 return true; 52 return true;
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index db571b895..ce4f3c854 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -53,7 +53,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
53 // Wait current thread (acquire the arbiter)... 53 // Wait current thread (acquire the arbiter)...
54 case ArbitrationType::WaitIfLessThan: 54 case ArbitrationType::WaitIfLessThan:
55 if ((s32)Memory::Read32(address) <= value) { 55 if ((s32)Memory::Read32(address) <= value) {
56 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); 56 Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
57 HLE::Reschedule(__func__); 57 HLE::Reschedule(__func__);
58 } 58 }
59 break; 59 break;
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
index 647f0dea9..a875fa7ff 100644
--- a/src/core/hle/kernel/archive.cpp
+++ b/src/core/hle/kernel/archive.cpp
@@ -421,6 +421,11 @@ ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys:
421 directory->path = path; 421 directory->path = path;
422 directory->backend = archive->backend->OpenDirectory(path); 422 directory->backend = archive->backend->OpenDirectory(path);
423 423
424 if (!directory->backend) {
425 return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
426 ErrorSummary::NotFound, ErrorLevel::Permanent);
427 }
428
424 return MakeResult<Handle>(handle); 429 return MakeResult<Handle>(handle);
425} 430}
426 431
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c01d76e4d..492b917e1 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -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
@@ -127,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
127 } 128 }
128 t->wait_type = WAITTYPE_NONE; 129 t->wait_type = WAITTYPE_NONE;
129 t->wait_handle = 0; 130 t->wait_handle = 0;
131 t->wait_address = 0;
130} 132}
131 133
132/// Change a thread to "ready" state 134/// Change a thread to "ready" state
@@ -147,11 +149,17 @@ void ChangeReadyState(Thread* t, bool ready) {
147} 149}
148 150
149/// Verify that a thread has not been released from waiting 151/// Verify that a thread has not been released from waiting
150inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { 152static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
151 _dbg_assert_(KERNEL, thread != nullptr); 153 _dbg_assert_(KERNEL, thread != nullptr);
152 return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting()); 154 return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting());
153} 155}
154 156
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);
161}
162
155/// Stops the current thread 163/// Stops the current thread
156ResultCode StopThread(Handle handle, const char* reason) { 164ResultCode StopThread(Handle handle, const char* reason) {
157 Thread* thread = g_object_pool.Get<Thread>(handle); 165 Thread* thread = g_object_pool.Get<Thread>(handle);
@@ -173,6 +181,7 @@ ResultCode StopThread(Handle handle, const char* reason) {
173 // Stopped threads are never waiting. 181 // Stopped threads are never waiting.
174 thread->wait_type = WAITTYPE_NONE; 182 thread->wait_type = WAITTYPE_NONE;
175 thread->wait_handle = 0; 183 thread->wait_handle = 0;
184 thread->wait_address = 0;
176 185
177 return RESULT_SUCCESS; 186 return RESULT_SUCCESS;
178} 187}
@@ -201,12 +210,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
201 for (Handle handle : thread_queue) { 210 for (Handle handle : thread_queue) {
202 Thread* thread = g_object_pool.Get<Thread>(handle); 211 Thread* thread = g_object_pool.Get<Thread>(handle);
203 212
204 // TODO(bunnei): Verify arbiter address... 213 if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
205 if (!VerifyWait(thread, WAITTYPE_ARB, arbiter))
206 continue; 214 continue;
207 215
208 if (thread == nullptr) 216 if (thread == nullptr)
209 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. 217 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
218
210 if(thread->current_priority <= priority) { 219 if(thread->current_priority <= priority) {
211 highest_priority_thread = handle; 220 highest_priority_thread = handle;
212 priority = thread->current_priority; 221 priority = thread->current_priority;
@@ -226,8 +235,7 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) {
226 for (Handle handle : thread_queue) { 235 for (Handle handle : thread_queue) {
227 Thread* thread = g_object_pool.Get<Thread>(handle); 236 Thread* thread = g_object_pool.Get<Thread>(handle);
228 237
229 // TODO(bunnei): Verify arbiter address... 238 if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
230 if (VerifyWait(thread, WAITTYPE_ARB, arbiter))
231 ResumeThreadFromWait(handle); 239 ResumeThreadFromWait(handle);
232 } 240 }
233} 241}
@@ -281,11 +289,6 @@ Thread* NextThread() {
281 return Kernel::g_object_pool.Get<Thread>(next); 289 return Kernel::g_object_pool.Get<Thread>(next);
282} 290}
283 291
284/**
285 * Puts the current thread in the wait state for the given type
286 * @param wait_type Type of wait
287 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
288 */
289void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { 292void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
290 Thread* thread = GetCurrentThread(); 293 Thread* thread = GetCurrentThread();
291 thread->wait_type = wait_type; 294 thread->wait_type = wait_type;
@@ -293,6 +296,11 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
293 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 296 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
294} 297}
295 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
296/// Resumes a thread from waiting by marking it as "ready" 304/// Resumes a thread from waiting by marking it as "ready"
297void ResumeThreadFromWait(Handle handle) { 305void ResumeThreadFromWait(Handle handle) {
298 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); 306 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
@@ -343,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
343 thread->processor_id = processor_id; 351 thread->processor_id = processor_id;
344 thread->wait_type = WAITTYPE_NONE; 352 thread->wait_type = WAITTYPE_NONE;
345 thread->wait_handle = 0; 353 thread->wait_handle = 0;
354 thread->wait_address = 0;
346 thread->name = name; 355 thread->name = name;
347 356
348 return thread; 357 return thread;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 53a19d779..be7adface 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -5,6 +5,9 @@
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"
9#include "core/hle/result.h" 12#include "core/hle/result.h"
10 13
@@ -85,6 +88,14 @@ Handle GetCurrentThreadHandle();
85 */ 88 */
86void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); 89void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
87 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
88/// Put current thread in a wait state - on WaitSynchronization 99/// Put current thread in a wait state - on WaitSynchronization
89void WaitThread_Synchronization(); 100void WaitThread_Synchronization();
90 101
diff --git a/src/core/hle/service/cfg_u.cpp b/src/core/hle/service/cfg_u.cpp
index d6b586ea0..82bab5797 100644
--- a/src/core/hle/service/cfg_u.cpp
+++ b/src/core/hle/service/cfg_u.cpp
@@ -11,33 +11,38 @@
11 11
12namespace CFG_U { 12namespace CFG_U {
13 13
14static const std::array<const char*, 187> country_codes = { 14// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
15 nullptr, "JP", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 0-7 15#define C(code) ((code)[0] | ((code)[1] << 8))
16 "AI", "AG", "AR", "AW", "BS", "BB", "BZ", "BO", // 8-15 16
17 "BR", "VG", "CA", "KY", "CL", "CO", "CR", "DM", // 16-23 17static const std::array<u16, 187> country_codes = {
18 "DO", "EC", "SV", "GF", "GD", "GP", "GT", "GY", // 24-31 18 0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7
19 "HT", "HN", "JM", "MQ", "MX", "MS", "AN", "NI", // 32-39 19 C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15
20 "PA", "PY", "PE", "KN", "LC", "VC", "SR", "TT", // 40-47 20 C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23
21 "TC", "US", "UY", "VI", "VE", nullptr, nullptr, nullptr, // 48-55 21 C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31
22 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 56-63 22 C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39
23 "AL", "AU", "AT", "BE", "BA", "BW", "BG", "HR", // 64-71 23 C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47
24 "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", // 72-79 24 C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55
25 "HU", "IS", "IE", "IT", "LV", "LS", "LI", "LT", // 80-87 25 0, 0, 0, 0, 0, 0, 0, 0, // 56-63
26 "LU", "MK", "MT", "ME", "MZ", "NA", "NL", "NZ", // 88-95 26 C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71
27 "NO", "PL", "PT", "RO", "RU", "RS", "SK", "SI", // 96-103 27 C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79
28 "ZA", "ES", "SZ", "SE", "CH", "TR", "GB", "ZM", // 104-111 28 C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87
29 "ZW", "AZ", "MR", "ML", "NE", "TD", "SD", "ER", // 112-119 29 C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95
30 "DJ", "SO", "AD", "GI", "GG", "IM", "JE", "MC", // 120-127 30 C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103
31 "TW", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 128-135 31 C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111
32 "KR", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 136-143 32 C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119
33 "HK", "MO", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 144-151 33 C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127
34 "ID", "SG", "TH", "PH", "MY", nullptr, nullptr, nullptr, // 152-159 34 C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135
35 "CN", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 160-167 35 C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143
36 "AE", "IN", "EG", "OM", "QA", "KW", "SA", "SY", // 168-175 36 C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151
37 "BH", "JO", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, // 176-183 37 C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159
38 "SM", "VA", "BM", // 184-186 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
39}; 42};
40 43
44#undef C
45
41/** 46/**
42 * CFG_User::GetCountryCodeString service function 47 * CFG_User::GetCountryCodeString service function
43 * Inputs: 48 * Inputs:
@@ -50,20 +55,14 @@ static void GetCountryCodeString(Service::Interface* self) {
50 u32* cmd_buffer = Service::GetCommandBuffer(); 55 u32* cmd_buffer = Service::GetCommandBuffer();
51 u32 country_code_id = cmd_buffer[1]; 56 u32 country_code_id = cmd_buffer[1];
52 57
53 if (country_code_id >= country_codes.size()) { 58 if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
54 ERROR_LOG(KERNEL, "requested country code id=%d is invalid", country_code_id); 59 ERROR_LOG(KERNEL, "requested country code id=%d is invalid", country_code_id);
55 cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; 60 cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
56 return; 61 return;
57 } 62 }
58 63
59 const char* code = country_codes[country_code_id]; 64 cmd_buffer[1] = 0;
60 if (code != nullptr) { 65 cmd_buffer[2] = country_codes[country_code_id];
61 cmd_buffer[1] = 0;
62 cmd_buffer[2] = code[0] | (code[1] << 8);
63 } else {
64 cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
65 DEBUG_LOG(KERNEL, "requested country code id=%d is not set", country_code_id);
66 }
67} 66}
68 67
69/** 68/**
@@ -77,20 +76,25 @@ static void GetCountryCodeString(Service::Interface* self) {
77static void GetCountryCodeID(Service::Interface* self) { 76static void GetCountryCodeID(Service::Interface* self) {
78 u32* cmd_buffer = Service::GetCommandBuffer(); 77 u32* cmd_buffer = Service::GetCommandBuffer();
79 u16 country_code = cmd_buffer[1]; 78 u16 country_code = cmd_buffer[1];
80 u16 country_code_id = -1; 79 u16 country_code_id = 0;
81 80
82 for (u32 i = 0; i < country_codes.size(); ++i) { 81 // The following algorithm will fail if the first country code isn't 0.
83 const char* code_string = country_codes[i]; 82 _dbg_assert_(HLE, country_codes[0] == 0);
84 83
85 if (code_string != nullptr) { 84 for (size_t id = 0; id < country_codes.size(); ++id) {
86 u16 code = code_string[0] | (code_string[1] << 8); 85 if (country_codes[id] == country_code) {
87 if (code == country_code) { 86 country_code_id = id;
88 country_code_id = i; 87 break;
89 break;
90 }
91 } 88 }
92 } 89 }
93 90
91 if (0 == country_code_id) {
92 ERROR_LOG(KERNEL, "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
94 cmd_buffer[1] = 0; 98 cmd_buffer[1] = 0;
95 cmd_buffer[2] = country_code_id; 99 cmd_buffer[2] = country_code_id;
96} 100}
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index de1bd3f61..34eabac45 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -162,7 +162,8 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
162 162
163 _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); 163 _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!");
164 164
165 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
166 cmd_buff[4] = g_shared_memory; // GSP shared memory 167 cmd_buff[4] = g_shared_memory; // GSP shared memory
167 168
168 Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? 169 Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct?
@@ -172,6 +173,7 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
172 * Signals that the specified interrupt type has occurred to userland code 173 * Signals that the specified interrupt type has occurred to userland code
173 * @param interrupt_id ID of interrupt that is being signalled 174 * @param interrupt_id ID of interrupt that is being signalled
174 * @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
175 */ 177 */
176void SignalInterrupt(InterruptId interrupt_id) { 178void SignalInterrupt(InterruptId interrupt_id) {
177 if (0 == g_interrupt_event) { 179 if (0 == g_interrupt_event) {
@@ -210,6 +212,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
210 memcpy(Memory::GetPointer(command.dma_request.dest_address), 212 memcpy(Memory::GetPointer(command.dma_request.dest_address),
211 Memory::GetPointer(command.dma_request.source_address), 213 Memory::GetPointer(command.dma_request.source_address),
212 command.dma_request.size); 214 command.dma_request.size);
215 SignalInterrupt(InterruptId::DMA);
213 break; 216 break;
214 217
215 // ctrulib homebrew sends all relevant command list data with this command, 218 // ctrulib homebrew sends all relevant command list data with this command,
@@ -218,13 +221,13 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
218 case CommandId::SET_COMMAND_LIST_LAST: 221 case CommandId::SET_COMMAND_LIST_LAST:
219 { 222 {
220 auto& params = command.set_command_list_last; 223 auto& params = command.set_command_list_last;
224
221 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);
222 WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3); 226 WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size);
223 227
224 // 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
225 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); 229 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
226 230
227 SignalInterrupt(InterruptId::P3D);
228 break; 231 break;
229 } 232 }
230 233
@@ -242,6 +245,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
242 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);
243 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);
244 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);
245 break; 250 break;
246 } 251 }
247 252
@@ -255,14 +260,9 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
255 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); 260 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
256 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); 261 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
257 262
258 // 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.
259 // work well enough for running demos. Need to figure out how these all work and trigger
260 // them correctly.
261 SignalInterrupt(InterruptId::PSC0);
262 SignalInterrupt(InterruptId::PSC1); 264 SignalInterrupt(InterruptId::PSC1);
263 SignalInterrupt(InterruptId::PPF); 265 SignalInterrupt(InterruptId::PPF);
264 SignalInterrupt(InterruptId::P3D);
265 SignalInterrupt(InterruptId::DMA);
266 266
267 // Update framebuffer information if requested 267 // Update framebuffer information if requested
268 for (int screen_id = 0; screen_id < 2; ++screen_id) { 268 for (int screen_id = 0; screen_id < 2; ++screen_id) {
@@ -305,6 +305,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
305/// 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.
306static void TriggerCmdReqQueue(Service::Interface* self) { 306static void TriggerCmdReqQueue(Service::Interface* self) {
307 307
308 DEBUG_LOG(GSP, "called");
309
308 // Iterate through each thread's command queue... 310 // Iterate through each thread's command queue...
309 for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { 311 for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) {
310 CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); 312 CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id);
@@ -320,6 +322,9 @@ static void TriggerCmdReqQueue(Service::Interface* self) {
320 command_buffer->number_commands = command_buffer->number_commands - 1; 322 command_buffer->number_commands = command_buffer->number_commands - 1;
321 } 323 }
322 } 324 }
325
326 u32* cmd_buff = Service::GetCommandBuffer();
327 cmd_buff[1] = 0; // No error
323} 328}
324 329
325const Interface::FunctionInfo FunctionTable[] = { 330const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 20e7fb4d3..3a7d6c469 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -10,6 +10,7 @@
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"
@@ -79,21 +80,20 @@ public:
79 u32* cmd_buff = GetCommandBuffer(); 80 u32* cmd_buff = GetCommandBuffer();
80 auto itr = m_functions.find(cmd_buff[0]); 81 auto itr = m_functions.find(cmd_buff[0]);
81 82
82 if (itr == m_functions.end()) { 83 if (itr == m_functions.end() || itr->second.func == nullptr) {
83 ERROR_LOG(OSHLE, "unknown/unimplemented function: port=%s, command=0x%08X", 84 // Number of params == bits 0-5 + bits 6-11
84 GetPortName().c_str(), cmd_buff[0]); 85 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
85 86
86 // TODO(bunnei): Hack - ignore error 87 std::string error = "unknown/unimplemented function '%s': port=%s";
87 u32* cmd_buff = Service::GetCommandBuffer(); 88 for (int i = 1; i <= num_params; ++i) {
88 cmd_buff[1] = 0; 89 error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
89 return MakeResult<bool>(false); 90 }
90 } 91
91 if (itr->second.func == nullptr) { 92 std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
92 ERROR_LOG(OSHLE, "unimplemented function: port=%s, name=%s", 93
93 GetPortName().c_str(), itr->second.name.c_str()); 94 ERROR_LOG(OSHLE, error.c_str(), name.c_str(), GetPortName().c_str());
94 95
95 // TODO(bunnei): Hack - ignore error 96 // TODO(bunnei): Hack - ignore error
96 u32* cmd_buff = Service::GetCommandBuffer();
97 cmd_buff[1] = 0; 97 cmd_buff[1] = 0;
98 return MakeResult<bool>(false); 98 return MakeResult<bool>(false);
99 } 99 }
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index af5e1b39b..77557e582 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -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 }
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 ea001673a..73a4f1e53 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
@@ -40,11 +39,6 @@ template <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 41
43 // TODO(bunnei): What is the virtual address of NDMA?
44 // case VADDR_NDMA:
45 // NDMA::Read(var, addr);
46 // break;
47
48 case VADDR_GPU: 42 case VADDR_GPU:
49 GPU::Read(var, addr); 43 GPU::Read(var, addr);
50 break; 44 break;
@@ -58,11 +52,6 @@ template <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 54
61 // TODO(bunnei): What is the virtual address of NDMA?
62 // case VADDR_NDMA
63 // NDMA::Write(addr, data);
64 // break;
65
66 case VADDR_GPU: 55 case VADDR_GPU:
67 GPU::Write(addr, data); 56 GPU::Write(addr, data);
68 break; 57 break;
@@ -87,13 +76,11 @@ 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();
97 NOTICE_LOG(HW, "initialized OK"); 84 NOTICE_LOG(HW, "initialized OK");
98} 85}
99 86
diff --git a/src/core/hw/ndma.cpp b/src/core/hw/ndma.cpp
deleted file mode 100644
index 593e5de30..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, (u32)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..7ef146359
--- /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/kernel/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 DEBUG_LOG(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 DEBUG_LOG(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 DEBUG_LOG(LOADER, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000);
203 DEBUG_LOG(LOADER, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000);
204 DEBUG_LOG(LOADER, "DATA: %u pages\n", data_load_size / 0x1000);
205 DEBUG_LOG(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 INFO_LOG(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/loader.cpp b/src/core/loader/loader.cpp
index a268e021a..174397b05 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -5,6 +5,7 @@
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/kernel/archive.h"
@@ -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}
@@ -56,6 +59,10 @@ ResultStatus LoadFile(const std::string& filename) {
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();
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/mem_map.h b/src/core/mem_map.h
index c9529f84c..f17afb60d 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -26,12 +26,10 @@ enum : u32 {
26 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
27 FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address 27 FCRAM_VADDR = 0x08000000, ///< FCRAM virtual address
28 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
29 FCRAM_MASK = (FCRAM_SIZE - 1), ///< FCRAM mask
30 29
31 SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size 30 SHARED_MEMORY_SIZE = 0x04000000, ///< Shared memory size
32 SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory 31 SHARED_MEMORY_VADDR = 0x10000000, ///< Shared memory
33 SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE), 32 SHARED_MEMORY_VADDR_END = (SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE),
34 SHARED_MEMORY_MASK = (SHARED_MEMORY_SIZE - 1),
35 33
36 DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size 34 DSP_MEMORY_SIZE = 0x00080000, ///< DSP memory size
37 DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address 35 DSP_MEMORY_VADDR = 0x1FF00000, ///< DSP memory virtual address
@@ -39,37 +37,31 @@ enum : u32 {
39 CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size 37 CONFIG_MEMORY_SIZE = 0x00001000, ///< Configuration memory size
40 CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address 38 CONFIG_MEMORY_VADDR = 0x1FF80000, ///< Configuration memory virtual address
41 CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE), 39 CONFIG_MEMORY_VADDR_END = (CONFIG_MEMORY_VADDR + CONFIG_MEMORY_SIZE),
42 CONFIG_MEMORY_MASK = (CONFIG_MEMORY_SIZE - 1),
43 40
44 KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size 41 KERNEL_MEMORY_SIZE = 0x00001000, ///< Kernel memory size
45 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
46 KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE), 43 KERNEL_MEMORY_VADDR_END = (KERNEL_MEMORY_VADDR + KERNEL_MEMORY_SIZE),
47 KERNEL_MEMORY_MASK = (KERNEL_MEMORY_SIZE - 1),
48 44
49 EXEFS_CODE_SIZE = 0x03F00000, 45 EXEFS_CODE_SIZE = 0x03F00000,
50 EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here 46 EXEFS_CODE_VADDR = 0x00100000, ///< ExeFS:/.code is loaded here
51 EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), 47 EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE),
52 EXEFS_CODE_MASK = 0x03FFFFFF,
53 48
54 // Region of FCRAM used by system 49 // Region of FCRAM used by system
55 SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB 50 SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB
56 SYSTEM_MEMORY_VADDR = 0x04000000, 51 SYSTEM_MEMORY_VADDR = 0x04000000,
57 SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), 52 SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE),
58 SYSTEM_MEMORY_MASK = 0x03FFFFFF,
59 53
60 HEAP_SIZE = FCRAM_SIZE, ///< Application heap size 54 HEAP_SIZE = FCRAM_SIZE, ///< Application heap size
61 //HEAP_PADDR = HEAP_GSP_SIZE, 55 //HEAP_PADDR = HEAP_GSP_SIZE,
62 //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), 56 //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE),
63 HEAP_VADDR = 0x08000000, 57 HEAP_VADDR = 0x08000000,
64 HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE), 58 HEAP_VADDR_END = (HEAP_VADDR + HEAP_SIZE),
65 HEAP_MASK = (HEAP_SIZE - 1),
66 59
67 HEAP_GSP_SIZE = 0x02000000, ///< GSP heap size... TODO: Define correctly? 60 HEAP_GSP_SIZE = 0x02000000, ///< GSP heap size... TODO: Define correctly?
68 HEAP_GSP_VADDR = 0x14000000, 61 HEAP_GSP_VADDR = 0x14000000,
69 HEAP_GSP_VADDR_END = (HEAP_GSP_VADDR + HEAP_GSP_SIZE), 62 HEAP_GSP_VADDR_END = (HEAP_GSP_VADDR + HEAP_GSP_SIZE),
70 HEAP_GSP_PADDR = 0x00000000, 63 HEAP_GSP_PADDR = 0x00000000,
71 HEAP_GSP_PADDR_END = (HEAP_GSP_PADDR + HEAP_GSP_SIZE), 64 HEAP_GSP_PADDR_END = (HEAP_GSP_PADDR + HEAP_GSP_SIZE),
72 HEAP_GSP_MASK = (HEAP_GSP_SIZE - 1),
73 65
74 HARDWARE_IO_SIZE = 0x01000000, 66 HARDWARE_IO_SIZE = 0x01000000,
75 HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start 67 HARDWARE_IO_PADDR = 0x10000000, ///< IO physical address start
@@ -82,12 +74,10 @@ enum : u32 {
82 VRAM_VADDR = 0x1F000000, 74 VRAM_VADDR = 0x1F000000,
83 VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE), 75 VRAM_PADDR_END = (VRAM_PADDR + VRAM_SIZE),
84 VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE), 76 VRAM_VADDR_END = (VRAM_VADDR + VRAM_SIZE),
85 VRAM_MASK = 0x007FFFFF,
86 77
87 SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader 78 SCRATCHPAD_SIZE = 0x00004000, ///< Typical stack size - TODO: Read from exheader
88 SCRATCHPAD_VADDR_END = 0x10000000, 79 SCRATCHPAD_VADDR_END = 0x10000000,
89 SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space 80 SCRATCHPAD_VADDR = (SCRATCHPAD_VADDR_END - SCRATCHPAD_SIZE), ///< Stack space
90 SCRATCHPAD_MASK = (SCRATCHPAD_SIZE - 1), ///< Scratchpad memory mask
91}; 81};
92 82
93//////////////////////////////////////////////////////////////////////////////////////////////////// 83////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp
index e8747840c..1887bcedb 100644
--- a/src/core/mem_map_funcs.cpp
+++ b/src/core/mem_map_funcs.cpp
@@ -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 - GSP heap
71 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { 71 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
72 var = *((const T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK]); 72 var = *((const T*)&g_heap_gsp[vaddr - HEAP_GSP_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,7 +89,7 @@ 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%lu @ 0x%08X", sizeof(var) * 8, vaddr); 95 ERROR_LOG(MEMMAP, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, vaddr);
@@ -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 - GSP heap
116 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { 116 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
117 *(T*)&g_heap_gsp[vaddr & HEAP_GSP_MASK] = data; 117 *(T*)&g_heap_gsp[vaddr - HEAP_GSP_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");
@@ -148,31 +148,31 @@ inline void Write(const VAddr vaddr, const T data) {
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 - GSP heap
158 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) { 158 } else if ((vaddr >= HEAP_GSP_VADDR) && (vaddr < HEAP_GSP_VADDR_END)) {
159 return g_heap_gsp + (vaddr & HEAP_GSP_MASK); 159 return g_heap_gsp + (vaddr - HEAP_GSP_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 ERROR_LOG(MEMMAP, "unknown GetPointer @ 0x%08x", vaddr);
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 8a6ba2560..431139cc2 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
@@ -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
@@ -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 8a5f11424..71b03f31c 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) {
@@ -312,6 +356,42 @@ 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_(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.
@@ -377,27 +457,15 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
377 buf = new u8[row_stride * texture_config.height]; 457 buf = new u8[row_stride * texture_config.height];
378 for (unsigned y = 0; y < texture_config.height; ++y) { 458 for (unsigned y = 0; y < texture_config.height; ++y) {
379 for (unsigned 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
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index b1558cfae..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
@@ -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/pica.h b/src/video_core/pica.h
index 5fe15a218..8bac178ca 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,7 +115,7 @@ 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_GSP_VADDR;
114 } 120 }
115 121
@@ -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;
@@ -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/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index a0eb0418c..fdac9ae1a 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -22,7 +22,7 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
22 // Compile Vertex Shader 22 // Compile Vertex Shader
23 DEBUG_LOG(GPU, "Compiling vertex shader."); 23 DEBUG_LOG(GPU, "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,14 @@ 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 DEBUG_LOG(GPU, "%s", &vertex_shader_error[0]);
36 } 36 }
37 37
38 // Compile Fragment Shader 38 // Compile Fragment Shader
39 DEBUG_LOG(GPU, "Compiling fragment shader."); 39 DEBUG_LOG(GPU, "Compiling fragment shader.");
40 40
41 glShaderSource(fragment_shader_id, 1, &fragment_shader, NULL); 41 glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
42 glCompileShader(fragment_shader_id); 42 glCompileShader(fragment_shader_id);
43 43
44 // Check Fragment Shader 44 // Check Fragment Shader
@@ -47,7 +47,7 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
47 47
48 if (info_log_length > 1) { 48 if (info_log_length > 1) {
49 std::vector<char> fragment_shader_error(info_log_length); 49 std::vector<char> fragment_shader_error(info_log_length);
50 glGetShaderInfoLog(fragment_shader_id, info_log_length, NULL, &fragment_shader_error[0]); 50 glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
51 DEBUG_LOG(GPU, "%s", &fragment_shader_error[0]); 51 DEBUG_LOG(GPU, "%s", &fragment_shader_error[0]);
52 } 52 }
53 53
@@ -65,7 +65,7 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
65 65
66 if (info_log_length > 1) { 66 if (info_log_length > 1) {
67 std::vector<char> program_error(info_log_length); 67 std::vector<char> program_error(info_log_length);
68 glGetProgramInfoLog(program_id, info_log_length, NULL, &program_error[0]); 68 glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]);
69 DEBUG_LOG(GPU, "%s", &program_error[0]); 69 DEBUG_LOG(GPU, "%s", &program_error[0]);
70 } 70 }
71 71
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index c779771c5..b581ff4da 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