diff options
67 files changed, 2280 insertions, 379 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 82b66be75..c8c8e3884 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
| @@ -27,6 +27,7 @@ Follow the indentation/whitespace style shown below. Do not use tabs, use 4-spac | |||
| 27 | ### Comments | 27 | ### Comments |
| 28 | * For regular comments, use C++ style (`//`) comments, even for multi-line ones. | 28 | * For regular comments, use C++ style (`//`) comments, even for multi-line ones. |
| 29 | * For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`. | 29 | * For doc-comments (Doxygen comments), use `/// ` if it's a single line, else use the `/**` `*/` style featured in the example. Start the text on the second line, not the first containing `/**`. |
| 30 | * For items that are both defined and declared in two separate files, put the doc-comment only next to the associated declaration. (In a header file, usually.) Otherwise, put it next to the implementation. Never duplicate doc-comments in both places. | ||
| 30 | 31 | ||
| 31 | ```cpp | 32 | ```cpp |
| 32 | namespace Example { | 33 | namespace Example { |
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 |
| 125 | void EmuWindow_GLFW::DoneCurrent() { | 125 | void EmuWindow_GLFW::DoneCurrent() { |
| 126 | glfwMakeContextCurrent(NULL); | 126 | glfwMakeContextCurrent(nullptr); |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | void EmuWindow_GLFW::ReloadSetKeymaps() { | 129 | void 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 | |||
| 14 | BreakPointModel::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 | |||
| 22 | int BreakPointModel::columnCount(const QModelIndex& parent) const | ||
| 23 | { | ||
| 24 | return 2; | ||
| 25 | } | ||
| 26 | |||
| 27 | int BreakPointModel::rowCount(const QModelIndex& parent) const | ||
| 28 | { | ||
| 29 | return static_cast<int>(Pica::DebugContext::Event::NumEvents); | ||
| 30 | } | ||
| 31 | |||
| 32 | QVariant 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 | |||
| 83 | QVariant 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 | |||
| 101 | bool 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 | |||
| 123 | void 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 | |||
| 135 | void 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 | |||
| 148 | GraphicsBreakPointsWidget::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 | |||
| 203 | void GraphicsBreakPointsWidget::OnPicaBreakPointHit(Event event, void* data) | ||
| 204 | { | ||
| 205 | // Process in GUI thread | ||
| 206 | emit BreakPointHit(event, data); | ||
| 207 | } | ||
| 208 | |||
| 209 | void 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 | |||
| 215 | void GraphicsBreakPointsWidget::OnPicaResume() | ||
| 216 | { | ||
| 217 | // Process in GUI thread | ||
| 218 | emit Resumed(); | ||
| 219 | } | ||
| 220 | |||
| 221 | void GraphicsBreakPointsWidget::OnResumed() | ||
| 222 | { | ||
| 223 | status_text->setText(tr("Emulation running")); | ||
| 224 | resume_button->setEnabled(false); | ||
| 225 | } | ||
| 226 | |||
| 227 | void GraphicsBreakPointsWidget::OnResumeRequested() | ||
| 228 | { | ||
| 229 | if (auto context = context_weak.lock()) | ||
| 230 | context->Resume(); | ||
| 231 | } | ||
| 232 | |||
| 233 | void 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 | |||
| 244 | void 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 | |||
| 254 | void 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 | |||
| 14 | class QLabel; | ||
| 15 | class QPushButton; | ||
| 16 | class QTreeView; | ||
| 17 | |||
| 18 | class BreakPointModel; | ||
| 19 | |||
| 20 | class GraphicsBreakPointsWidget : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||
| 21 | Q_OBJECT | ||
| 22 | |||
| 23 | using Event = Pica::DebugContext::Event; | ||
| 24 | |||
| 25 | public: | ||
| 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 | |||
| 32 | public 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 | |||
| 39 | signals: | ||
| 40 | void Resumed(); | ||
| 41 | void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 42 | void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); | ||
| 43 | |||
| 44 | private: | ||
| 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 | |||
| 13 | class BreakPointModel : public QAbstractListModel { | ||
| 14 | Q_OBJECT | ||
| 15 | |||
| 16 | public: | ||
| 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 | |||
| 30 | public slots: | ||
| 31 | void OnBreakPointHit(Pica::DebugContext::Event event); | ||
| 32 | void OnResumed(); | ||
| 33 | |||
| 34 | private: | ||
| 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 | ||
| 12 | GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) | 21 | #include "util/spinbox.hxx" |
| 13 | { | 22 | |
| 23 | QImage 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 | |||
| 35 | class TextureInfoWidget : public QWidget { | ||
| 36 | public: | ||
| 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 | |||
| 49 | TextureInfoDockWidget::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 | |||
| 127 | void TextureInfoDockWidget::OnAddressChanged(qint64 value) { | ||
| 128 | info.address = value; | ||
| 129 | emit UpdatePixmap(ReloadPixmap()); | ||
| 130 | } | ||
| 131 | |||
| 132 | void TextureInfoDockWidget::OnFormatChanged(int value) { | ||
| 133 | info.format = static_cast<Pica::Regs::TextureFormat>(value); | ||
| 134 | emit UpdatePixmap(ReloadPixmap()); | ||
| 135 | } | ||
| 136 | |||
| 137 | void TextureInfoDockWidget::OnWidthChanged(int value) { | ||
| 138 | info.width = value; | ||
| 139 | emit UpdatePixmap(ReloadPixmap()); | ||
| 140 | } | ||
| 141 | |||
| 142 | void TextureInfoDockWidget::OnHeightChanged(int value) { | ||
| 143 | info.height = value; | ||
| 144 | emit UpdatePixmap(ReloadPixmap()); | ||
| 145 | } | ||
| 14 | 146 | ||
| 147 | void TextureInfoDockWidget::OnStrideChanged(int value) { | ||
| 148 | info.stride = value; | ||
| 149 | emit UpdatePixmap(ReloadPixmap()); | ||
| 15 | } | 150 | } |
| 16 | 151 | ||
| 17 | int GPUCommandListModel::rowCount(const QModelIndex& parent) const | 152 | QPixmap TextureInfoDockWidget::ReloadPixmap() const { |
| 18 | { | 153 | u8* src = Memory::GetPointer(info.address); |
| 154 | return QPixmap::fromImage(LoadTexture(src, info)); | ||
| 155 | } | ||
| 156 | |||
| 157 | GPUCommandListModel::GPUCommandListModel(QObject* parent) : QAbstractListModel(parent) { | ||
| 158 | |||
| 159 | } | ||
| 160 | |||
| 161 | int GPUCommandListModel::rowCount(const QModelIndex& parent) const { | ||
| 19 | return pica_trace.writes.size(); | 162 | return pica_trace.writes.size(); |
| 20 | } | 163 | } |
| 21 | 164 | ||
| 22 | int GPUCommandListModel::columnCount(const QModelIndex& parent) const | 165 | int GPUCommandListModel::columnCount(const QModelIndex& parent) const { |
| 23 | { | ||
| 24 | return 2; | 166 | return 2; |
| 25 | } | 167 | } |
| 26 | 168 | ||
| 27 | QVariant GPUCommandListModel::data(const QModelIndex& index, int role) const | 169 | QVariant 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 | |||
| 195 | QVariant 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 | ||
| 52 | void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace) | 212 | void 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 | |||
| 224 | void 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 | |||
| 236 | void 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 | ||
| 62 | GPUCommandListWidget::GPUCommandListWidget(QWidget* parent) : QDockWidget(tr("Pica Command List"), parent) | 256 | GPUCommandListWidget::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 | ||
| 87 | void GPUCommandListWidget::OnToggleTracing() | 289 | void 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 | ||
| 13 | class QPushButton; | ||
| 14 | class QTreeView; | ||
| 15 | |||
| 13 | class GPUCommandListModel : public QAbstractListModel | 16 | class GPUCommandListModel : public QAbstractListModel |
| 14 | { | 17 | { |
| 15 | Q_OBJECT | 18 | Q_OBJECT |
| 16 | 19 | ||
| 17 | public: | 20 | public: |
| 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 | ||
| 24 | public slots: | 32 | public slots: |
| 25 | void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); | 33 | void OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& trace); |
| @@ -37,10 +45,39 @@ public: | |||
| 37 | 45 | ||
| 38 | public slots: | 46 | public slots: |
| 39 | void OnToggleTracing(); | 47 | void OnToggleTracing(); |
| 48 | void OnCommandDoubleClicked(const QModelIndex&); | ||
| 49 | |||
| 50 | void SetCommandInfo(const QModelIndex&); | ||
| 40 | 51 | ||
| 41 | signals: | 52 | signals: |
| 42 | void TracingFinished(const Pica::DebugUtils::PicaTrace&); | 53 | void TracingFinished(const Pica::DebugUtils::PicaTrace&); |
| 43 | 54 | ||
| 44 | private: | 55 | private: |
| 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 | |||
| 63 | class TextureInfoDockWidget : public QDockWidget { | ||
| 64 | Q_OBJECT | ||
| 65 | |||
| 66 | public: | ||
| 67 | TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent = nullptr); | ||
| 68 | |||
| 69 | signals: | ||
| 70 | void UpdatePixmap(const QPixmap& pixmap); | ||
| 71 | |||
| 72 | private 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 | |||
| 79 | private: | ||
| 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 | |||
| 19 | BreakPointObserverDock::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 | |||
| 34 | void BreakPointObserverDock::OnPicaBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 35 | { | ||
| 36 | emit BreakPointHit(event, data); | ||
| 37 | } | ||
| 38 | |||
| 39 | void BreakPointObserverDock::OnPicaResume() | ||
| 40 | { | ||
| 41 | emit Resumed(); | ||
| 42 | } | ||
| 43 | |||
| 44 | |||
| 45 | GraphicsFramebufferWidget::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 | |||
| 132 | void GraphicsFramebufferWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) | ||
| 133 | { | ||
| 134 | emit Update(); | ||
| 135 | widget()->setEnabled(true); | ||
| 136 | } | ||
| 137 | |||
| 138 | void GraphicsFramebufferWidget::OnResumed() | ||
| 139 | { | ||
| 140 | widget()->setEnabled(false); | ||
| 141 | } | ||
| 142 | |||
| 143 | void GraphicsFramebufferWidget::OnFramebufferSourceChanged(int new_value) | ||
| 144 | { | ||
| 145 | framebuffer_source = static_cast<Source>(new_value); | ||
| 146 | emit Update(); | ||
| 147 | } | ||
| 148 | |||
| 149 | void 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 | |||
| 159 | void 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 | |||
| 169 | void 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 | |||
| 179 | void 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 | |||
| 189 | void 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 | |||
| 11 | class QComboBox; | ||
| 12 | class QLabel; | ||
| 13 | class QSpinBox; | ||
| 14 | |||
| 15 | class 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. | ||
| 20 | class BreakPointObserverDock : public QDockWidget, Pica::DebugContext::BreakPointObserver { | ||
| 21 | Q_OBJECT | ||
| 22 | |||
| 23 | public: | ||
| 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 | |||
| 30 | private slots: | ||
| 31 | virtual void OnBreakPointHit(Pica::DebugContext::Event event, void* data) = 0; | ||
| 32 | virtual void OnResumed() = 0; | ||
| 33 | |||
| 34 | signals: | ||
| 35 | void Resumed(); | ||
| 36 | void BreakPointHit(Pica::DebugContext::Event event, void* data); | ||
| 37 | }; | ||
| 38 | |||
| 39 | class 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 | |||
| 59 | public: | ||
| 60 | GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context, QWidget* parent = nullptr); | ||
| 61 | |||
| 62 | public 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 | |||
| 70 | private slots: | ||
| 71 | void OnBreakPointHit(Pica::DebugContext::Event event, void* data) override; | ||
| 72 | void OnResumed() override; | ||
| 73 | |||
| 74 | signals: | ||
| 75 | void Update(); | ||
| 76 | |||
| 77 | private: | ||
| 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 | ||
| 6 | struct Hotkey | 6 | struct 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() | |||
| 131 | GMainWindow::~GMainWindow() | 145 | GMainWindow::~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 | ||
| 138 | void GMainWindow::BootGame(std::string filename) | 154 | void GMainWindow::BootGame(std::string filename) |
| @@ -164,7 +180,7 @@ void GMainWindow::BootGame(std::string filename) | |||
| 164 | 180 | ||
| 165 | void GMainWindow::OnMenuLoadFile() | 181 | void 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() | |||
| 213 | void GMainWindow::ToggleWindowMode() | 229 | void 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 | |||
| 39 | CSpinBox::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 | |||
| 49 | void 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 | |||
| 60 | void CSpinBox::SetRange(qint64 min, qint64 max) | ||
| 61 | { | ||
| 62 | min_value = min; | ||
| 63 | max_value = max; | ||
| 64 | |||
| 65 | SetValue(value); | ||
| 66 | UpdateText(); | ||
| 67 | } | ||
| 68 | |||
| 69 | void 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 | |||
| 96 | QAbstractSpinBox::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 | |||
| 109 | void CSpinBox::SetBase(int base) | ||
| 110 | { | ||
| 111 | this->base = base; | ||
| 112 | |||
| 113 | UpdateText(); | ||
| 114 | } | ||
| 115 | |||
| 116 | void CSpinBox::SetNumDigits(int num_digits) | ||
| 117 | { | ||
| 118 | this->num_digits = num_digits; | ||
| 119 | |||
| 120 | UpdateText(); | ||
| 121 | } | ||
| 122 | |||
| 123 | void CSpinBox::SetPrefix(const QString& prefix) | ||
| 124 | { | ||
| 125 | this->prefix = prefix; | ||
| 126 | |||
| 127 | UpdateText(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void CSpinBox::SetSuffix(const QString& suffix) | ||
| 131 | { | ||
| 132 | this->suffix = suffix; | ||
| 133 | |||
| 134 | UpdateText(); | ||
| 135 | } | ||
| 136 | |||
| 137 | static 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 | |||
| 164 | void 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 | |||
| 206 | QString 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 | |||
| 214 | qint64 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 | |||
| 222 | bool CSpinBox::HasSign() const | ||
| 223 | { | ||
| 224 | return base == 10 && min_value < 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | void 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 | |||
| 236 | QValidator::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 | |||
| 38 | class QVariant; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * A custom spin box widget with enhanced functionality over Qt's QSpinBox | ||
| 42 | */ | ||
| 43 | class CSpinBox : public QAbstractSpinBox { | ||
| 44 | Q_OBJECT | ||
| 45 | |||
| 46 | public: | ||
| 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 | |||
| 65 | signals: | ||
| 66 | void ValueChanged(qint64 val); | ||
| 67 | |||
| 68 | private slots: | ||
| 69 | void OnEditingFinished(); | ||
| 70 | |||
| 71 | private: | ||
| 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 @@ | |||
| 16 | ConsoleListener::ConsoleListener() | 16 | ConsoleListener::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() | |||
| 66 | void ConsoleListener::Close() | 66 | void 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 | ||
| 78 | bool ConsoleListener::IsOpen() | 78 | bool 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 | ||
| 828 | IOFile::IOFile() | 828 | IOFile::IOFile() |
| 829 | : m_file(NULL), m_good(true) | 829 | : m_file(nullptr), m_good(true) |
| 830 | {} | 830 | {} |
| 831 | 831 | ||
| 832 | IOFile::IOFile(std::FILE* file) | 832 | IOFile::IOFile(std::FILE* file) |
| @@ -834,7 +834,7 @@ IOFile::IOFile(std::FILE* file) | |||
| 834 | {} | 834 | {} |
| 835 | 835 | ||
| 836 | IOFile::IOFile(const std::string& filename, const char openmode[]) | 836 | IOFile::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 | ||
| 847 | IOFile::IOFile(IOFile&& other) | 847 | IOFile::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 | ||
| 887 | std::FILE* IOFile::ReleaseHandle() | 887 | std::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 | ||
| 24 | LogManager *LogManager::m_logManager = NULL; | 24 | LogManager *LogManager::m_logManager = nullptr; |
| 25 | 25 | ||
| 26 | LogManager::LogManager() | 26 | LogManager::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() | |||
| 141 | void LogManager::Shutdown() | 140 | void LogManager::Shutdown() |
| 142 | { | 141 | { |
| 143 | delete m_logManager; | 142 | delete m_logManager; |
| 144 | m_logManager = NULL; | 143 | m_logManager = nullptr; |
| 145 | } | 144 | } |
| 146 | 145 | ||
| 147 | LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable) | 146 | LogContainer::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 |
| 33 | void* globalbase = NULL; | 33 | void* 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 @@ | |||
| 77 | inline struct tm* localtime_r(const time_t *clock, struct tm *result) { | 77 | inline 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 |
| 32 | bool AsciiToHex(const char* _szValue, u32& result) | 32 | bool 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 | |||
| 92 | std::string StringFromFormat(const char* format, ...) | 92 | std::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 | ||
| 163 | bool TryParse(const std::string &str, u32 *const output) | 163 | bool 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 | ||
| 583 | std::string UTF16ToUTF8(const std::u16string& input) | 583 | std::string UTF16ToUTF8(const std::u16string& input) |
| 584 | { | 584 | { |
| 585 | return CodeToUTF8("UTF-16", input); | 585 | return CodeToUTF8("UTF-16LE", input); |
| 586 | } | 586 | } |
| 587 | 587 | ||
| 588 | std::string CP1252ToUTF8(const std::string& input) | 588 | std::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 | ||
| 359 | const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) | 359 | const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) |
| @@ -378,7 +378,7 @@ const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) | |||
| 378 | lasti = i; | 378 | lasti = i; |
| 379 | (*charn)++; | 379 | (*charn)++; |
| 380 | } | 380 | } |
| 381 | return NULL; | 381 | return nullptr; |
| 382 | } | 382 | } |
| 383 | 383 | ||
| 384 | int u8_is_locale_utf8(const char *locale) | 384 | int u8_is_locale_utf8(const char *locale) |
| @@ -419,35 +419,35 @@ bool UTF8StringHasNonASCII(const char *utf8string) { | |||
| 419 | 419 | ||
| 420 | std::string ConvertWStringToUTF8(const wchar_t *wstr) { | 420 | std::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 | ||
| 431 | std::string ConvertWStringToUTF8(const std::wstring &wstr) { | 431 | std::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 | ||
| 442 | void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) { | 442 | void 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 | ||
| 448 | std::wstring ConvertUTF8ToWString(const std::string &source) { | 448 | std::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; | |||
| 67 | static std::recursive_mutex externalEventSection; | 67 | static std::recursive_mutex externalEventSection; |
| 68 | 68 | ||
| 69 | // Warning: not included in save state. | 69 | // Warning: not included in save state. |
| 70 | void(*advanceCallback)(int cyclesExecuted) = NULL; | 70 | void(*advanceCallback)(int cyclesExecuted) = nullptr; |
| 71 | 71 | ||
| 72 | void SetClockFrequencyMHz(int cpuMhz) | 72 | void SetClockFrequencyMHz(int cpuMhz) |
| 73 | { | 73 | { |
| @@ -231,7 +231,7 @@ void ClearPendingEvents() | |||
| 231 | 231 | ||
| 232 | void AddEventToQueue(Event* ne) | 232 | void 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: | |||
| 100 | std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const { | 100 | std::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() { | |||
| 17 | Directory_RomFS::~Directory_RomFS() { | 17 | Directory_RomFS::~Directory_RomFS() { |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | bool 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 | ||
| 27 | Directory_SDMC::~Directory_SDMC() { | 26 | Directory_SDMC::~Directory_SDMC() { |
| 28 | Close(); | 27 | Close(); |
| 29 | } | 28 | } |
| 30 | 29 | ||
| 30 | bool 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 | ||
| 39 | private: | 45 | private: |
| 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 |
| 150 | inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { | 152 | static 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) | ||
| 158 | static 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 |
| 156 | ResultCode StopThread(Handle handle, const char* reason) { | 164 | ResultCode 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 | */ | ||
| 289 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | 292 | void 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 | ||
| 299 | void 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" |
| 297 | void ResumeThreadFromWait(Handle handle) { | 305 | void 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 | */ |
| 86 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); | 89 | void 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 | */ | ||
| 97 | void 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 |
| 89 | void WaitThread_Synchronization(); | 100 | void 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 | ||
| 12 | namespace CFG_U { | 12 | namespace CFG_U { |
| 13 | 13 | ||
| 14 | static 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 | 17 | static 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) { | |||
| 77 | static void GetCountryCodeID(Service::Interface* self) { | 76 | static 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 | */ |
| 176 | void SignalInterrupt(InterruptId interrupt_id) { | 178 | void 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. |
| 306 | static void TriggerCmdReqQueue(Service::Interface* self) { | 306 | static 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 | ||
| 325 | const Interface::FunctionInfo FunctionTable[] = { | 330 | const 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 | ||
| 11 | namespace HW { | 10 | namespace HW { |
| 12 | 11 | ||
| @@ -40,11 +39,6 @@ template <typename T> | |||
| 40 | inline void Read(T &var, const u32 addr) { | 39 | inline 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> | |||
| 58 | inline void Write(u32 addr, const T data) { | 52 | inline 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 |
| 88 | void Update() { | 77 | void Update() { |
| 89 | GPU::Update(); | 78 | GPU::Update(); |
| 90 | NDMA::Update(); | ||
| 91 | } | 79 | } |
| 92 | 80 | ||
| 93 | /// Initialize hardware | 81 | /// Initialize hardware |
| 94 | void Init() { | 82 | void 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 | |||
| 9 | namespace NDMA { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | inline void Read(T &var, const u32 addr) { | ||
| 13 | ERROR_LOG(NDMA, "unknown Read%lu @ 0x%08X", sizeof(var) * 8, addr); | ||
| 14 | } | ||
| 15 | |||
| 16 | template <typename T> | ||
| 17 | inline 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 | |||
| 23 | template void Read<u64>(u64 &var, const u32 addr); | ||
| 24 | template void Read<u32>(u32 &var, const u32 addr); | ||
| 25 | template void Read<u16>(u16 &var, const u32 addr); | ||
| 26 | template void Read<u8>(u8 &var, const u32 addr); | ||
| 27 | |||
| 28 | template void Write<u64>(u32 addr, const u64 data); | ||
| 29 | template void Write<u32>(u32 addr, const u32 data); | ||
| 30 | template void Write<u16>(u32 addr, const u16 data); | ||
| 31 | template void Write<u8>(u32 addr, const u8 data); | ||
| 32 | |||
| 33 | /// Update hardware | ||
| 34 | void Update() { | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Initialize hardware | ||
| 38 | void Init() { | ||
| 39 | NOTICE_LOG(GPU, "initialized OK"); | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Shutdown hardware | ||
| 43 | void 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 | |||
| 9 | namespace NDMA { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | inline void Read(T &var, const u32 addr); | ||
| 13 | |||
| 14 | template <typename T> | ||
| 15 | inline void Write(u32 addr, const T data); | ||
| 16 | |||
| 17 | /// Update hardware | ||
| 18 | void Update(); | ||
| 19 | |||
| 20 | /// Initialize hardware | ||
| 21 | void Init(); | ||
| 22 | |||
| 23 | /// Shutdown hardware | ||
| 24 | void 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 | |||
| 17 | namespace 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 | */ | ||
| 40 | enum THREEDSX_Error { | ||
| 41 | ERROR_NONE = 0, | ||
| 42 | ERROR_READ = 1, | ||
| 43 | ERROR_FILE = 2, | ||
| 44 | ERROR_ALLOC = 3 | ||
| 45 | }; | ||
| 46 | static const u32 RELOCBUFSIZE = 512; | ||
| 47 | |||
| 48 | // File header | ||
| 49 | static const u32 THREEDSX_MAGIC = 0x58534433; // '3DSX' | ||
| 50 | #pragma pack(1) | ||
| 51 | struct 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. | ||
| 64 | struct 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 | ||
| 78 | struct THREEDSX_Reloc | ||
| 79 | { | ||
| 80 | u16 skip, patch; | ||
| 81 | }; | ||
| 82 | #pragma pack() | ||
| 83 | |||
| 84 | struct THREEloadinfo | ||
| 85 | { | ||
| 86 | u8* seg_ptrs[3]; // code, rodata & data | ||
| 87 | u32 seg_addrs[3]; | ||
| 88 | u32 seg_sizes[3]; | ||
| 89 | }; | ||
| 90 | |||
| 91 | class THREEDSXReader { | ||
| 92 | public: | ||
| 93 | static int Load3DSXFile(const std::string& filename, u32 base_addr); | ||
| 94 | }; | ||
| 95 | |||
| 96 | static 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 | |||
| 105 | int 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 | |||
| 13 | namespace Loader { | ||
| 14 | |||
| 15 | /// Loads an 3DSX file | ||
| 16 | class AppLoader_THREEDSX final : public AppLoader { | ||
| 17 | public: | ||
| 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 | |||
| 27 | private: | ||
| 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) { | |||
| 148 | u8 *GetPointer(const VAddr vaddr) { | 148 | u8 *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 | ||
| 234 | static std::ptrdiff_t ExecuteCommandBlock(const u32* first_command_word) { | 253 | static 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 | ||
| 260 | void ProcessCommandList(const u32* list, u32 size) { | 279 | void 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 | ||
| 21 | namespace Pica { | 25 | namespace Pica { |
| 22 | 26 | ||
| 27 | void 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 | |||
| 49 | void 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 | |||
| 65 | std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global | ||
| 66 | |||
| 23 | namespace DebugUtils { | 67 | namespace DebugUtils { |
| 24 | 68 | ||
| 25 | void GeometryDumper::AddTriangle(Vertex& v0, Vertex& v1, Vertex& v2) { | 69 | void 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 | ||
| 359 | const 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 | |||
| 383 | TextureInfo 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 | |||
| 315 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | 395 | void 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 | ||
| 13 | namespace Pica { | 18 | namespace Pica { |
| 14 | 19 | ||
| 20 | class DebugContext { | ||
| 21 | public: | ||
| 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 | |||
| 130 | private: | ||
| 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 | |||
| 147 | extern std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global | ||
| 148 | |||
| 15 | namespace DebugUtils { | 149 | namespace 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(); | |||
| 57 | void OnPicaRegWrite(u32 id, u32 value); | 191 | void OnPicaRegWrite(u32 id, u32 value); |
| 58 | std::unique_ptr<PicaTrace> FinishPicaTracing(); | 192 | std::unique_ptr<PicaTrace> FinishPicaTracing(); |
| 59 | 193 | ||
| 194 | struct 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 | |||
| 205 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info); | ||
| 60 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | 206 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); |
| 61 | 207 | ||
| 62 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); | 208 | void 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 | ||
| 617 | ASSERT_REG_POSITION(trigger_irq, 0x10); | ||
| 596 | ASSERT_REG_POSITION(viewport_size_x, 0x41); | 618 | ASSERT_REG_POSITION(viewport_size_x, 0x41); |
| 597 | ASSERT_REG_POSITION(viewport_size_y, 0x43); | 619 | ASSERT_REG_POSITION(viewport_size_y, 0x43); |
| 598 | ASSERT_REG_POSITION(viewport_depth_range, 0x4d); | 620 | ASSERT_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 | ||
| 18 | namespace VideoCore { | 18 | namespace VideoCore { |
| 19 | 19 | ||
| 20 | EmuWindow* g_emu_window = NULL; ///< Frontend emulator window | 20 | EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window |
| 21 | RendererBase* g_renderer = NULL; ///< Renderer plugin | 21 | RendererBase* g_renderer = nullptr; ///< Renderer plugin |
| 22 | int g_current_frame = 0; | 22 | int g_current_frame = 0; |
| 23 | 23 | ||
| 24 | /// Initialize the video core | 24 | /// Initialize the video core |