diff options
60 files changed, 1778 insertions, 1196 deletions
diff --git a/.gitmodules b/.gitmodules index 54714e5cd..a9e0a5c1a 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -4,3 +4,6 @@ | |||
| 4 | [submodule "externals/boost"] | 4 | [submodule "externals/boost"] |
| 5 | path = externals/boost | 5 | path = externals/boost |
| 6 | url = https://github.com/citra-emu/ext-boost.git | 6 | url = https://github.com/citra-emu/ext-boost.git |
| 7 | [submodule "externals/nihstro"] | ||
| 8 | path = externals/nihstro | ||
| 9 | url = https://github.com/neobrain/nihstro.git | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index 63738b5ff..638b468a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -5,8 +5,7 @@ cmake_minimum_required(VERSION 2.8.11) | |||
| 5 | project(citra) | 5 | project(citra) |
| 6 | 6 | ||
| 7 | if (NOT MSVC) | 7 | if (NOT MSVC) |
| 8 | # -std=c++14 is only supported on very new compilers, so use the old c++1y alias instead. | 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes") |
| 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes") | ||
| 10 | else() | 9 | else() |
| 11 | # Silence deprecation warnings | 10 | # Silence deprecation warnings |
| 12 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) | 11 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) |
| @@ -142,6 +141,8 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih") | |||
| 142 | include_directories(${INI_PREFIX}) | 141 | include_directories(${INI_PREFIX}) |
| 143 | add_subdirectory(${INI_PREFIX}) | 142 | add_subdirectory(${INI_PREFIX}) |
| 144 | 143 | ||
| 144 | include_directories(externals/nihstro/include) | ||
| 145 | |||
| 145 | # process subdirectories | 146 | # process subdirectories |
| 146 | if(ENABLE_QT) | 147 | if(ENABLE_QT) |
| 147 | include_directories(externals/qhexedit) | 148 | include_directories(externals/qhexedit) |
diff --git a/externals/nihstro b/externals/nihstro new file mode 160000 | |||
| Subproject fc71f8684d26ccf277ad68809c8bd7273141fe8 | |||
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index 895851be3..a9ec2f7fe 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp | |||
| @@ -27,10 +27,10 @@ void CallstackWidget::OnCPUStepped() | |||
| 27 | ARM_Interface* app_core = Core::g_app_core; | 27 | ARM_Interface* app_core = Core::g_app_core; |
| 28 | 28 | ||
| 29 | u32 sp = app_core->GetReg(13); //stack pointer | 29 | u32 sp = app_core->GetReg(13); //stack pointer |
| 30 | u32 addr, ret_addr, call_addr, func_addr; | 30 | u32 ret_addr, call_addr, func_addr; |
| 31 | 31 | ||
| 32 | int counter = 0; | 32 | int counter = 0; |
| 33 | for (int addr = 0x10000000; addr >= sp; addr -= 4) | 33 | for (u32 addr = 0x10000000; addr >= sp; addr -= 4) |
| 34 | { | 34 | { |
| 35 | ret_addr = Memory::Read32(addr); | 35 | ret_addr = Memory::Read32(addr); |
| 36 | call_addr = ret_addr - 4; //get call address??? | 36 | call_addr = ret_addr - 4; //get call address??? |
diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 53394b6e6..4cb41db22 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp | |||
| @@ -39,15 +39,17 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const | |||
| 39 | switch (index.column()) { | 39 | switch (index.column()) { |
| 40 | case 0: | 40 | case 0: |
| 41 | { | 41 | { |
| 42 | std::map<Pica::DebugContext::Event, QString> map; | 42 | static const std::map<Pica::DebugContext::Event, QString> map = { |
| 43 | map.insert({Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded")}); | 43 | { Pica::DebugContext::Event::CommandLoaded, tr("Pica command loaded") }, |
| 44 | map.insert({Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed")}); | 44 | { Pica::DebugContext::Event::CommandProcessed, tr("Pica command processed") }, |
| 45 | map.insert({Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch")}); | 45 | { Pica::DebugContext::Event::IncomingPrimitiveBatch, tr("Incoming primitive batch") }, |
| 46 | map.insert({Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch")}); | 46 | { Pica::DebugContext::Event::FinishedPrimitiveBatch, tr("Finished primitive batch") }, |
| 47 | { Pica::DebugContext::Event::VertexLoaded, tr("Vertex Loaded") } | ||
| 48 | }; | ||
| 47 | 49 | ||
| 48 | _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); | 50 | _dbg_assert_(Debug_GPU, map.size() == static_cast<size_t>(Pica::DebugContext::Event::NumEvents)); |
| 49 | 51 | ||
| 50 | return map[event]; | 52 | return (map.find(event) != map.end()) ? map.at(event) : QString(); |
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | case 1: | 55 | case 1: |
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 7f97cf143..95187e54d 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp | |||
| @@ -24,7 +24,7 @@ QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) { | |||
| 24 | QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); | 24 | QImage decoded_image(info.width, info.height, QImage::Format_ARGB32); |
| 25 | for (int y = 0; y < info.height; ++y) { | 25 | for (int y = 0; y < info.height; ++y) { |
| 26 | for (int x = 0; x < info.width; ++x) { | 26 | for (int x = 0; x < info.width; ++x) { |
| 27 | Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info); | 27 | Math::Vec4<u8> color = Pica::DebugUtils::LookupTexture(src, x, y, info, true); |
| 28 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | 28 | decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); |
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| @@ -47,7 +47,7 @@ public: | |||
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent) | 49 | TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo& info, QWidget* parent) |
| 50 | : QDockWidget(tr("Texture 0x%1").arg(info.address, 8, 16, QLatin1Char('0'))), | 50 | : QDockWidget(tr("Texture 0x%1").arg(info.physical_address, 8, 16, QLatin1Char('0'))), |
| 51 | info(info) { | 51 | info(info) { |
| 52 | 52 | ||
| 53 | QWidget* main_widget = new QWidget; | 53 | QWidget* main_widget = new QWidget; |
| @@ -60,7 +60,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 60 | phys_address_spinbox->SetBase(16); | 60 | phys_address_spinbox->SetBase(16); |
| 61 | phys_address_spinbox->SetRange(0, 0xFFFFFFFF); | 61 | phys_address_spinbox->SetRange(0, 0xFFFFFFFF); |
| 62 | phys_address_spinbox->SetPrefix("0x"); | 62 | phys_address_spinbox->SetPrefix("0x"); |
| 63 | phys_address_spinbox->SetValue(info.address); | 63 | phys_address_spinbox->SetValue(info.physical_address); |
| 64 | connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64))); | 64 | connect(phys_address_spinbox, SIGNAL(ValueChanged(qint64)), this, SLOT(OnAddressChanged(qint64))); |
| 65 | 65 | ||
| 66 | QComboBox* format_choice = new QComboBox; | 66 | QComboBox* format_choice = new QComboBox; |
| @@ -69,6 +69,13 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 69 | format_choice->addItem(tr("RGBA5551")); | 69 | format_choice->addItem(tr("RGBA5551")); |
| 70 | format_choice->addItem(tr("RGB565")); | 70 | format_choice->addItem(tr("RGB565")); |
| 71 | format_choice->addItem(tr("RGBA4")); | 71 | format_choice->addItem(tr("RGBA4")); |
| 72 | format_choice->addItem(tr("IA8")); | ||
| 73 | format_choice->addItem(tr("UNK6")); | ||
| 74 | format_choice->addItem(tr("I8")); | ||
| 75 | format_choice->addItem(tr("A8")); | ||
| 76 | format_choice->addItem(tr("IA4")); | ||
| 77 | format_choice->addItem(tr("UNK10")); | ||
| 78 | format_choice->addItem(tr("A4")); | ||
| 72 | format_choice->setCurrentIndex(static_cast<int>(info.format)); | 79 | format_choice->setCurrentIndex(static_cast<int>(info.format)); |
| 73 | connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); | 80 | connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); |
| 74 | 81 | ||
| @@ -125,7 +132,7 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 125 | } | 132 | } |
| 126 | 133 | ||
| 127 | void TextureInfoDockWidget::OnAddressChanged(qint64 value) { | 134 | void TextureInfoDockWidget::OnAddressChanged(qint64 value) { |
| 128 | info.address = value; | 135 | info.physical_address = value; |
| 129 | emit UpdatePixmap(ReloadPixmap()); | 136 | emit UpdatePixmap(ReloadPixmap()); |
| 130 | } | 137 | } |
| 131 | 138 | ||
| @@ -150,7 +157,7 @@ void TextureInfoDockWidget::OnStrideChanged(int value) { | |||
| 150 | } | 157 | } |
| 151 | 158 | ||
| 152 | QPixmap TextureInfoDockWidget::ReloadPixmap() const { | 159 | QPixmap TextureInfoDockWidget::ReloadPixmap() const { |
| 153 | u8* src = Memory::GetPointer(info.address); | 160 | u8* src = Memory::GetPointer(Pica::PAddrToVAddr(info.physical_address)); |
| 154 | return QPixmap::fromImage(LoadTexture(src, info)); | 161 | return QPixmap::fromImage(LoadTexture(src, info)); |
| 155 | } | 162 | } |
| 156 | 163 | ||
| @@ -223,9 +230,21 @@ void GPUCommandListModel::OnPicaTraceFinished(const Pica::DebugUtils::PicaTrace& | |||
| 223 | 230 | ||
| 224 | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | 231 | void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { |
| 225 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | 232 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); |
| 226 | if (COMMAND_IN_RANGE(command_id, texture0)) { | 233 | if (COMMAND_IN_RANGE(command_id, texture0) || |
| 227 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | 234 | COMMAND_IN_RANGE(command_id, texture1) || |
| 228 | Pica::registers.texture0_format); | 235 | COMMAND_IN_RANGE(command_id, texture2)) { |
| 236 | |||
| 237 | unsigned index; | ||
| 238 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 239 | index = 0; | ||
| 240 | } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||
| 241 | index = 1; | ||
| 242 | } else { | ||
| 243 | index = 2; | ||
| 244 | } | ||
| 245 | auto config = Pica::registers.GetTextures()[index].config; | ||
| 246 | auto format = Pica::registers.GetTextures()[index].format; | ||
| 247 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||
| 229 | 248 | ||
| 230 | // TODO: Instead, emit a signal here to be caught by the main window widget. | 249 | // TODO: Instead, emit a signal here to be caught by the main window widget. |
| 231 | auto main_window = static_cast<QMainWindow*>(parent()); | 250 | auto main_window = static_cast<QMainWindow*>(parent()); |
| @@ -237,10 +256,23 @@ void GPUCommandListWidget::SetCommandInfo(const QModelIndex& index) { | |||
| 237 | QWidget* new_info_widget; | 256 | QWidget* new_info_widget; |
| 238 | 257 | ||
| 239 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); | 258 | const int command_id = list_widget->model()->data(index, GPUCommandListModel::CommandIdRole).toInt(); |
| 240 | if (COMMAND_IN_RANGE(command_id, texture0)) { | 259 | if (COMMAND_IN_RANGE(command_id, texture0) || |
| 241 | u8* src = Memory::GetPointer(Pica::registers.texture0.GetPhysicalAddress()); | 260 | COMMAND_IN_RANGE(command_id, texture1) || |
| 242 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(Pica::registers.texture0, | 261 | COMMAND_IN_RANGE(command_id, texture2)) { |
| 243 | Pica::registers.texture0_format); | 262 | |
| 263 | unsigned index; | ||
| 264 | if (COMMAND_IN_RANGE(command_id, texture0)) { | ||
| 265 | index = 0; | ||
| 266 | } else if (COMMAND_IN_RANGE(command_id, texture1)) { | ||
| 267 | index = 1; | ||
| 268 | } else { | ||
| 269 | index = 2; | ||
| 270 | } | ||
| 271 | auto config = Pica::registers.GetTextures()[index].config; | ||
| 272 | auto format = Pica::registers.GetTextures()[index].format; | ||
| 273 | |||
| 274 | auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||
| 275 | u8* src = Memory::GetPointer(Pica::PAddrToVAddr(config.GetPhysicalAddress())); | ||
| 244 | new_info_widget = new TextureInfoWidget(src, info); | 276 | new_info_widget = new TextureInfoWidget(src, info); |
| 245 | } else { | 277 | } else { |
| 246 | new_info_widget = new QWidget; | 278 | new_info_widget = new QWidget; |
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index ac47f298d..484be1db5 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp | |||
| @@ -125,7 +125,8 @@ GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::Debug | |||
| 125 | setWidget(main_widget); | 125 | setWidget(main_widget); |
| 126 | 126 | ||
| 127 | // Load current data - TODO: Make sure this works when emulation is not running | 127 | // Load current data - TODO: Make sure this works when emulation is not running |
| 128 | emit Update(); | 128 | if (debug_context && debug_context->at_breakpoint) |
| 129 | emit Update(); | ||
| 129 | widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint | 130 | widget()->setEnabled(false); // TODO: Only enable if currently at breakpoint |
| 130 | } | 131 | } |
| 131 | 132 | ||
| @@ -198,7 +199,7 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 198 | auto framebuffer = Pica::registers.framebuffer; | 199 | auto framebuffer = Pica::registers.framebuffer; |
| 199 | using Framebuffer = decltype(framebuffer); | 200 | using Framebuffer = decltype(framebuffer); |
| 200 | 201 | ||
| 201 | framebuffer_address = framebuffer.GetColorBufferAddress(); | 202 | framebuffer_address = framebuffer.GetColorBufferPhysicalAddress(); |
| 202 | framebuffer_width = framebuffer.GetWidth(); | 203 | framebuffer_width = framebuffer.GetWidth(); |
| 203 | framebuffer_height = framebuffer.GetHeight(); | 204 | framebuffer_height = framebuffer.GetHeight(); |
| 204 | framebuffer_format = static_cast<Format>(framebuffer.color_format); | 205 | framebuffer_format = static_cast<Format>(framebuffer.color_format); |
| @@ -223,9 +224,9 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 223 | case Format::RGBA8: | 224 | case Format::RGBA8: |
| 224 | { | 225 | { |
| 225 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | 226 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); |
| 226 | u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address); | 227 | u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); |
| 227 | for (int y = 0; y < framebuffer_height; ++y) { | 228 | for (unsigned y = 0; y < framebuffer_height; ++y) { |
| 228 | for (int x = 0; x < framebuffer_width; ++x) { | 229 | for (unsigned x = 0; x < framebuffer_width; ++x) { |
| 229 | u32 value = *(color_buffer + x + y * framebuffer_width); | 230 | u32 value = *(color_buffer + x + y * framebuffer_width); |
| 230 | 231 | ||
| 231 | decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/)); | 232 | decoded_image.setPixel(x, y, qRgba((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255/*value >> 24*/)); |
| @@ -238,9 +239,9 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 238 | case Format::RGB8: | 239 | case Format::RGB8: |
| 239 | { | 240 | { |
| 240 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | 241 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); |
| 241 | u8* color_buffer = Memory::GetPointer(framebuffer_address); | 242 | u8* color_buffer = Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); |
| 242 | for (int y = 0; y < framebuffer_height; ++y) { | 243 | for (unsigned y = 0; y < framebuffer_height; ++y) { |
| 243 | for (int x = 0; x < framebuffer_width; ++x) { | 244 | for (unsigned x = 0; x < framebuffer_width; ++x) { |
| 244 | u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width; | 245 | u8* pixel_pointer = color_buffer + x * 3 + y * 3 * framebuffer_width; |
| 245 | 246 | ||
| 246 | decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/)); | 247 | decoded_image.setPixel(x, y, qRgba(pixel_pointer[0], pixel_pointer[1], pixel_pointer[2], 255/*value >> 24*/)); |
| @@ -253,9 +254,9 @@ void GraphicsFramebufferWidget::OnUpdate() | |||
| 253 | case Format::RGBA5551: | 254 | case Format::RGBA5551: |
| 254 | { | 255 | { |
| 255 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); | 256 | QImage decoded_image(framebuffer_width, framebuffer_height, QImage::Format_ARGB32); |
| 256 | u32* color_buffer = (u32*)Memory::GetPointer(framebuffer_address); | 257 | u32* color_buffer = (u32*)Memory::GetPointer(Pica::PAddrToVAddr(framebuffer_address)); |
| 257 | for (int y = 0; y < framebuffer_height; ++y) { | 258 | for (unsigned y = 0; y < framebuffer_height; ++y) { |
| 258 | for (int x = 0; x < framebuffer_width; ++x) { | 259 | for (unsigned x = 0; x < framebuffer_width; ++x) { |
| 259 | u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2); | 260 | u16 value = *(u16*)(((u8*)color_buffer) + x * 2 + y * framebuffer_width * 2); |
| 260 | u8 r = (value >> 11) & 0x1F; | 261 | u8 r = (value >> 11) & 0x1F; |
| 261 | u8 g = (value >> 6) & 0x1F; | 262 | u8 g = (value >> 6) & 0x1F; |
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp index 9672168f5..24ea3a967 100644 --- a/src/citra_qt/util/spinbox.cpp +++ b/src/citra_qt/util/spinbox.cpp | |||
| @@ -238,7 +238,7 @@ QValidator::State CSpinBox::validate(QString& input, int& pos) const | |||
| 238 | if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) | 238 | if (!prefix.isEmpty() && input.left(prefix.length()) != prefix) |
| 239 | return QValidator::Invalid; | 239 | return QValidator::Invalid; |
| 240 | 240 | ||
| 241 | unsigned strpos = prefix.length(); | 241 | int strpos = prefix.length(); |
| 242 | 242 | ||
| 243 | // Empty "numbers" allowed as intermediate values | 243 | // Empty "numbers" allowed as intermediate values |
| 244 | if (strpos >= input.length() - HasSign() - suffix.length()) | 244 | if (strpos >= input.length() - HasSign() - suffix.length()) |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 15989708d..3c3419bbc 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -49,6 +49,7 @@ set(HEADERS | |||
| 49 | logging/filter.h | 49 | logging/filter.h |
| 50 | logging/log.h | 50 | logging/log.h |
| 51 | logging/backend.h | 51 | logging/backend.h |
| 52 | make_unique.h | ||
| 52 | math_util.h | 53 | math_util.h |
| 53 | mem_arena.h | 54 | mem_arena.h |
| 54 | memory_util.h | 55 | memory_util.h |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 9e02210f9..3ec061e63 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -142,7 +142,7 @@ public: | |||
| 142 | 142 | ||
| 143 | __forceinline BitField& operator=(T val) | 143 | __forceinline BitField& operator=(T val) |
| 144 | { | 144 | { |
| 145 | storage = (storage & ~GetMask()) | (((StorageType)val << position) & GetMask()); | 145 | Assign(val); |
| 146 | return *this; | 146 | return *this; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| @@ -151,6 +151,10 @@ public: | |||
| 151 | return Value(); | 151 | return Value(); |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | __forceinline void Assign(const T& value) { | ||
| 155 | storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask()); | ||
| 156 | } | ||
| 157 | |||
| 154 | __forceinline T Value() const | 158 | __forceinline T Value() const |
| 155 | { | 159 | { |
| 156 | if (std::numeric_limits<T>::is_signed) | 160 | if (std::numeric_limits<T>::is_signed) |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 42e1a29c1..966402a3d 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -40,7 +40,9 @@ | |||
| 40 | #define MAPS_DIR "maps" | 40 | #define MAPS_DIR "maps" |
| 41 | #define CACHE_DIR "cache" | 41 | #define CACHE_DIR "cache" |
| 42 | #define SDMC_DIR "sdmc" | 42 | #define SDMC_DIR "sdmc" |
| 43 | #define SAVEDATA_DIR "savedata" | ||
| 43 | #define SYSDATA_DIR "sysdata" | 44 | #define SYSDATA_DIR "sysdata" |
| 45 | #define SYSSAVEDATA_DIR "syssavedata" | ||
| 44 | #define SHADERCACHE_DIR "shader_cache" | 46 | #define SHADERCACHE_DIR "shader_cache" |
| 45 | #define STATESAVES_DIR "state_saves" | 47 | #define STATESAVES_DIR "state_saves" |
| 46 | #define SCREENSHOTS_DIR "screenShots" | 48 | #define SCREENSHOTS_DIR "screenShots" |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 88c46c117..20c680571 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -676,7 +676,9 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 676 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | 676 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; |
| 677 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 677 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 678 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 679 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||
| 679 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; | 680 | paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; |
| 681 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 680 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 682 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 681 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 683 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 682 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 684 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
| @@ -718,6 +720,8 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new | |||
| 718 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | 720 | paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; |
| 719 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 721 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 720 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; | 722 | paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; |
| 723 | paths[D_SAVEDATA_IDX] = paths[D_USER_IDX] + SAVEDATA_DIR DIR_SEP; | ||
| 724 | paths[D_SYSSAVEDATA_IDX] = paths[D_USER_IDX] + SYSSAVEDATA_DIR DIR_SEP; | ||
| 721 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | 725 | paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; |
| 722 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | 726 | paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; |
| 723 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | 727 | paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; |
diff --git a/src/common/file_util.h b/src/common/file_util.h index a9d48cfe8..b1a60fb81 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -27,7 +27,9 @@ enum { | |||
| 27 | D_STATESAVES_IDX, | 27 | D_STATESAVES_IDX, |
| 28 | D_SCREENSHOTS_IDX, | 28 | D_SCREENSHOTS_IDX, |
| 29 | D_SDMC_IDX, | 29 | D_SDMC_IDX, |
| 30 | D_SAVEDATA_IDX, | ||
| 30 | D_SYSDATA_IDX, | 31 | D_SYSDATA_IDX, |
| 32 | D_SYSSAVEDATA_IDX, | ||
| 31 | D_HIRESTEXTURES_IDX, | 33 | D_HIRESTEXTURES_IDX, |
| 32 | D_DUMP_IDX, | 34 | D_DUMP_IDX, |
| 33 | D_DUMPFRAMES_IDX, | 35 | D_DUMPFRAMES_IDX, |
diff --git a/src/common/make_unique.h b/src/common/make_unique.h new file mode 100644 index 000000000..2a7b76412 --- /dev/null +++ b/src/common/make_unique.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | template <typename T, typename... Args> | ||
| 12 | std::unique_ptr<T> make_unique(Args&&... args) { | ||
| 13 | return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); | ||
| 14 | } | ||
| 15 | |||
| 16 | } // namespace | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 198e4afd3..3381524e3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -18,11 +18,12 @@ set(SRCS | |||
| 18 | arm/skyeye_common/vfp/vfpinstr.cpp | 18 | arm/skyeye_common/vfp/vfpinstr.cpp |
| 19 | arm/skyeye_common/vfp/vfpsingle.cpp | 19 | arm/skyeye_common/vfp/vfpsingle.cpp |
| 20 | file_sys/archive_romfs.cpp | 20 | file_sys/archive_romfs.cpp |
| 21 | file_sys/archive_savedata.cpp | ||
| 21 | file_sys/archive_sdmc.cpp | 22 | file_sys/archive_sdmc.cpp |
| 23 | file_sys/archive_systemsavedata.cpp | ||
| 24 | file_sys/disk_archive.cpp | ||
| 22 | file_sys/file_romfs.cpp | 25 | file_sys/file_romfs.cpp |
| 23 | file_sys/file_sdmc.cpp | ||
| 24 | file_sys/directory_romfs.cpp | 26 | file_sys/directory_romfs.cpp |
| 25 | file_sys/directory_sdmc.cpp | ||
| 26 | hle/kernel/address_arbiter.cpp | 27 | hle/kernel/address_arbiter.cpp |
| 27 | hle/kernel/event.cpp | 28 | hle/kernel/event.cpp |
| 28 | hle/kernel/kernel.cpp | 29 | hle/kernel/kernel.cpp |
| @@ -99,13 +100,14 @@ set(HEADERS | |||
| 99 | arm/arm_interface.h | 100 | arm/arm_interface.h |
| 100 | file_sys/archive_backend.h | 101 | file_sys/archive_backend.h |
| 101 | file_sys/archive_romfs.h | 102 | file_sys/archive_romfs.h |
| 103 | file_sys/archive_savedata.h | ||
| 102 | file_sys/archive_sdmc.h | 104 | file_sys/archive_sdmc.h |
| 105 | file_sys/archive_systemsavedata.h | ||
| 106 | file_sys/disk_archive.h | ||
| 103 | file_sys/file_backend.h | 107 | file_sys/file_backend.h |
| 104 | file_sys/file_romfs.h | 108 | file_sys/file_romfs.h |
| 105 | file_sys/file_sdmc.h | ||
| 106 | file_sys/directory_backend.h | 109 | file_sys/directory_backend.h |
| 107 | file_sys/directory_romfs.h | 110 | file_sys/directory_romfs.h |
| 108 | file_sys/directory_sdmc.h | ||
| 109 | hle/kernel/address_arbiter.h | 111 | hle/kernel/address_arbiter.h |
| 110 | hle/kernel/event.h | 112 | hle/kernel/event.h |
| 111 | hle/kernel/kernel.h | 113 | hle/kernel/kernel.h |
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 68012bffd..84b4a38f0 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp | |||
| @@ -1266,6 +1266,13 @@ typedef struct _smla_inst { | |||
| 1266 | unsigned int Rn; | 1266 | unsigned int Rn; |
| 1267 | } smla_inst; | 1267 | } smla_inst; |
| 1268 | 1268 | ||
| 1269 | typedef struct umaal_inst { | ||
| 1270 | unsigned int Rn; | ||
| 1271 | unsigned int Rm; | ||
| 1272 | unsigned int RdHi; | ||
| 1273 | unsigned int RdLo; | ||
| 1274 | } umaal_inst; | ||
| 1275 | |||
| 1269 | typedef struct _umlal_inst { | 1276 | typedef struct _umlal_inst { |
| 1270 | unsigned int S; | 1277 | unsigned int S; |
| 1271 | unsigned int Rm; | 1278 | unsigned int Rm; |
| @@ -3010,7 +3017,26 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(uhaddsubx)(unsigned int inst, int index) { UN | |||
| 3010 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); } | 3017 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub16)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB16"); } |
| 3011 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); } | 3018 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsub8)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUB8"); } |
| 3012 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); } | 3019 | ARM_INST_PTR INTERPRETER_TRANSLATE(uhsubaddx)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UHSUBADDX"); } |
| 3013 | ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) { UNIMPLEMENTED_INSTRUCTION("UMAAL"); } | 3020 | ARM_INST_PTR INTERPRETER_TRANSLATE(umaal)(unsigned int inst, int index) |
| 3021 | { | ||
| 3022 | arm_inst* const inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(umaal_inst)); | ||
| 3023 | umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; | ||
| 3024 | |||
| 3025 | inst_base->cond = BITS(inst, 28, 31); | ||
| 3026 | inst_base->idx = index; | ||
| 3027 | inst_base->br = NON_BRANCH; | ||
| 3028 | inst_base->load_r15 = 0; | ||
| 3029 | |||
| 3030 | inst_cream->Rm = BITS(inst, 8, 11); | ||
| 3031 | inst_cream->Rn = BITS(inst, 0, 3); | ||
| 3032 | inst_cream->RdLo = BITS(inst, 12, 15); | ||
| 3033 | inst_cream->RdHi = BITS(inst, 16, 19); | ||
| 3034 | |||
| 3035 | if (CHECK_RM || CHECK_RN) | ||
| 3036 | inst_base->load_r15 = 1; | ||
| 3037 | |||
| 3038 | return inst_base; | ||
| 3039 | } | ||
| 3014 | ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) | 3040 | ARM_INST_PTR INTERPRETER_TRANSLATE(umlal)(unsigned int inst, int index) |
| 3015 | { | 3041 | { |
| 3016 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); | 3042 | arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(umlal_inst)); |
| @@ -6374,6 +6400,26 @@ unsigned InterpreterMainLoop(ARMul_State* state) | |||
| 6374 | UHSUB8_INST: | 6400 | UHSUB8_INST: |
| 6375 | UHSUBADDX_INST: | 6401 | UHSUBADDX_INST: |
| 6376 | UMAAL_INST: | 6402 | UMAAL_INST: |
| 6403 | { | ||
| 6404 | INC_ICOUNTER; | ||
| 6405 | if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) { | ||
| 6406 | umaal_inst* const inst_cream = (umaal_inst*)inst_base->component; | ||
| 6407 | |||
| 6408 | const u32 rm = RM; | ||
| 6409 | const u32 rn = RN; | ||
| 6410 | const u32 rd_lo = RDLO; | ||
| 6411 | const u32 rd_hi = RDHI; | ||
| 6412 | |||
| 6413 | const u64 result = (rm * rn) + rd_lo + rd_hi; | ||
| 6414 | |||
| 6415 | RDLO = (result & 0xFFFFFFFF); | ||
| 6416 | RDHI = ((result >> 32) & 0xFFFFFFFF); | ||
| 6417 | } | ||
| 6418 | cpu->Reg[15] += GET_INST_SIZE(cpu); | ||
| 6419 | INC_PC(sizeof(umaal_inst)); | ||
| 6420 | FETCH_INST; | ||
| 6421 | GOTO_NEXT_INST; | ||
| 6422 | } | ||
| 6377 | UMLAL_INST: | 6423 | UMLAL_INST: |
| 6378 | { | 6424 | { |
| 6379 | INC_ICOUNTER; | 6425 | INC_ICOUNTER; |
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index b846fbe9c..7a319b635 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp | |||
| @@ -1356,7 +1356,13 @@ mainswitch: | |||
| 1356 | } | 1356 | } |
| 1357 | break; | 1357 | break; |
| 1358 | 1358 | ||
| 1359 | case 0x04: /* SUB reg */ | 1359 | case 0x04: /* SUB reg */ |
| 1360 | // Signifies UMAAL | ||
| 1361 | if (state->is_v6 && BITS(4, 7) == 0x09) { | ||
| 1362 | if (handle_v6_insn(state, instr)) | ||
| 1363 | break; | ||
| 1364 | } | ||
| 1365 | |||
| 1360 | #ifdef MODET | 1366 | #ifdef MODET |
| 1361 | if (BITS (4, 7) == 0xB) { | 1367 | if (BITS (4, 7) == 0xB) { |
| 1362 | /* STRH immediate offset, no write-back, down, post indexed. */ | 1368 | /* STRH immediate offset, no write-back, down, post indexed. */ |
| @@ -3103,12 +3109,18 @@ mainswitch: | |||
| 3103 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); | 3109 | state->Reg[idest] = (state->Reg[rfis] & 0xFFFF) | ((state->Reg[rlast] << ishi) & 0xFFFF0000); |
| 3104 | break; | 3110 | break; |
| 3105 | } else if ((instr & 0x70) == 0x50) { //pkhtb | 3111 | } else if ((instr & 0x70) == 0x50) { //pkhtb |
| 3106 | u8 idest = BITS(12, 15); | 3112 | const u8 rd_idx = BITS(12, 15); |
| 3107 | u8 rfis = BITS(16, 19); | 3113 | const u8 rn_idx = BITS(16, 19); |
| 3108 | u8 rlast = BITS(0, 3); | 3114 | const u8 rm_idx = BITS(0, 3); |
| 3109 | u8 ishi = BITS(7, 11); | 3115 | const u8 imm5 = BITS(7, 11); |
| 3110 | if (ishi == 0)ishi = 0x20; | 3116 | |
| 3111 | state->Reg[idest] = (((int)(state->Reg[rlast]) >> (int)(ishi))& 0xFFFF) | ((state->Reg[rfis]) & 0xFFFF0000); | 3117 | ARMword val; |
| 3118 | if (imm5 >= 32) | ||
| 3119 | val = (state->Reg[rm_idx] >> 31); | ||
| 3120 | else | ||
| 3121 | val = (state->Reg[rm_idx] >> imm5); | ||
| 3122 | |||
| 3123 | state->Reg[rd_idx] = (val & 0xFFFF) | ((state->Reg[rn_idx]) & 0xFFFF0000); | ||
| 3112 | break; | 3124 | break; |
| 3113 | } else if (BIT (4)) { | 3125 | } else if (BIT (4)) { |
| 3114 | #ifdef MODE32 | 3126 | #ifdef MODE32 |
| @@ -5669,16 +5681,29 @@ L_stm_s_takeabort: | |||
| 5669 | /* Attempt to emulate an ARMv6 instruction. | 5681 | /* Attempt to emulate an ARMv6 instruction. |
| 5670 | Returns non-zero upon success. */ | 5682 | Returns non-zero upon success. */ |
| 5671 | 5683 | ||
| 5672 | static int | 5684 | static int handle_v6_insn(ARMul_State* state, ARMword instr) { |
| 5673 | handle_v6_insn (ARMul_State * state, ARMword instr) { | 5685 | switch (BITS(20, 27)) { |
| 5674 | ARMword lhs, temp; | ||
| 5675 | |||
| 5676 | switch (BITS (20, 27)) { | ||
| 5677 | case 0x03: | 5686 | case 0x03: |
| 5678 | printf ("Unhandled v6 insn: ldr\n"); | 5687 | printf ("Unhandled v6 insn: ldr\n"); |
| 5679 | break; | 5688 | break; |
| 5680 | case 0x04: | 5689 | case 0x04: // UMAAL |
| 5681 | printf ("Unhandled v6 insn: umaal\n"); | 5690 | { |
| 5691 | const u8 rm_idx = BITS(8, 11); | ||
| 5692 | const u8 rn_idx = BITS(0, 3); | ||
| 5693 | const u8 rd_lo_idx = BITS(12, 15); | ||
| 5694 | const u8 rd_hi_idx = BITS(16, 19); | ||
| 5695 | |||
| 5696 | const u32 rm_val = state->Reg[rm_idx]; | ||
| 5697 | const u32 rn_val = state->Reg[rn_idx]; | ||
| 5698 | const u32 rd_lo_val = state->Reg[rd_lo_idx]; | ||
| 5699 | const u32 rd_hi_val = state->Reg[rd_hi_idx]; | ||
| 5700 | |||
| 5701 | const u64 result = (rn_val * rm_val) + rd_lo_val + rd_hi_val; | ||
| 5702 | |||
| 5703 | state->Reg[rd_lo_idx] = (result & 0xFFFFFFFF); | ||
| 5704 | state->Reg[rd_hi_idx] = ((result >> 32) & 0xFFFFFFFF); | ||
| 5705 | return 1; | ||
| 5706 | } | ||
| 5682 | break; | 5707 | break; |
| 5683 | case 0x06: | 5708 | case 0x06: |
| 5684 | printf ("Unhandled v6 insn: mls/str\n"); | 5709 | printf ("Unhandled v6 insn: mls/str\n"); |
| @@ -5691,7 +5716,7 @@ L_stm_s_takeabort: | |||
| 5691 | /* strex */ | 5716 | /* strex */ |
| 5692 | u32 l = LHSReg; | 5717 | u32 l = LHSReg; |
| 5693 | u32 r = RHSReg; | 5718 | u32 r = RHSReg; |
| 5694 | lhs = LHS; | 5719 | u32 lhs = LHS; |
| 5695 | 5720 | ||
| 5696 | bool enter = false; | 5721 | bool enter = false; |
| 5697 | 5722 | ||
| @@ -5716,7 +5741,7 @@ L_stm_s_takeabort: | |||
| 5716 | case 0x19: | 5741 | case 0x19: |
| 5717 | /* ldrex */ | 5742 | /* ldrex */ |
| 5718 | if (BITS(4, 7) == 0x9) { | 5743 | if (BITS(4, 7) == 0x9) { |
| 5719 | lhs = LHS; | 5744 | u32 lhs = LHS; |
| 5720 | 5745 | ||
| 5721 | state->currentexaddr = lhs; | 5746 | state->currentexaddr = lhs; |
| 5722 | state->currentexval = ARMul_ReadWord(state, lhs); | 5747 | state->currentexval = ARMul_ReadWord(state, lhs); |
| @@ -5735,7 +5760,7 @@ L_stm_s_takeabort: | |||
| 5735 | case 0x1c: | 5760 | case 0x1c: |
| 5736 | if (BITS(4, 7) == 0x9) { | 5761 | if (BITS(4, 7) == 0x9) { |
| 5737 | /* strexb */ | 5762 | /* strexb */ |
| 5738 | lhs = LHS; | 5763 | u32 lhs = LHS; |
| 5739 | 5764 | ||
| 5740 | bool enter = false; | 5765 | bool enter = false; |
| 5741 | 5766 | ||
| @@ -5765,11 +5790,11 @@ L_stm_s_takeabort: | |||
| 5765 | case 0x1d: | 5790 | case 0x1d: |
| 5766 | if ((BITS(4, 7)) == 0x9) { | 5791 | if ((BITS(4, 7)) == 0x9) { |
| 5767 | /* ldrexb */ | 5792 | /* ldrexb */ |
| 5768 | temp = LHS; | 5793 | u32 lhs = LHS; |
| 5769 | LoadByte(state, instr, temp, LUNSIGNED); | 5794 | LoadByte(state, instr, lhs, LUNSIGNED); |
| 5770 | 5795 | ||
| 5771 | state->currentexaddr = temp; | 5796 | state->currentexaddr = lhs; |
| 5772 | state->currentexval = (u32)ARMul_ReadByte(state, temp); | 5797 | state->currentexval = (u32)ARMul_ReadByte(state, lhs); |
| 5773 | 5798 | ||
| 5774 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); | 5799 | //state->Reg[BITS(12, 15)] = ARMul_LoadByte(state, state->Reg[BITS(16, 19)]); |
| 5775 | //printf("ldrexb\n"); | 5800 | //printf("ldrexb\n"); |
| @@ -5799,83 +5824,115 @@ L_stm_s_takeabort: | |||
| 5799 | case 0x3f: | 5824 | case 0x3f: |
| 5800 | printf ("Unhandled v6 insn: rbit\n"); | 5825 | printf ("Unhandled v6 insn: rbit\n"); |
| 5801 | break; | 5826 | break; |
| 5802 | case 0x61: | 5827 | case 0x61: // SSUB16, SADD16, SSAX, and SASX |
| 5803 | if ((instr & 0xFF0) == 0xf70) { //ssub16 | 5828 | if ((instr & 0xFF0) == 0xf70 || (instr & 0xFF0) == 0xf10 || |
| 5804 | u8 tar = BITS(12, 15); | 5829 | (instr & 0xFF0) == 0xf50 || (instr & 0xFF0) == 0xf30) |
| 5805 | u8 src1 = BITS(16, 19); | 5830 | { |
| 5806 | u8 src2 = BITS(0, 3); | 5831 | const u8 rd_idx = BITS(12, 15); |
| 5807 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5832 | const u8 rm_idx = BITS(0, 3); |
| 5808 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5833 | const u8 rn_idx = BITS(16, 19); |
| 5809 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5834 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5810 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5835 | const s16 rn_hi = ((state->Reg[rn_idx] >> 16) & 0xFFFF); |
| 5811 | state->Reg[tar] = ((a1 - a2) & 0xFFFF) | (((b1 - b2) & 0xFFFF) << 0x10); | 5836 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); |
| 5812 | return 1; | 5837 | const s16 rm_hi = ((state->Reg[rm_idx] >> 16) & 0xFFFF); |
| 5813 | } else if ((instr & 0xFF0) == 0xf10) { //sadd16 | 5838 | |
| 5814 | u8 tar = BITS(12, 15); | 5839 | s32 lo_result; |
| 5815 | u8 src1 = BITS(16, 19); | 5840 | s32 hi_result; |
| 5816 | u8 src2 = BITS(0, 3); | 5841 | |
| 5817 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5842 | // SSUB16 |
| 5818 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5843 | if ((instr & 0xFF0) == 0xf70) { |
| 5819 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5844 | lo_result = (rn_lo - rm_lo); |
| 5820 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5845 | hi_result = (rn_hi - rm_hi); |
| 5821 | state->Reg[tar] = ((a1 + a2) & 0xFFFF) | (((b1 + b2) & 0xFFFF) << 0x10); | 5846 | } |
| 5822 | return 1; | 5847 | // SADD16 |
| 5823 | } else if ((instr & 0xFF0) == 0xf50) { //ssax | 5848 | else if ((instr & 0xFF0) == 0xf10) { |
| 5824 | u8 tar = BITS(12, 15); | 5849 | lo_result = (rn_lo + rm_lo); |
| 5825 | u8 src1 = BITS(16, 19); | 5850 | hi_result = (rn_hi + rm_hi); |
| 5826 | u8 src2 = BITS(0, 3); | 5851 | } |
| 5827 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5852 | // SSAX |
| 5828 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5853 | else if ((instr & 0xFF0) == 0xf50) { |
| 5829 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5854 | lo_result = (rn_lo + rm_hi); |
| 5830 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5855 | hi_result = (rn_hi - rm_lo); |
| 5831 | state->Reg[tar] = ((a1 + b2) & 0xFFFF) | (((a2 - b1) & 0xFFFF) << 0x10); | 5856 | } |
| 5832 | return 1; | 5857 | // SASX |
| 5833 | } else if ((instr & 0xFF0) == 0xf30) { //sasx | 5858 | else { |
| 5834 | u8 tar = BITS(12, 15); | 5859 | lo_result = (rn_lo - rm_hi); |
| 5835 | u8 src1 = BITS(16, 19); | 5860 | hi_result = (rn_hi + rm_lo); |
| 5836 | u8 src2 = BITS(0, 3); | 5861 | } |
| 5837 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5862 | |
| 5838 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5863 | state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); |
| 5839 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5864 | |
| 5840 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5865 | if (lo_result >= 0) { |
| 5841 | state->Reg[tar] = ((a1 - b2) & 0xFFFF) | (((a2 + b1) & 0xFFFF) << 0x10); | 5866 | state->Cpsr |= (1 << 16); |
| 5867 | state->Cpsr |= (1 << 17); | ||
| 5868 | } else { | ||
| 5869 | state->Cpsr &= ~(1 << 16); | ||
| 5870 | state->Cpsr &= ~(1 << 17); | ||
| 5871 | } | ||
| 5872 | |||
| 5873 | if (hi_result >= 0) { | ||
| 5874 | state->Cpsr |= (1 << 18); | ||
| 5875 | state->Cpsr |= (1 << 19); | ||
| 5876 | } else { | ||
| 5877 | state->Cpsr &= ~(1 << 18); | ||
| 5878 | state->Cpsr &= ~(1 << 19); | ||
| 5879 | } | ||
| 5842 | return 1; | 5880 | return 1; |
| 5843 | } else printf ("Unhandled v6 insn: sadd/ssub/ssax/sasx\n"); | 5881 | } else { |
| 5882 | printf("Unhandled v6 insn: %08x", BITS(20, 27)); | ||
| 5883 | } | ||
| 5844 | break; | 5884 | break; |
| 5845 | case 0x62: | 5885 | case 0x62: // QADD16, QASX, QSAX, and QSUB16 |
| 5846 | if ((instr & 0xFF0) == 0xf70) { //QSUB16 | 5886 | if ((instr & 0xFF0) == 0xf10 || (instr & 0xFF0) == 0xf30 || |
| 5847 | u8 tar = BITS(12, 15); | 5887 | (instr & 0xFF0) == 0xf50 || (instr & 0xFF0) == 0xf70) |
| 5848 | u8 src1 = BITS(16, 19); | 5888 | { |
| 5849 | u8 src2 = BITS(0, 3); | 5889 | const u8 rd_idx = BITS(12, 15); |
| 5850 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5890 | const u8 rn_idx = BITS(16, 19); |
| 5851 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5891 | const u8 rm_idx = BITS(0, 3); |
| 5852 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5892 | const s16 rm_lo = (state->Reg[rm_idx] & 0xFFFF); |
| 5853 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5893 | const s16 rm_hi = ((state->Reg[rm_idx] >> 0x10) & 0xFFFF); |
| 5854 | s32 res1 = (a1 - b1); | 5894 | const s16 rn_lo = (state->Reg[rn_idx] & 0xFFFF); |
| 5855 | s32 res2 = (a2 - b2); | 5895 | const s16 rn_hi = ((state->Reg[rn_idx] >> 0x10) & 0xFFFF); |
| 5856 | if (res1 > 0x7FFF) res1 = 0x7FFF; | 5896 | |
| 5857 | if (res2 > 0x7FFF) res2 = 0x7FFF; | 5897 | s32 lo_result; |
| 5858 | if (res1 < 0x7FFF) res1 = -0x8000; | 5898 | s32 hi_result; |
| 5859 | if (res2 < 0x7FFF) res2 = -0x8000; | 5899 | |
| 5860 | state->Reg[tar] = (res1 & 0xFFFF) | ((res2 & 0xFFFF) << 0x10); | 5900 | // QADD16 |
| 5861 | return 1; | 5901 | if ((instr & 0xFF0) == 0xf10) { |
| 5862 | } else if ((instr & 0xFF0) == 0xf10) { //QADD16 | 5902 | lo_result = (rn_lo + rm_lo); |
| 5863 | u8 tar = BITS(12, 15); | 5903 | hi_result = (rn_hi + rm_hi); |
| 5864 | u8 src1 = BITS(16, 19); | 5904 | } |
| 5865 | u8 src2 = BITS(0, 3); | 5905 | // QASX |
| 5866 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 5906 | else if ((instr & 0xFF0) == 0xf30) { |
| 5867 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 5907 | lo_result = (rn_lo - rm_hi); |
| 5868 | s16 b1 = (state->Reg[src2] & 0xFFFF); | 5908 | hi_result = (rn_hi + rm_lo); |
| 5869 | s16 b2 = ((state->Reg[src2] >> 0x10) & 0xFFFF); | 5909 | } |
| 5870 | s32 res1 = (a1 + b1); | 5910 | // QSAX |
| 5871 | s32 res2 = (a2 + b2); | 5911 | else if ((instr & 0xFF0) == 0xf50) { |
| 5872 | if (res1 > 0x7FFF) res1 = 0x7FFF; | 5912 | lo_result = (rn_lo + rm_hi); |
| 5873 | if (res2 > 0x7FFF) res2 = 0x7FFF; | 5913 | hi_result = (rn_hi - rm_lo); |
| 5874 | if (res1 < 0x7FFF) res1 = -0x8000; | 5914 | } |
| 5875 | if (res2 < 0x7FFF) res2 = -0x8000; | 5915 | // QSUB16 |
| 5876 | state->Reg[tar] = ((res1) & 0xFFFF) | (((res2) & 0xFFFF) << 0x10); | 5916 | else { |
| 5917 | lo_result = (rn_lo - rm_lo); | ||
| 5918 | hi_result = (rn_hi - rm_hi); | ||
| 5919 | } | ||
| 5920 | |||
| 5921 | if (lo_result > 0x7FFF) | ||
| 5922 | lo_result = 0x7FFF; | ||
| 5923 | else if (lo_result < -0x8000) | ||
| 5924 | lo_result = -0x8000; | ||
| 5925 | |||
| 5926 | if (hi_result > 0x7FFF) | ||
| 5927 | hi_result = 0x7FFF; | ||
| 5928 | else if (hi_result < -0x8000) | ||
| 5929 | hi_result = -0x8000; | ||
| 5930 | |||
| 5931 | state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); | ||
| 5877 | return 1; | 5932 | return 1; |
| 5878 | } else printf ("Unhandled v6 insn: qadd16/qsub16\n"); | 5933 | } else { |
| 5934 | printf("Unhandled v6 insn: %08x", BITS(20, 27)); | ||
| 5935 | } | ||
| 5879 | break; | 5936 | break; |
| 5880 | case 0x63: | 5937 | case 0x63: |
| 5881 | printf ("Unhandled v6 insn: shadd/shsub\n"); | 5938 | printf ("Unhandled v6 insn: shadd/shsub\n"); |
| @@ -5925,11 +5982,29 @@ L_stm_s_takeabort: | |||
| 5925 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); | 5982 | b2 = ((u8)(from >> 8) + (u8)(to >> 8)); |
| 5926 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); | 5983 | b3 = ((u8)(from >> 16) + (u8)(to >> 16)); |
| 5927 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); | 5984 | b4 = ((u8)(from >> 24) + (u8)(to >> 24)); |
| 5928 | if (b1 & 0xffffff00) state->Cpsr |= (1 << 16); | 5985 | |
| 5929 | if (b2 & 0xffffff00) state->Cpsr |= (1 << 17); | 5986 | if (b1 & 0xffffff00) |
| 5930 | if (b3 & 0xffffff00) state->Cpsr |= (1 << 18); | 5987 | state->Cpsr |= (1 << 16); |
| 5931 | if (b4 & 0xffffff00) state->Cpsr |= (1 << 19); | 5988 | else |
| 5989 | state->Cpsr &= ~(1 << 16); | ||
| 5990 | |||
| 5991 | if (b2 & 0xffffff00) | ||
| 5992 | state->Cpsr |= (1 << 17); | ||
| 5993 | else | ||
| 5994 | state->Cpsr &= ~(1 << 17); | ||
| 5995 | |||
| 5996 | if (b3 & 0xffffff00) | ||
| 5997 | state->Cpsr |= (1 << 18); | ||
| 5998 | else | ||
| 5999 | state->Cpsr &= ~(1 << 18); | ||
| 6000 | |||
| 6001 | |||
| 6002 | if (b4 & 0xffffff00) | ||
| 6003 | state->Cpsr |= (1 << 19); | ||
| 6004 | else | ||
| 6005 | state->Cpsr &= ~(1 << 19); | ||
| 5932 | } | 6006 | } |
| 6007 | |||
| 5933 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); | 6008 | state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); |
| 5934 | return 1; | 6009 | return 1; |
| 5935 | } | 6010 | } |
| @@ -6016,22 +6091,28 @@ L_stm_s_takeabort: | |||
| 6016 | //ichfly | 6091 | //ichfly |
| 6017 | //SSAT16 | 6092 | //SSAT16 |
| 6018 | { | 6093 | { |
| 6019 | u8 tar = BITS(12, 15); | 6094 | const u8 rd_idx = BITS(12, 15); |
| 6020 | u8 src = BITS(0, 3); | 6095 | const u8 rn_idx = BITS(0, 3); |
| 6021 | u8 val = BITS(16, 19) + 1; | 6096 | const u8 num_bits = BITS(16, 19) + 1; |
| 6022 | s16 a1 = (state->Reg[src]); | 6097 | const s16 min = -(0x8000 >> (16 - num_bits)); |
| 6023 | s16 a2 = (state->Reg[src] >> 0x10); | 6098 | const s16 max = (0x7FFF >> (16 - num_bits)); |
| 6024 | s16 min = (s16)(0x8000 >> (16 - val)); | 6099 | s16 rn_lo = (state->Reg[rn_idx]); |
| 6025 | s16 max = 0x7FFF >> (16 - val); | 6100 | s16 rn_hi = (state->Reg[rn_idx] >> 16); |
| 6026 | if (min > a1) a1 = min; | 6101 | |
| 6027 | if (max < a1) a1 = max; | 6102 | if (rn_lo > max) |
| 6028 | if (min > a2) a2 = min; | 6103 | rn_lo = max; |
| 6029 | if (max < a2) a2 = max; | 6104 | else if (rn_lo < min) |
| 6030 | u32 temp2 = ((u32)(a2)) << 0x10; | 6105 | rn_lo = min; |
| 6031 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | 6106 | |
| 6107 | if (rn_hi > max) | ||
| 6108 | rn_hi = max; | ||
| 6109 | else if (rn_hi < min) | ||
| 6110 | rn_hi = min; | ||
| 6111 | |||
| 6112 | state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi & 0xFFFF) << 16); | ||
| 6113 | return 1; | ||
| 6032 | } | 6114 | } |
| 6033 | 6115 | ||
| 6034 | return 1; | ||
| 6035 | default: | 6116 | default: |
| 6036 | break; | 6117 | break; |
| 6037 | } | 6118 | } |
| @@ -6044,7 +6125,7 @@ L_stm_s_takeabort: | |||
| 6044 | break; | 6125 | break; |
| 6045 | } | 6126 | } |
| 6046 | 6127 | ||
| 6047 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6128 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF); |
| 6048 | if (Rm & 0x80) | 6129 | if (Rm & 0x80) |
| 6049 | Rm |= 0xffffff00; | 6130 | Rm |= 0xffffff00; |
| 6050 | 6131 | ||
| @@ -6053,11 +6134,12 @@ L_stm_s_takeabort: | |||
| 6053 | state->Reg[BITS(12, 15)] = Rm; | 6134 | state->Reg[BITS(12, 15)] = Rm; |
| 6054 | else | 6135 | else |
| 6055 | /* SXTAB */ | 6136 | /* SXTAB */ |
| 6056 | state->Reg[BITS(12, 15)] += Rm; | 6137 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6057 | 6138 | ||
| 6058 | return 1; | 6139 | return 1; |
| 6059 | } | 6140 | } |
| 6060 | case 0x6b: { | 6141 | case 0x6b: |
| 6142 | { | ||
| 6061 | ARMword Rm; | 6143 | ARMword Rm; |
| 6062 | int ror = -1; | 6144 | int ror = -1; |
| 6063 | 6145 | ||
| @@ -6075,10 +6157,10 @@ L_stm_s_takeabort: | |||
| 6075 | ror = 24; | 6157 | ror = 24; |
| 6076 | break; | 6158 | break; |
| 6077 | 6159 | ||
| 6078 | case 0xf3: | 6160 | case 0xf3: // REV |
| 6079 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); | 6161 | DEST = ((RHS & 0xFF) << 24) | ((RHS & 0xFF00)) << 8 | ((RHS & 0xFF0000) >> 8) | ((RHS & 0xFF000000) >> 24); |
| 6080 | return 1; | 6162 | return 1; |
| 6081 | case 0xfb: | 6163 | case 0xfb: // REV16 |
| 6082 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); | 6164 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00)) >> 8 | ((RHS & 0xFF0000) << 8) | ((RHS & 0xFF000000) >> 8); |
| 6083 | return 1; | 6165 | return 1; |
| 6084 | default: | 6166 | default: |
| @@ -6088,7 +6170,7 @@ L_stm_s_takeabort: | |||
| 6088 | if (ror == -1) | 6170 | if (ror == -1) |
| 6089 | break; | 6171 | break; |
| 6090 | 6172 | ||
| 6091 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6173 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF); |
| 6092 | if (Rm & 0x8000) | 6174 | if (Rm & 0x8000) |
| 6093 | Rm |= 0xffff0000; | 6175 | Rm |= 0xffff0000; |
| 6094 | 6176 | ||
| @@ -6151,18 +6233,27 @@ L_stm_s_takeabort: | |||
| 6151 | //ichfly | 6233 | //ichfly |
| 6152 | //USAT16 | 6234 | //USAT16 |
| 6153 | { | 6235 | { |
| 6154 | u8 tar = BITS(12, 15); | 6236 | const u8 rd_idx = BITS(12, 15); |
| 6155 | u8 src = BITS(0, 3); | 6237 | const u8 rn_idx = BITS(0, 3); |
| 6156 | u8 val = BITS(16, 19); | 6238 | const u8 num_bits = BITS(16, 19); |
| 6157 | s16 a1 = (state->Reg[src]); | 6239 | const s16 max = 0xFFFF >> (16 - num_bits); |
| 6158 | s16 a2 = (state->Reg[src] >> 0x10); | 6240 | s16 rn_lo = (state->Reg[rn_idx]); |
| 6159 | s16 max = 0xFFFF >> (16 - val); | 6241 | s16 rn_hi = (state->Reg[rn_idx] >> 16); |
| 6160 | if (max < a1) a1 = max; | 6242 | |
| 6161 | if (max < a2) a2 = max; | 6243 | if (max < rn_lo) |
| 6162 | u32 temp2 = ((u32)(a2)) << 0x10; | 6244 | rn_lo = max; |
| 6163 | state->Reg[tar] = (a1 & 0xFFFF) | (temp2); | 6245 | else if (rn_lo < 0) |
| 6246 | rn_lo = 0; | ||
| 6247 | |||
| 6248 | if (max < rn_hi) | ||
| 6249 | rn_hi = max; | ||
| 6250 | else if (rn_hi < 0) | ||
| 6251 | rn_hi = 0; | ||
| 6252 | |||
| 6253 | state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi << 16) & 0xFFFF); | ||
| 6254 | return 1; | ||
| 6164 | } | 6255 | } |
| 6165 | return 1; | 6256 | |
| 6166 | default: | 6257 | default: |
| 6167 | break; | 6258 | break; |
| 6168 | } | 6259 | } |
| @@ -6175,7 +6266,7 @@ L_stm_s_takeabort: | |||
| 6175 | break; | 6266 | break; |
| 6176 | } | 6267 | } |
| 6177 | 6268 | ||
| 6178 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF); | 6269 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFF) & 0xFF); |
| 6179 | 6270 | ||
| 6180 | if (BITS(16, 19) == 0xf) | 6271 | if (BITS(16, 19) == 0xf) |
| 6181 | /* UXTB */ | 6272 | /* UXTB */ |
| @@ -6205,9 +6296,13 @@ L_stm_s_takeabort: | |||
| 6205 | ror = 24; | 6296 | ror = 24; |
| 6206 | break; | 6297 | break; |
| 6207 | 6298 | ||
| 6208 | case 0xfb: | 6299 | case 0xfb: // REVSH |
| 6209 | printf("Unhandled v6 insn: revsh\n"); | 6300 | { |
| 6210 | return 0; | 6301 | DEST = ((RHS & 0xFF) << 8) | ((RHS & 0xFF00) >> 8); |
| 6302 | if (DEST & 0x8000) | ||
| 6303 | DEST |= 0xffff0000; | ||
| 6304 | return 1; | ||
| 6305 | } | ||
| 6211 | default: | 6306 | default: |
| 6212 | break; | 6307 | break; |
| 6213 | } | 6308 | } |
| @@ -6215,13 +6310,13 @@ L_stm_s_takeabort: | |||
| 6215 | if (ror == -1) | 6310 | if (ror == -1) |
| 6216 | break; | 6311 | break; |
| 6217 | 6312 | ||
| 6218 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF); | 6313 | Rm = ((state->Reg[BITS(0, 3)] >> ror) & 0xFFFF) | (((state->Reg[BITS(0, 3)] << (32 - ror)) & 0xFFFF) & 0xFFFF); |
| 6219 | 6314 | ||
| 6220 | /* UXT */ | 6315 | /* UXT */ |
| 6221 | /* state->Reg[BITS (12, 15)] = Rm; */ | 6316 | /* state->Reg[BITS (12, 15)] = Rm; */ |
| 6222 | /* dyf add */ | 6317 | /* dyf add */ |
| 6223 | if (BITS(16, 19) == 0xf) { | 6318 | if (BITS(16, 19) == 0xf) { |
| 6224 | state->Reg[BITS(12, 15)] = (Rm >> (8 * BITS(10, 11))) & 0x0000FFFF; | 6319 | state->Reg[BITS(12, 15)] = Rm; |
| 6225 | } | 6320 | } |
| 6226 | else { | 6321 | else { |
| 6227 | /* UXTAH */ | 6322 | /* UXTAH */ |
| @@ -6229,7 +6324,7 @@ L_stm_s_takeabort: | |||
| 6229 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] | 6324 | // printf("rd is %x rn is %x rm is %x rotate is %x\n", state->Reg[BITS (12, 15)], state->Reg[BITS (16, 19)] |
| 6230 | // , Rm, BITS(10, 11)); | 6325 | // , Rm, BITS(10, 11)); |
| 6231 | // printf("icounter is %lld\n", state->NumInstrs); | 6326 | // printf("icounter is %lld\n", state->NumInstrs); |
| 6232 | state->Reg[BITS(12, 15)] = (state->Reg[BITS(16, 19)] >> (8 * (BITS(10, 11)))) + Rm; | 6327 | state->Reg[BITS(12, 15)] = state->Reg[BITS(16, 19)] + Rm; |
| 6233 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); | 6328 | // printf("rd is %x\n", state->Reg[BITS (12, 15)]); |
| 6234 | // exit(-1); | 6329 | // exit(-1); |
| 6235 | } | 6330 | } |
| @@ -6237,45 +6332,46 @@ L_stm_s_takeabort: | |||
| 6237 | return 1; | 6332 | return 1; |
| 6238 | } | 6333 | } |
| 6239 | case 0x70: | 6334 | case 0x70: |
| 6240 | if ((instr & 0xf0d0) == 0xf010) { //smuad //ichfly | 6335 | // ichfly |
| 6241 | u8 tar = BITS(16, 19); | 6336 | // SMUAD, SMUSD, SMLAD, and SMLSD |
| 6242 | u8 src1 = BITS(0, 3); | 6337 | if ((instr & 0xf0d0) == 0xf010 || (instr & 0xf0d0) == 0xf050 || |
| 6243 | u8 src2 = BITS(8, 11); | 6338 | (instr & 0xd0) == 0x10 || (instr & 0xd0) == 0x50) |
| 6244 | u8 swap = BIT(5); | 6339 | { |
| 6245 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6340 | const u8 rd_idx = BITS(16, 19); |
| 6246 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6341 | const u8 rn_idx = BITS(0, 3); |
| 6247 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6342 | const u8 rm_idx = BITS(8, 11); |
| 6248 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6343 | const u8 ra_idx = BITS(12, 15); |
| 6249 | state->Reg[tar] = a1*a2 + b1*b2; | 6344 | const bool do_swap = (BIT(5) == 1); |
| 6250 | return 1; | 6345 | |
| 6251 | 6346 | u32 rm_val = state->Reg[rm_idx]; | |
| 6252 | } else if ((instr & 0xf0d0) == 0xf050) { //smusd | 6347 | const u32 rn_val = state->Reg[rn_idx]; |
| 6253 | u8 tar = BITS(16, 19); | 6348 | |
| 6254 | u8 src1 = BITS(0, 3); | 6349 | if (do_swap) |
| 6255 | u8 src2 = BITS(8, 11); | 6350 | rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16)); |
| 6256 | u8 swap = BIT(5); | 6351 | |
| 6257 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6352 | const s16 rm_lo = (rm_val & 0xFFFF); |
| 6258 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6353 | const s16 rm_hi = ((rm_val >> 16) & 0xFFFF); |
| 6259 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6354 | const s16 rn_lo = (rn_val & 0xFFFF); |
| 6260 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6355 | const s16 rn_hi = ((rn_val >> 16) & 0xFFFF); |
| 6261 | state->Reg[tar] = a1*a2 - b1*b2; | 6356 | |
| 6262 | return 1; | 6357 | // SMUAD |
| 6263 | } else if ((instr & 0xd0) == 0x10) { //smlad | 6358 | if ((instr & 0xf0d0) == 0xf010) { |
| 6264 | u8 tar = BITS(16, 19); | 6359 | state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi); |
| 6265 | u8 src1 = BITS(0, 3); | 6360 | } |
| 6266 | u8 src2 = BITS(8, 11); | 6361 | // SMUSD |
| 6267 | u8 src3 = BITS(12, 15); | 6362 | else if ((instr & 0xf0d0) == 0xf050) { |
| 6268 | u8 swap = BIT(5); | 6363 | state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi); |
| 6269 | 6364 | } | |
| 6270 | u32 a3 = state->Reg[src3]; | 6365 | // SMLAD |
| 6271 | 6366 | else if ((instr & 0xd0) == 0x10) { | |
| 6272 | s16 a1 = (state->Reg[src1] & 0xFFFF); | 6367 | state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx]; |
| 6273 | s16 a2 = ((state->Reg[src1] >> 0x10) & 0xFFFF); | 6368 | } |
| 6274 | s16 b1 = swap ? ((state->Reg[src2] >> 0x10) & 0xFFFF) : (state->Reg[src2] & 0xFFFF); | 6369 | // SMLSD |
| 6275 | s16 b2 = swap ? (state->Reg[src2] & 0xFFFF) : ((state->Reg[src2] >> 0x10) & 0xFFFF); | 6370 | else { |
| 6276 | state->Reg[tar] = a1*a2 + b1*b2 + a3; | 6371 | state->Reg[rd_idx] = ((rn_lo * rm_lo) - (rn_hi * rm_hi)) + (s32)state->Reg[ra_idx]; |
| 6372 | } | ||
| 6277 | return 1; | 6373 | return 1; |
| 6278 | } else printf ("Unhandled v6 insn: smuad/smusd/smlad/smlsd\n"); | 6374 | } |
| 6279 | break; | 6375 | break; |
| 6280 | case 0x74: | 6376 | case 0x74: |
| 6281 | printf ("Unhandled v6 insn: smlald/smlsld\n"); | 6377 | printf ("Unhandled v6 insn: smlald/smlsld\n"); |
| @@ -6284,7 +6380,30 @@ L_stm_s_takeabort: | |||
| 6284 | printf ("Unhandled v6 insn: smmla/smmls/smmul\n"); | 6380 | printf ("Unhandled v6 insn: smmla/smmls/smmul\n"); |
| 6285 | break; | 6381 | break; |
| 6286 | case 0x78: | 6382 | case 0x78: |
| 6287 | printf ("Unhandled v6 insn: usad/usada8\n"); | 6383 | if (BITS(20, 24) == 0x18) |
| 6384 | { | ||
| 6385 | const u8 rm_idx = BITS(8, 11); | ||
| 6386 | const u8 rn_idx = BITS(0, 3); | ||
| 6387 | const u8 rd_idx = BITS(16, 19); | ||
| 6388 | |||
| 6389 | const u32 rm_val = state->Reg[rm_idx]; | ||
| 6390 | const u32 rn_val = state->Reg[rn_idx]; | ||
| 6391 | |||
| 6392 | const u8 diff1 = (u8)std::labs((rn_val & 0xFF) - (rm_val & 0xFF)); | ||
| 6393 | const u8 diff2 = (u8)std::labs(((rn_val >> 8) & 0xFF) - ((rm_val >> 8) & 0xFF)); | ||
| 6394 | const u8 diff3 = (u8)std::labs(((rn_val >> 16) & 0xFF) - ((rm_val >> 16) & 0xFF)); | ||
| 6395 | const u8 diff4 = (u8)std::labs(((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF)); | ||
| 6396 | |||
| 6397 | u32 finalDif = (diff1 + diff2 + diff3 + diff4); | ||
| 6398 | |||
| 6399 | // Op is USADA8 if true. | ||
| 6400 | const u8 ra_idx = BITS(12, 15); | ||
| 6401 | if (ra_idx != 15) | ||
| 6402 | finalDif += state->Reg[ra_idx]; | ||
| 6403 | |||
| 6404 | state->Reg[rd_idx] = finalDif; | ||
| 6405 | return 1; | ||
| 6406 | } | ||
| 6288 | break; | 6407 | break; |
| 6289 | case 0x7a: | 6408 | case 0x7a: |
| 6290 | printf ("Unhandled v6 insn: usbfx\n"); | 6409 | printf ("Unhandled v6 insn: usbfx\n"); |
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 871900497..6c33d8b78 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp | |||
| @@ -614,7 +614,7 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f | |||
| 614 | exceptions |= FPSCR_IDC; | 614 | exceptions |= FPSCR_IDC; |
| 615 | 615 | ||
| 616 | if (tm & VFP_NAN) | 616 | if (tm & VFP_NAN) |
| 617 | vsm.sign = 0; | 617 | vsm.sign = 1; |
| 618 | 618 | ||
| 619 | if (vsm.exponent >= 127 + 32) { | 619 | if (vsm.exponent >= 127 + 32) { |
| 620 | d = vsm.sign ? 0 : 0xffffffff; | 620 | d = vsm.sign ? 0 : 0xffffffff; |
| @@ -1148,7 +1148,10 @@ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) | |||
| 1148 | /* | 1148 | /* |
| 1149 | * Subtraction is addition with one sign inverted. | 1149 | * Subtraction is addition with one sign inverted. |
| 1150 | */ | 1150 | */ |
| 1151 | return vfp_single_fadd(state, sd, sn, vfp_single_packed_negate(m), fpscr); | 1151 | if (m != 0x7FC00000) // Only negate if m isn't NaN. |
| 1152 | m = vfp_single_packed_negate(m); | ||
| 1153 | |||
| 1154 | return vfp_single_fadd(state, sd, sn, m, fpscr); | ||
| 1152 | } | 1155 | } |
| 1153 | 1156 | ||
| 1154 | /* | 1157 | /* |
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index 18c314884..d7959b2ca 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h | |||
| @@ -143,7 +143,16 @@ public: | |||
| 143 | case Char: | 143 | case Char: |
| 144 | return std::vector<u8>(string.begin(), string.end()); | 144 | return std::vector<u8>(string.begin(), string.end()); |
| 145 | case Wchar: | 145 | case Wchar: |
| 146 | return std::vector<u8>(u16str.begin(), u16str.end()); | 146 | { |
| 147 | // use two u8 for each character of u16str | ||
| 148 | std::vector<u8> to_return(u16str.size() * 2); | ||
| 149 | for (size_t i = 0; i < u16str.size(); ++i) { | ||
| 150 | u16 tmp_char = u16str.at(i); | ||
| 151 | to_return[i*2] = (tmp_char & 0xFF00) >> 8; | ||
| 152 | to_return[i*2 + 1] = (tmp_char & 0x00FF); | ||
| 153 | } | ||
| 154 | return to_return; | ||
| 155 | } | ||
| 147 | case Empty: | 156 | case Empty: |
| 148 | return {}; | 157 | return {}; |
| 149 | default: | 158 | default: |
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp index 0709b62a1..1e3e9dc60 100644 --- a/src/core/file_sys/archive_romfs.cpp +++ b/src/core/file_sys/archive_romfs.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/make_unique.h" | ||
| 8 | 9 | ||
| 9 | #include "core/file_sys/archive_romfs.h" | 10 | #include "core/file_sys/archive_romfs.h" |
| 10 | #include "core/file_sys/directory_romfs.h" | 11 | #include "core/file_sys/directory_romfs.h" |
| @@ -29,7 +30,7 @@ Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) { | |||
| 29 | * @return Opened file, or nullptr | 30 | * @return Opened file, or nullptr |
| 30 | */ | 31 | */ |
| 31 | std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { | 32 | std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const { |
| 32 | return std::make_unique<File_RomFS>(this); | 33 | return Common::make_unique<File_RomFS>(this); |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | /** | 36 | /** |
| @@ -78,7 +79,7 @@ bool Archive_RomFS::RenameDirectory(const FileSys::Path& src_path, const FileSys | |||
| 78 | * @return Opened directory, or nullptr | 79 | * @return Opened directory, or nullptr |
| 79 | */ | 80 | */ |
| 80 | std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const { | 81 | std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const { |
| 81 | return std::make_unique<Directory_RomFS>(); | 82 | return Common::make_unique<Directory_RomFS>(); |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | } // namespace FileSys | 85 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp new file mode 100644 index 000000000..2414564e4 --- /dev/null +++ b/src/core/file_sys/archive_savedata.cpp | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/archive_savedata.h" | ||
| 11 | #include "core/file_sys/disk_archive.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id) | ||
| 20 | : DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) { | ||
| 21 | LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str()); | ||
| 22 | } | ||
| 23 | |||
| 24 | bool Archive_SaveData::Initialize() { | ||
| 25 | if (!FileUtil::CreateFullPath(mount_point)) { | ||
| 26 | LOG_ERROR(Service_FS, "Unable to create SaveData path."); | ||
| 27 | return false; | ||
| 28 | } | ||
| 29 | |||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h new file mode 100644 index 000000000..d394ad37e --- /dev/null +++ b/src/core/file_sys/archive_savedata.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/disk_archive.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // FileSys namespace | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /// File system interface to the SaveData archive | ||
| 18 | class Archive_SaveData final : public DiskArchive { | ||
| 19 | public: | ||
| 20 | Archive_SaveData(const std::string& mount_point, u64 program_id); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Initialize the archive. | ||
| 24 | * @return true if it initialized successfully | ||
| 25 | */ | ||
| 26 | bool Initialize(); | ||
| 27 | |||
| 28 | std::string GetName() const override { return "SaveData"; } | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 9d58668e0..dccdf7f67 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp | |||
| @@ -8,8 +8,7 @@ | |||
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | 9 | ||
| 10 | #include "core/file_sys/archive_sdmc.h" | 10 | #include "core/file_sys/archive_sdmc.h" |
| 11 | #include "core/file_sys/directory_sdmc.h" | 11 | #include "core/file_sys/disk_archive.h" |
| 12 | #include "core/file_sys/file_sdmc.h" | ||
| 13 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 14 | 13 | ||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -17,18 +16,10 @@ | |||
| 17 | 16 | ||
| 18 | namespace FileSys { | 17 | namespace FileSys { |
| 19 | 18 | ||
| 20 | Archive_SDMC::Archive_SDMC(const std::string& mount_point) { | 19 | Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) { |
| 21 | this->mount_point = mount_point; | ||
| 22 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); | 20 | LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str()); |
| 23 | } | 21 | } |
| 24 | 22 | ||
| 25 | Archive_SDMC::~Archive_SDMC() { | ||
| 26 | } | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Initialize the archive. | ||
| 30 | * @return true if it initialized successfully | ||
| 31 | */ | ||
| 32 | bool Archive_SDMC::Initialize() { | 23 | bool Archive_SDMC::Initialize() { |
| 33 | if (!Settings::values.use_virtual_sd) { | 24 | if (!Settings::values.use_virtual_sd) { |
| 34 | LOG_WARNING(Service_FS, "SDMC disabled by config."); | 25 | LOG_WARNING(Service_FS, "SDMC disabled by config."); |
| @@ -43,74 +34,4 @@ bool Archive_SDMC::Initialize() { | |||
| 43 | return true; | 34 | return true; |
| 44 | } | 35 | } |
| 45 | 36 | ||
| 46 | /** | ||
| 47 | * Open a file specified by its path, using the specified mode | ||
| 48 | * @param path Path relative to the archive | ||
| 49 | * @param mode Mode to open the file with | ||
| 50 | * @return Opened file, or nullptr | ||
| 51 | */ | ||
| 52 | std::unique_ptr<FileBackend> Archive_SDMC::OpenFile(const Path& path, const Mode mode) const { | ||
| 53 | LOG_DEBUG(Service_FS, "called path=%s mode=%u", path.DebugStr().c_str(), mode.hex); | ||
| 54 | File_SDMC* file = new File_SDMC(this, path, mode); | ||
| 55 | if (!file->Open()) | ||
| 56 | return nullptr; | ||
| 57 | return std::unique_ptr<FileBackend>(file); | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Delete a file specified by its path | ||
| 62 | * @param path Path relative to the archive | ||
| 63 | * @return Whether the file could be deleted | ||
| 64 | */ | ||
| 65 | bool Archive_SDMC::DeleteFile(const FileSys::Path& path) const { | ||
| 66 | return FileUtil::Delete(GetMountPoint() + path.AsString()); | ||
| 67 | } | ||
| 68 | |||
| 69 | bool Archive_SDMC::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 70 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Delete a directory specified by its path | ||
| 75 | * @param path Path relative to the archive | ||
| 76 | * @return Whether the directory could be deleted | ||
| 77 | */ | ||
| 78 | bool Archive_SDMC::DeleteDirectory(const FileSys::Path& path) const { | ||
| 79 | return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Create a directory specified by its path | ||
| 84 | * @param path Path relative to the archive | ||
| 85 | * @return Whether the directory could be created | ||
| 86 | */ | ||
| 87 | bool Archive_SDMC::CreateDirectory(const Path& path) const { | ||
| 88 | return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | ||
| 89 | } | ||
| 90 | |||
| 91 | bool Archive_SDMC::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 92 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Open a directory specified by its path | ||
| 97 | * @param path Path relative to the archive | ||
| 98 | * @return Opened directory, or nullptr | ||
| 99 | */ | ||
| 100 | std::unique_ptr<DirectoryBackend> Archive_SDMC::OpenDirectory(const Path& path) const { | ||
| 101 | LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | ||
| 102 | Directory_SDMC* directory = new Directory_SDMC(this, path); | ||
| 103 | if (!directory->Open()) | ||
| 104 | return nullptr; | ||
| 105 | return std::unique_ptr<DirectoryBackend>(directory); | ||
| 106 | } | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Getter for the path used for this Archive | ||
| 110 | * @return Mount point of that passthrough archive | ||
| 111 | */ | ||
| 112 | std::string Archive_SDMC::GetMountPoint() const { | ||
| 113 | return mount_point; | ||
| 114 | } | ||
| 115 | |||
| 116 | } // namespace FileSys | 37 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 059045245..c84c6948e 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | 8 | ||
| 9 | #include "core/file_sys/archive_backend.h" | 9 | #include "core/file_sys/disk_archive.h" |
| 10 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 11 | 11 | ||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -15,10 +15,9 @@ | |||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | /// File system interface to the SDMC archive | 17 | /// File system interface to the SDMC archive |
| 18 | class Archive_SDMC final : public ArchiveBackend { | 18 | class Archive_SDMC final : public DiskArchive { |
| 19 | public: | 19 | public: |
| 20 | Archive_SDMC(const std::string& mount_point); | 20 | Archive_SDMC(const std::string& mount_point); |
| 21 | ~Archive_SDMC() override; | ||
| 22 | 21 | ||
| 23 | /** | 22 | /** |
| 24 | * Initialize the archive. | 23 | * Initialize the archive. |
| @@ -27,67 +26,6 @@ public: | |||
| 27 | bool Initialize(); | 26 | bool Initialize(); |
| 28 | 27 | ||
| 29 | std::string GetName() const override { return "SDMC"; } | 28 | std::string GetName() const override { return "SDMC"; } |
| 30 | |||
| 31 | /** | ||
| 32 | * Open a file specified by its path, using the specified mode | ||
| 33 | * @param path Path relative to the archive | ||
| 34 | * @param mode Mode to open the file with | ||
| 35 | * @return Opened file, or nullptr | ||
| 36 | */ | ||
| 37 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Delete a file specified by its path | ||
| 41 | * @param path Path relative to the archive | ||
| 42 | * @return Whether the file could be deleted | ||
| 43 | */ | ||
| 44 | bool DeleteFile(const FileSys::Path& path) const override; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Rename a File specified by its path | ||
| 48 | * @param src_path Source path relative to the archive | ||
| 49 | * @param dest_path Destination path relative to the archive | ||
| 50 | * @return Whether rename succeeded | ||
| 51 | */ | ||
| 52 | bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Delete a directory specified by its path | ||
| 56 | * @param path Path relative to the archive | ||
| 57 | * @return Whether the directory could be deleted | ||
| 58 | */ | ||
| 59 | bool DeleteDirectory(const FileSys::Path& path) const override; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Create a directory specified by its path | ||
| 63 | * @param path Path relative to the archive | ||
| 64 | * @return Whether the directory could be created | ||
| 65 | */ | ||
| 66 | bool CreateDirectory(const Path& path) const override; | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Rename a Directory specified by its path | ||
| 70 | * @param src_path Source path relative to the archive | ||
| 71 | * @param dest_path Destination path relative to the archive | ||
| 72 | * @return Whether rename succeeded | ||
| 73 | */ | ||
| 74 | bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Open a directory specified by its path | ||
| 78 | * @param path Path relative to the archive | ||
| 79 | * @return Opened directory, or nullptr | ||
| 80 | */ | ||
| 81 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Getter for the path used for this Archive | ||
| 85 | * @return Mount point of that passthrough archive | ||
| 86 | */ | ||
| 87 | std::string GetMountPoint() const; | ||
| 88 | |||
| 89 | private: | ||
| 90 | std::string mount_point; | ||
| 91 | }; | 29 | }; |
| 92 | 30 | ||
| 93 | } // namespace FileSys | 31 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp new file mode 100644 index 000000000..dc2c23b41 --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.cpp | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/archive_systemsavedata.h" | ||
| 11 | #include "core/file_sys/disk_archive.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point) | ||
| 20 | : DiskArchive(mount_point) { | ||
| 21 | LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str()); | ||
| 22 | } | ||
| 23 | |||
| 24 | bool Archive_SystemSaveData::Initialize() { | ||
| 25 | if (!FileUtil::CreateFullPath(mount_point)) { | ||
| 26 | LOG_ERROR(Service_FS, "Unable to create SystemSaveData path."); | ||
| 27 | return false; | ||
| 28 | } | ||
| 29 | |||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h new file mode 100644 index 000000000..360ed1e13 --- /dev/null +++ b/src/core/file_sys/archive_systemsavedata.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/disk_archive.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // FileSys namespace | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /// File system interface to the SystemSaveData archive | ||
| 18 | /// TODO(Subv): This archive should point to a location in the NAND, | ||
| 19 | /// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High> | ||
| 20 | class Archive_SystemSaveData final : public DiskArchive { | ||
| 21 | public: | ||
| 22 | Archive_SystemSaveData(const std::string& mount_point); | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Initialize the archive. | ||
| 26 | * @return true if it initialized successfully | ||
| 27 | */ | ||
| 28 | bool Initialize(); | ||
| 29 | |||
| 30 | std::string GetName() const override { return "SystemSaveData"; } | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/directory_sdmc.cpp b/src/core/file_sys/directory_sdmc.cpp deleted file mode 100644 index 519787641..000000000 --- a/src/core/file_sys/directory_sdmc.cpp +++ /dev/null | |||
| @@ -1,88 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/directory_sdmc.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | 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 | ||
| 20 | // the root directory we set while opening the archive. | ||
| 21 | // For example, opening /../../usr/bin can give the emulated program your installed programs. | ||
| 22 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 23 | |||
| 24 | } | ||
| 25 | |||
| 26 | Directory_SDMC::~Directory_SDMC() { | ||
| 27 | Close(); | ||
| 28 | } | ||
| 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 | |||
| 38 | /** | ||
| 39 | * List files contained in the directory | ||
| 40 | * @param count Number of entries to return at once in entries | ||
| 41 | * @param entries Buffer to read data into | ||
| 42 | * @return Number of entries listed | ||
| 43 | */ | ||
| 44 | u32 Directory_SDMC::Read(const u32 count, Entry* entries) { | ||
| 45 | u32 entries_read = 0; | ||
| 46 | |||
| 47 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 48 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 49 | const std::string& filename = file.virtualName; | ||
| 50 | Entry& entry = entries[entries_read]; | ||
| 51 | |||
| 52 | LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); | ||
| 53 | |||
| 54 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 55 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 56 | entry.filename[j] = filename[j]; | ||
| 57 | if (!filename[j]) | ||
| 58 | break; | ||
| 59 | } | ||
| 60 | |||
| 61 | FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); | ||
| 62 | |||
| 63 | entry.is_directory = file.isDirectory; | ||
| 64 | entry.is_hidden = (filename[0] == '.'); | ||
| 65 | entry.is_read_only = 0; | ||
| 66 | entry.file_size = file.size; | ||
| 67 | |||
| 68 | // We emulate a SD card where the archive bit has never been cleared, as it would be on | ||
| 69 | // most user SD cards. | ||
| 70 | // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a | ||
| 71 | // file bit. | ||
| 72 | entry.is_archive = !file.isDirectory; | ||
| 73 | |||
| 74 | ++entries_read; | ||
| 75 | ++children_iterator; | ||
| 76 | } | ||
| 77 | return entries_read; | ||
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Close the directory | ||
| 82 | * @return true if the directory closed correctly | ||
| 83 | */ | ||
| 84 | bool Directory_SDMC::Close() const { | ||
| 85 | return true; | ||
| 86 | } | ||
| 87 | |||
| 88 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/directory_sdmc.h b/src/core/file_sys/directory_sdmc.h deleted file mode 100644 index 407a256ef..000000000 --- a/src/core/file_sys/directory_sdmc.h +++ /dev/null | |||
| @@ -1,55 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/directory_backend.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | #include "core/loader/loader.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class Directory_SDMC final : public DirectoryBackend { | ||
| 20 | public: | ||
| 21 | Directory_SDMC(); | ||
| 22 | Directory_SDMC(const Archive_SDMC* archive, const Path& path); | ||
| 23 | ~Directory_SDMC() override; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Open the directory | ||
| 27 | * @return true if the directory opened correctly | ||
| 28 | */ | ||
| 29 | bool Open() override; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * List files contained in the directory | ||
| 33 | * @param count Number of entries to return at once in entries | ||
| 34 | * @param entries Buffer to read data into | ||
| 35 | * @return Number of entries listed | ||
| 36 | */ | ||
| 37 | u32 Read(const u32 count, Entry* entries) override; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Close the directory | ||
| 41 | * @return true if the directory closed correctly | ||
| 42 | */ | ||
| 43 | bool Close() const override; | ||
| 44 | |||
| 45 | private: | ||
| 46 | std::string path; | ||
| 47 | u32 total_entries_in_directory; | ||
| 48 | FileUtil::FSTEntry directory; | ||
| 49 | |||
| 50 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 51 | // from the next one. This iterator will always point to the next unread entry. | ||
| 52 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 53 | }; | ||
| 54 | |||
| 55 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp new file mode 100644 index 000000000..eabf58057 --- /dev/null +++ b/src/core/file_sys/disk_archive.cpp | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/disk_archive.h" | ||
| 11 | #include "core/settings.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const { | ||
| 19 | LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); | ||
| 20 | DiskFile* file = new DiskFile(this, path, mode); | ||
| 21 | if (!file->Open()) | ||
| 22 | return nullptr; | ||
| 23 | return std::unique_ptr<FileBackend>(file); | ||
| 24 | } | ||
| 25 | |||
| 26 | bool DiskArchive::DeleteFile(const FileSys::Path& path) const { | ||
| 27 | return FileUtil::Delete(GetMountPoint() + path.AsString()); | ||
| 28 | } | ||
| 29 | |||
| 30 | bool DiskArchive::RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 31 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 32 | } | ||
| 33 | |||
| 34 | bool DiskArchive::DeleteDirectory(const FileSys::Path& path) const { | ||
| 35 | return FileUtil::DeleteDir(GetMountPoint() + path.AsString()); | ||
| 36 | } | ||
| 37 | |||
| 38 | bool DiskArchive::CreateDirectory(const Path& path) const { | ||
| 39 | return FileUtil::CreateDir(GetMountPoint() + path.AsString()); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool DiskArchive::RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const { | ||
| 43 | return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString()); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { | ||
| 47 | LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); | ||
| 48 | DiskDirectory* directory = new DiskDirectory(this, path); | ||
| 49 | if (!directory->Open()) | ||
| 50 | return nullptr; | ||
| 51 | return std::unique_ptr<DirectoryBackend>(directory); | ||
| 52 | } | ||
| 53 | |||
| 54 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 55 | |||
| 56 | DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) { | ||
| 57 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 58 | // the root directory we set while opening the archive. | ||
| 59 | // For example, opening /../../etc/passwd can give the emulated program your users list. | ||
| 60 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 61 | this->mode.hex = mode.hex; | ||
| 62 | this->archive = archive; | ||
| 63 | } | ||
| 64 | |||
| 65 | bool DiskFile::Open() { | ||
| 66 | if (!mode.create_flag && !FileUtil::Exists(path)) { | ||
| 67 | LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str()); | ||
| 68 | return false; | ||
| 69 | } | ||
| 70 | |||
| 71 | std::string mode_string; | ||
| 72 | if (mode.create_flag) | ||
| 73 | mode_string = "w+"; | ||
| 74 | else if (mode.write_flag) | ||
| 75 | mode_string = "r+"; // Files opened with Write access can be read from | ||
| 76 | else if (mode.read_flag) | ||
| 77 | mode_string = "r"; | ||
| 78 | |||
| 79 | // Open the file in binary mode, to avoid problems with CR/LF on Windows systems | ||
| 80 | mode_string += "b"; | ||
| 81 | |||
| 82 | file = new FileUtil::IOFile(path, mode_string.c_str()); | ||
| 83 | return true; | ||
| 84 | } | ||
| 85 | |||
| 86 | size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 87 | file->Seek(offset, SEEK_SET); | ||
| 88 | return file->ReadBytes(buffer, length); | ||
| 89 | } | ||
| 90 | |||
| 91 | size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||
| 92 | file->Seek(offset, SEEK_SET); | ||
| 93 | size_t written = file->WriteBytes(buffer, length); | ||
| 94 | if (flush) | ||
| 95 | file->Flush(); | ||
| 96 | return written; | ||
| 97 | } | ||
| 98 | |||
| 99 | size_t DiskFile::GetSize() const { | ||
| 100 | return static_cast<size_t>(file->GetSize()); | ||
| 101 | } | ||
| 102 | |||
| 103 | bool DiskFile::SetSize(const u64 size) const { | ||
| 104 | file->Resize(size); | ||
| 105 | file->Flush(); | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | |||
| 109 | bool DiskFile::Close() const { | ||
| 110 | return file->Close(); | ||
| 111 | } | ||
| 112 | |||
| 113 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 114 | |||
| 115 | DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) { | ||
| 116 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 117 | // the root directory we set while opening the archive. | ||
| 118 | // For example, opening /../../usr/bin can give the emulated program your installed programs. | ||
| 119 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 120 | this->archive = archive; | ||
| 121 | } | ||
| 122 | |||
| 123 | bool DiskDirectory::Open() { | ||
| 124 | if (!FileUtil::IsDirectory(path)) | ||
| 125 | return false; | ||
| 126 | FileUtil::ScanDirectoryTree(path, directory); | ||
| 127 | children_iterator = directory.children.begin(); | ||
| 128 | return true; | ||
| 129 | } | ||
| 130 | |||
| 131 | u32 DiskDirectory::Read(const u32 count, Entry* entries) { | ||
| 132 | u32 entries_read = 0; | ||
| 133 | |||
| 134 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 135 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 136 | const std::string& filename = file.virtualName; | ||
| 137 | Entry& entry = entries[entries_read]; | ||
| 138 | |||
| 139 | LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory); | ||
| 140 | |||
| 141 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 142 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 143 | entry.filename[j] = filename[j]; | ||
| 144 | if (!filename[j]) | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | |||
| 148 | FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); | ||
| 149 | |||
| 150 | entry.is_directory = file.isDirectory; | ||
| 151 | entry.is_hidden = (filename[0] == '.'); | ||
| 152 | entry.is_read_only = 0; | ||
| 153 | entry.file_size = file.size; | ||
| 154 | |||
| 155 | // We emulate a SD card where the archive bit has never been cleared, as it would be on | ||
| 156 | // most user SD cards. | ||
| 157 | // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a | ||
| 158 | // file bit. | ||
| 159 | entry.is_archive = !file.isDirectory; | ||
| 160 | |||
| 161 | ++entries_read; | ||
| 162 | ++children_iterator; | ||
| 163 | } | ||
| 164 | return entries_read; | ||
| 165 | } | ||
| 166 | |||
| 167 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h new file mode 100644 index 000000000..778c83953 --- /dev/null +++ b/src/core/file_sys/disk_archive.h | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/file_sys/archive_backend.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // FileSys namespace | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /** | ||
| 18 | * Helper which implements a backend accessing the host machine's filesystem. | ||
| 19 | * This should be subclassed by concrete archive types, which will provide the | ||
| 20 | * base directory on the host filesystem and override any required functionality. | ||
| 21 | */ | ||
| 22 | class DiskArchive : public ArchiveBackend { | ||
| 23 | public: | ||
| 24 | DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} | ||
| 25 | |||
| 26 | virtual std::string GetName() const = 0; | ||
| 27 | std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override; | ||
| 28 | bool DeleteFile(const FileSys::Path& path) const override; | ||
| 29 | bool RenameFile(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 30 | bool DeleteDirectory(const FileSys::Path& path) const override; | ||
| 31 | bool CreateDirectory(const Path& path) const override; | ||
| 32 | bool RenameDirectory(const FileSys::Path& src_path, const FileSys::Path& dest_path) const override; | ||
| 33 | std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Getter for the path used for this Archive | ||
| 37 | * @return Mount point of that passthrough archive | ||
| 38 | */ | ||
| 39 | const std::string& GetMountPoint() const { | ||
| 40 | return mount_point; | ||
| 41 | } | ||
| 42 | |||
| 43 | protected: | ||
| 44 | std::string mount_point; | ||
| 45 | }; | ||
| 46 | |||
| 47 | class DiskFile : public FileBackend { | ||
| 48 | public: | ||
| 49 | DiskFile(); | ||
| 50 | DiskFile(const DiskArchive* archive, const Path& path, const Mode mode); | ||
| 51 | |||
| 52 | ~DiskFile() override { | ||
| 53 | Close(); | ||
| 54 | } | ||
| 55 | |||
| 56 | bool Open() override; | ||
| 57 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 58 | size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | ||
| 59 | size_t GetSize() const override; | ||
| 60 | bool SetSize(const u64 size) const override; | ||
| 61 | bool Close() const override; | ||
| 62 | |||
| 63 | void Flush() const override { | ||
| 64 | file->Flush(); | ||
| 65 | } | ||
| 66 | |||
| 67 | protected: | ||
| 68 | const DiskArchive* archive; | ||
| 69 | std::string path; | ||
| 70 | Mode mode; | ||
| 71 | FileUtil::IOFile* file; | ||
| 72 | }; | ||
| 73 | |||
| 74 | class DiskDirectory : public DirectoryBackend { | ||
| 75 | public: | ||
| 76 | DiskDirectory(); | ||
| 77 | DiskDirectory(const DiskArchive* archive, const Path& path); | ||
| 78 | |||
| 79 | ~DiskDirectory() override { | ||
| 80 | Close(); | ||
| 81 | } | ||
| 82 | |||
| 83 | bool Open() override; | ||
| 84 | u32 Read(const u32 count, Entry* entries) override; | ||
| 85 | |||
| 86 | bool Close() const override { | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | |||
| 90 | protected: | ||
| 91 | const DiskArchive* archive; | ||
| 92 | std::string path; | ||
| 93 | u32 total_entries_in_directory; | ||
| 94 | FileUtil::FSTEntry directory; | ||
| 95 | |||
| 96 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 97 | // from the next one. This iterator will always point to the next unread entry. | ||
| 98 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 99 | }; | ||
| 100 | |||
| 101 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index 1b81d5fe9..539ec7314 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h | |||
| @@ -61,6 +61,11 @@ public: | |||
| 61 | * @return true if the file closed correctly | 61 | * @return true if the file closed correctly |
| 62 | */ | 62 | */ |
| 63 | virtual bool Close() const = 0; | 63 | virtual bool Close() const = 0; |
| 64 | |||
| 65 | /** | ||
| 66 | * Flushes the file | ||
| 67 | */ | ||
| 68 | virtual void Flush() const = 0; | ||
| 64 | }; | 69 | }; |
| 65 | 70 | ||
| 66 | } // namespace FileSys | 71 | } // namespace FileSys |
diff --git a/src/core/file_sys/file_romfs.h b/src/core/file_sys/file_romfs.h index 09fa2e7e3..32fa6b6d3 100644 --- a/src/core/file_sys/file_romfs.h +++ b/src/core/file_sys/file_romfs.h | |||
| @@ -64,6 +64,8 @@ public: | |||
| 64 | */ | 64 | */ |
| 65 | bool Close() const override; | 65 | bool Close() const override; |
| 66 | 66 | ||
| 67 | void Flush() const override { } | ||
| 68 | |||
| 67 | private: | 69 | private: |
| 68 | const Archive_RomFS* archive; | 70 | const Archive_RomFS* archive; |
| 69 | }; | 71 | }; |
diff --git a/src/core/file_sys/file_sdmc.cpp b/src/core/file_sys/file_sdmc.cpp deleted file mode 100644 index 46c29900b..000000000 --- a/src/core/file_sys/file_sdmc.cpp +++ /dev/null | |||
| @@ -1,110 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sys/stat.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/file_sdmc.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | File_SDMC::File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode) { | ||
| 19 | // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass | ||
| 20 | // the root directory we set while opening the archive. | ||
| 21 | // For example, opening /../../etc/passwd can give the emulated program your users list. | ||
| 22 | this->path = archive->GetMountPoint() + path.AsString(); | ||
| 23 | this->mode.hex = mode.hex; | ||
| 24 | } | ||
| 25 | |||
| 26 | File_SDMC::~File_SDMC() { | ||
| 27 | Close(); | ||
| 28 | } | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Open the file | ||
| 32 | * @return true if the file opened correctly | ||
| 33 | */ | ||
| 34 | bool File_SDMC::Open() { | ||
| 35 | if (!mode.create_flag && !FileUtil::Exists(path)) { | ||
| 36 | LOG_ERROR(Service_FS, "Non-existing file %s can’t be open without mode create.", path.c_str()); | ||
| 37 | return false; | ||
| 38 | } | ||
| 39 | |||
| 40 | std::string mode_string; | ||
| 41 | if (mode.create_flag) | ||
| 42 | mode_string = "w+"; | ||
| 43 | else if (mode.write_flag) | ||
| 44 | mode_string = "r+"; // Files opened with Write access can be read from | ||
| 45 | else if (mode.read_flag) | ||
| 46 | mode_string = "r"; | ||
| 47 | |||
| 48 | // Open the file in binary mode, to avoid problems with CR/LF on Windows systems | ||
| 49 | mode_string += "b"; | ||
| 50 | |||
| 51 | file = new FileUtil::IOFile(path, mode_string.c_str()); | ||
| 52 | return true; | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Read data from the file | ||
| 57 | * @param offset Offset in bytes to start reading data from | ||
| 58 | * @param length Length in bytes of data to read from file | ||
| 59 | * @param buffer Buffer to read data into | ||
| 60 | * @return Number of bytes read | ||
| 61 | */ | ||
| 62 | size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const { | ||
| 63 | file->Seek(offset, SEEK_SET); | ||
| 64 | return file->ReadBytes(buffer, length); | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Write data to the file | ||
| 69 | * @param offset Offset in bytes to start writing data to | ||
| 70 | * @param length Length in bytes of data to write to file | ||
| 71 | * @param flush The flush parameters (0 == do not flush) | ||
| 72 | * @param buffer Buffer to read data from | ||
| 73 | * @return Number of bytes written | ||
| 74 | */ | ||
| 75 | size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const { | ||
| 76 | file->Seek(offset, SEEK_SET); | ||
| 77 | size_t written = file->WriteBytes(buffer, length); | ||
| 78 | if (flush) | ||
| 79 | file->Flush(); | ||
| 80 | return written; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Get the size of the file in bytes | ||
| 85 | * @return Size of the file in bytes | ||
| 86 | */ | ||
| 87 | size_t File_SDMC::GetSize() const { | ||
| 88 | return static_cast<size_t>(file->GetSize()); | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Set the size of the file in bytes | ||
| 93 | * @param size New size of the file | ||
| 94 | * @return true if successful | ||
| 95 | */ | ||
| 96 | bool File_SDMC::SetSize(const u64 size) const { | ||
| 97 | file->Resize(size); | ||
| 98 | file->Flush(); | ||
| 99 | return true; | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Close the file | ||
| 104 | * @return true if the file closed correctly | ||
| 105 | */ | ||
| 106 | bool File_SDMC::Close() const { | ||
| 107 | return file->Close(); | ||
| 108 | } | ||
| 109 | |||
| 110 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/file_sdmc.h b/src/core/file_sys/file_sdmc.h deleted file mode 100644 index e01548598..000000000 --- a/src/core/file_sys/file_sdmc.h +++ /dev/null | |||
| @@ -1,75 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/file_util.h" | ||
| 9 | |||
| 10 | #include "core/file_sys/file_backend.h" | ||
| 11 | #include "core/file_sys/archive_sdmc.h" | ||
| 12 | #include "core/loader/loader.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class File_SDMC final : public FileBackend { | ||
| 20 | public: | ||
| 21 | File_SDMC(); | ||
| 22 | File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode); | ||
| 23 | ~File_SDMC() override; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Open the file | ||
| 27 | * @return true if the file opened correctly | ||
| 28 | */ | ||
| 29 | bool Open() override; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Read data from the file | ||
| 33 | * @param offset Offset in bytes to start reading data from | ||
| 34 | * @param length Length in bytes of data to read from file | ||
| 35 | * @param buffer Buffer to read data into | ||
| 36 | * @return Number of bytes read | ||
| 37 | */ | ||
| 38 | size_t Read(const u64 offset, const u32 length, u8* buffer) const override; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Write data to the file | ||
| 42 | * @param offset Offset in bytes to start writing data to | ||
| 43 | * @param length Length in bytes of data to write to file | ||
| 44 | * @param flush The flush parameters (0 == do not flush) | ||
| 45 | * @param buffer Buffer to read data from | ||
| 46 | * @return Number of bytes written | ||
| 47 | */ | ||
| 48 | size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Get the size of the file in bytes | ||
| 52 | * @return Size of the file in bytes | ||
| 53 | */ | ||
| 54 | size_t GetSize() const override; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Set the size of the file in bytes | ||
| 58 | * @param size New size of the file | ||
| 59 | * @return true if successful | ||
| 60 | */ | ||
| 61 | bool SetSize(const u64 size) const override; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Close the file | ||
| 65 | * @return true if the file closed correctly | ||
| 66 | */ | ||
| 67 | bool Close() const override; | ||
| 68 | |||
| 69 | private: | ||
| 70 | std::string path; | ||
| 71 | Mode mode; | ||
| 72 | FileUtil::IOFile* file; | ||
| 73 | }; | ||
| 74 | |||
| 75 | } // namespace FileSys | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 929422b36..6a690e915 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -14,6 +14,7 @@ namespace Kernel { | |||
| 14 | 14 | ||
| 15 | Handle g_main_thread = 0; | 15 | Handle g_main_thread = 0; |
| 16 | ObjectPool g_object_pool; | 16 | ObjectPool g_object_pool; |
| 17 | u64 g_program_id = 0; | ||
| 17 | 18 | ||
| 18 | ObjectPool::ObjectPool() { | 19 | ObjectPool::ObjectPool() { |
| 19 | next_id = INITIAL_NEXT_ID; | 20 | next_id = INITIAL_NEXT_ID; |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 861a8e69a..683fffeee 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -163,6 +163,12 @@ private: | |||
| 163 | extern ObjectPool g_object_pool; | 163 | extern ObjectPool g_object_pool; |
| 164 | extern Handle g_main_thread; | 164 | extern Handle g_main_thread; |
| 165 | 165 | ||
| 166 | /// The ID code of the currently running game | ||
| 167 | /// TODO(Subv): This variable should not be here, | ||
| 168 | /// we need a way to store information about the currently loaded application | ||
| 169 | /// for later query during runtime, maybe using the LDR service? | ||
| 170 | extern u64 g_program_id; | ||
| 171 | |||
| 166 | /// Initialize the kernel | 172 | /// Initialize the kernel |
| 167 | void Init(); | 173 | void Init(); |
| 168 | 174 | ||
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6f56da8a9..f955d1957 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -20,8 +20,8 @@ public: | |||
| 20 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; } | 20 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; } |
| 21 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; } | 21 | Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; } |
| 22 | 22 | ||
| 23 | u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have | 23 | s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have |
| 24 | u32 available_count; ///< Number of free slots left in the semaphore | 24 | s32 available_count; ///< Number of free slots left in the semaphore |
| 25 | std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore | 25 | std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore |
| 26 | std::string name; ///< Name of semaphore (optional) | 26 | std::string name; ///< Name of semaphore (optional) |
| 27 | 27 | ||
| @@ -49,8 +49,8 @@ public: | |||
| 49 | 49 | ||
| 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 50 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 51 | 51 | ||
| 52 | ResultCode CreateSemaphore(Handle* handle, u32 initial_count, | 52 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, |
| 53 | u32 max_count, const std::string& name) { | 53 | s32 max_count, const std::string& name) { |
| 54 | 54 | ||
| 55 | if (initial_count > max_count) | 55 | if (initial_count > max_count) |
| 56 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, | 56 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, |
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index f0075fdb8..ad474b875 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -18,7 +18,7 @@ namespace Kernel { | |||
| 18 | * @param name Optional name of semaphore | 18 | * @param name Optional name of semaphore |
| 19 | * @return ResultCode of the error | 19 | * @return ResultCode of the error |
| 20 | */ | 20 | */ |
| 21 | ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown"); | 21 | ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown"); |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | * Releases a certain number of slots from a semaphore. | 24 | * Releases a certain number of slots from a semaphore. |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 304cf5b67..bb778ec26 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -12,11 +12,15 @@ namespace Kernel { | |||
| 12 | 12 | ||
| 13 | /// Permissions for mapped shared memory blocks | 13 | /// Permissions for mapped shared memory blocks |
| 14 | enum class MemoryPermission : u32 { | 14 | enum class MemoryPermission : u32 { |
| 15 | None = 0, | 15 | None = 0, |
| 16 | Read = (1u << 0), | 16 | Read = (1u << 0), |
| 17 | Write = (1u << 1), | 17 | Write = (1u << 1), |
| 18 | ReadWrite = (Read | Write), | 18 | ReadWrite = (Read | Write), |
| 19 | DontCare = (1u << 28) | 19 | Execute = (1u << 2), |
| 20 | ReadExecute = (Read | Execute), | ||
| 21 | WriteExecute = (Write | Execute), | ||
| 22 | ReadWriteExecute = (Read | Write | Execute), | ||
| 23 | DontCare = (1u << 28) | ||
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | /** | 26 | /** |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 15c4a2677..14d2be4a2 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -17,6 +17,8 @@ | |||
| 17 | /// Detailed description of the error. This listing is likely incomplete. | 17 | /// Detailed description of the error. This listing is likely incomplete. |
| 18 | enum class ErrorDescription : u32 { | 18 | enum class ErrorDescription : u32 { |
| 19 | Success = 0, | 19 | Success = 0, |
| 20 | FS_NotFound = 100, | ||
| 21 | FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive | ||
| 20 | InvalidSection = 1000, | 22 | InvalidSection = 1000, |
| 21 | TooLarge = 1001, | 23 | TooLarge = 1001, |
| 22 | NotAuthorized = 1002, | 24 | NotAuthorized = 1002, |
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index caf82d556..510d7320c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp | |||
| @@ -7,8 +7,10 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 10 | #include "common/make_unique.h" | ||
| 10 | #include "common/math_util.h" | 11 | #include "common/math_util.h" |
| 11 | 12 | ||
| 13 | #include "core/file_sys/archive_savedata.h" | ||
| 12 | #include "core/file_sys/archive_backend.h" | 14 | #include "core/file_sys/archive_backend.h" |
| 13 | #include "core/file_sys/archive_sdmc.h" | 15 | #include "core/file_sys/archive_sdmc.h" |
| 14 | #include "core/file_sys/directory_backend.h" | 16 | #include "core/file_sys/directory_backend.h" |
| @@ -135,6 +137,13 @@ public: | |||
| 135 | break; | 137 | break; |
| 136 | } | 138 | } |
| 137 | 139 | ||
| 140 | case FileCommand::Flush: | ||
| 141 | { | ||
| 142 | LOG_TRACE(Service_FS, "Flush"); | ||
| 143 | backend->Flush(); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | |||
| 138 | // Unknown command... | 147 | // Unknown command... |
| 139 | default: | 148 | default: |
| 140 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | 149 | LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); |
| @@ -220,9 +229,18 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) { | |||
| 220 | 229 | ||
| 221 | auto itr = id_code_map.find(id_code); | 230 | auto itr = id_code_map.find(id_code); |
| 222 | if (itr == id_code_map.end()) { | 231 | if (itr == id_code_map.end()) { |
| 232 | if (id_code == ArchiveIdCode::SaveData) { | ||
| 233 | // When a SaveData archive is created for the first time, it is not yet formatted | ||
| 234 | // and the save file/directory structure expected by the game has not yet been initialized. | ||
| 235 | // Returning the NotFormatted error code will signal the game to provision the SaveData archive | ||
| 236 | // with the files and folders that it expects. | ||
| 237 | // The FormatSaveData service call will create the SaveData archive when it is called. | ||
| 238 | return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, | ||
| 239 | ErrorSummary::InvalidState, ErrorLevel::Status); | ||
| 240 | } | ||
| 223 | // TODO: Verify error against hardware | 241 | // TODO: Verify error against hardware |
| 224 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | 242 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, |
| 225 | ErrorSummary::NotFound, ErrorLevel::Permanent); | 243 | ErrorSummary::NotFound, ErrorLevel::Permanent); |
| 226 | } | 244 | } |
| 227 | 245 | ||
| 228 | // This should never even happen in the first place with 64-bit handles, | 246 | // This should never even happen in the first place with 64-bit handles, |
| @@ -243,7 +261,7 @@ ResultCode CloseArchive(ArchiveHandle handle) { | |||
| 243 | // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in | 261 | // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in |
| 244 | // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 | 262 | // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22 |
| 245 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { | 263 | ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) { |
| 246 | auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code)); | 264 | auto result = id_code_map.emplace(id_code, Common::make_unique<Archive>(std::move(backend), id_code)); |
| 247 | 265 | ||
| 248 | bool inserted = result.second; | 266 | bool inserted = result.second; |
| 249 | _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); | 267 | _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code"); |
| @@ -260,11 +278,11 @@ ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSy | |||
| 260 | 278 | ||
| 261 | std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); | 279 | std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); |
| 262 | if (backend == nullptr) { | 280 | if (backend == nullptr) { |
| 263 | return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, | 281 | return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, |
| 264 | ErrorSummary::NotFound, ErrorLevel::Permanent); | 282 | ErrorSummary::NotFound, ErrorLevel::Status); |
| 265 | } | 283 | } |
| 266 | 284 | ||
| 267 | auto file = std::make_unique<File>(std::move(backend), path); | 285 | auto file = Common::make_unique<File>(std::move(backend), path); |
| 268 | Handle handle = Kernel::g_object_pool.Create(file.release()); | 286 | Handle handle = Kernel::g_object_pool.Create(file.release()); |
| 269 | return MakeResult<Handle>(handle); | 287 | return MakeResult<Handle>(handle); |
| 270 | } | 288 | } |
| @@ -361,11 +379,33 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F | |||
| 361 | ErrorSummary::NotFound, ErrorLevel::Permanent); | 379 | ErrorSummary::NotFound, ErrorLevel::Permanent); |
| 362 | } | 380 | } |
| 363 | 381 | ||
| 364 | auto directory = std::make_unique<Directory>(std::move(backend), path); | 382 | auto directory = Common::make_unique<Directory>(std::move(backend), path); |
| 365 | Handle handle = Kernel::g_object_pool.Create(directory.release()); | 383 | Handle handle = Kernel::g_object_pool.Create(directory.release()); |
| 366 | return MakeResult<Handle>(handle); | 384 | return MakeResult<Handle>(handle); |
| 367 | } | 385 | } |
| 368 | 386 | ||
| 387 | ResultCode FormatSaveData() { | ||
| 388 | // TODO(Subv): Actually wipe the savedata folder after creating or opening it | ||
| 389 | |||
| 390 | // Do not create the archive again if it already exists | ||
| 391 | if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end()) | ||
| 392 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code | ||
| 393 | |||
| 394 | // Create the SaveData archive | ||
| 395 | std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX); | ||
| 396 | auto savedata_archive = Common::make_unique<FileSys::Archive_SaveData>(savedata_directory, | ||
| 397 | Kernel::g_program_id); | ||
| 398 | |||
| 399 | if (savedata_archive->Initialize()) { | ||
| 400 | CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData); | ||
| 401 | return RESULT_SUCCESS; | ||
| 402 | } else { | ||
| 403 | LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s", | ||
| 404 | savedata_archive->GetMountPoint().c_str()); | ||
| 405 | return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 369 | /// Initialize archives | 409 | /// Initialize archives |
| 370 | void ArchiveInit() { | 410 | void ArchiveInit() { |
| 371 | next_handle = 1; | 411 | next_handle = 1; |
| @@ -375,11 +415,20 @@ void ArchiveInit() { | |||
| 375 | // archive type is SDMC, so it is the only one getting exposed. | 415 | // archive type is SDMC, so it is the only one getting exposed. |
| 376 | 416 | ||
| 377 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); | 417 | std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); |
| 378 | auto archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory); | 418 | auto sdmc_archive = Common::make_unique<FileSys::Archive_SDMC>(sdmc_directory); |
| 379 | if (archive->Initialize()) | 419 | if (sdmc_archive->Initialize()) |
| 380 | CreateArchive(std::move(archive), ArchiveIdCode::SDMC); | 420 | CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC); |
| 381 | else | 421 | else |
| 382 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); | 422 | LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); |
| 423 | |||
| 424 | std::string systemsavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX); | ||
| 425 | auto systemsavedata_archive = Common::make_unique<FileSys::Archive_SDMC>(systemsavedata_directory); | ||
| 426 | if (systemsavedata_archive->Initialize()) { | ||
| 427 | CreateArchive(std::move(systemsavedata_archive), ArchiveIdCode::SystemSaveData); | ||
| 428 | } else { | ||
| 429 | LOG_ERROR(Service_FS, "Can't instantiate SystemSaveData archive with path %s", | ||
| 430 | systemsavedata_directory.c_str()); | ||
| 431 | } | ||
| 383 | } | 432 | } |
| 384 | 433 | ||
| 385 | /// Shutdown archives | 434 | /// Shutdown archives |
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index a38de92e3..a128276b6 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h | |||
| @@ -109,6 +109,12 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons | |||
| 109 | */ | 109 | */ |
| 110 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); | 110 | ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); |
| 111 | 111 | ||
| 112 | /** | ||
| 113 | * Creates a blank SaveData archive. | ||
| 114 | * @return ResultCode 0 on success or the corresponding code on error | ||
| 115 | */ | ||
| 116 | ResultCode FormatSaveData(); | ||
| 117 | |||
| 112 | /// Initialize archives | 118 | /// Initialize archives |
| 113 | void ArchiveInit(); | 119 | void ArchiveInit(); |
| 114 | 120 | ||
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 0f75d5e3a..8b908d691 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp | |||
| @@ -3,11 +3,11 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common.h" | 5 | #include "common/common.h" |
| 6 | #include "common/file_util.h" | ||
| 6 | #include "common/scope_exit.h" | 7 | #include "common/scope_exit.h" |
| 7 | |||
| 8 | #include "common/string_util.h" | 8 | #include "common/string_util.h" |
| 9 | #include "core/hle/service/fs/archive.h" | ||
| 10 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 10 | #include "core/hle/service/fs/archive.h" | ||
| 11 | #include "core/hle/service/fs/fs_user.h" | 11 | #include "core/hle/service/fs/fs_user.h" |
| 12 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 13 | 13 | ||
| @@ -50,9 +50,7 @@ static void Initialize(Service::Interface* self) { | |||
| 50 | static void OpenFile(Service::Interface* self) { | 50 | static void OpenFile(Service::Interface* self) { |
| 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 51 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 52 | 52 | ||
| 53 | // TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to | 53 | ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); |
| 54 | // 3dmoo's or ctrulib's implementations. Triple check if it's really the case. | ||
| 55 | Handle archive_handle = static_cast<Handle>(cmd_buff[3]); | ||
| 56 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); | 54 | auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); |
| 57 | u32 filename_size = cmd_buff[5]; | 55 | u32 filename_size = cmd_buff[5]; |
| 58 | FileSys::Mode mode; mode.hex = cmd_buff[6]; | 56 | FileSys::Mode mode; mode.hex = cmd_buff[6]; |
| @@ -398,6 +396,65 @@ static void IsSdmcDetected(Service::Interface* self) { | |||
| 398 | LOG_DEBUG(Service_FS, "called"); | 396 | LOG_DEBUG(Service_FS, "called"); |
| 399 | } | 397 | } |
| 400 | 398 | ||
| 399 | /** | ||
| 400 | * FS_User::FormatSaveData service function, | ||
| 401 | * formats the SaveData specified by the input path. | ||
| 402 | * Inputs: | ||
| 403 | * 0 : 0x084C0242 | ||
| 404 | * 1 : Archive ID | ||
| 405 | * 2 : Archive low path type | ||
| 406 | * 3 : Archive low path size | ||
| 407 | * 10 : (LowPathSize << 14) | 2 | ||
| 408 | * 11 : Archive low path | ||
| 409 | * Outputs: | ||
| 410 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 411 | */ | ||
| 412 | static void FormatSaveData(Service::Interface* self) { | ||
| 413 | // TODO(Subv): Find out what the other inputs and outputs of this function are | ||
| 414 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 415 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 416 | |||
| 417 | auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]); | ||
| 418 | auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]); | ||
| 419 | u32 archivename_size = cmd_buff[3]; | ||
| 420 | u32 archivename_ptr = cmd_buff[11]; | ||
| 421 | FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); | ||
| 422 | |||
| 423 | LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); | ||
| 424 | |||
| 425 | if (archive_id != FS::ArchiveIdCode::SaveData) { | ||
| 426 | // TODO(Subv): What should happen if somebody attempts to format a different archive? | ||
| 427 | LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]); | ||
| 428 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 429 | return; | ||
| 430 | } | ||
| 431 | |||
| 432 | if (archive_path.GetType() != FileSys::LowPathType::Empty) { | ||
| 433 | // TODO(Subv): Implement formatting the SaveData of other games | ||
| 434 | LOG_ERROR(Service_FS, "archive LowPath type other than empty is currently unsupported"); | ||
| 435 | cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw; | ||
| 436 | return; | ||
| 437 | } | ||
| 438 | |||
| 439 | cmd_buff[1] = FormatSaveData().raw; | ||
| 440 | } | ||
| 441 | |||
| 442 | /** | ||
| 443 | * FS_User::FormatThisUserSaveData service function | ||
| 444 | * Inputs: | ||
| 445 | * 0: 0x080F0180 | ||
| 446 | * Outputs: | ||
| 447 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 448 | */ | ||
| 449 | static void FormatThisUserSaveData(Service::Interface* self) { | ||
| 450 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 451 | LOG_DEBUG(Service_FS, "(STUBBED)"); | ||
| 452 | |||
| 453 | // TODO(Subv): Find out what the inputs and outputs of this function are | ||
| 454 | |||
| 455 | cmd_buff[1] = FormatSaveData().raw; | ||
| 456 | } | ||
| 457 | |||
| 401 | const FSUserInterface::FunctionInfo FunctionTable[] = { | 458 | const FSUserInterface::FunctionInfo FunctionTable[] = { |
| 402 | {0x000100C6, nullptr, "Dummy1"}, | 459 | {0x000100C6, nullptr, "Dummy1"}, |
| 403 | {0x040100C4, nullptr, "Control"}, | 460 | {0x040100C4, nullptr, "Control"}, |
| @@ -415,7 +472,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { | |||
| 415 | {0x080C00C2, OpenArchive, "OpenArchive"}, | 472 | {0x080C00C2, OpenArchive, "OpenArchive"}, |
| 416 | {0x080D0144, nullptr, "ControlArchive"}, | 473 | {0x080D0144, nullptr, "ControlArchive"}, |
| 417 | {0x080E0080, CloseArchive, "CloseArchive"}, | 474 | {0x080E0080, CloseArchive, "CloseArchive"}, |
| 418 | {0x080F0180, nullptr, "FormatThisUserSaveData"}, | 475 | {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"}, |
| 419 | {0x08100200, nullptr, "CreateSystemSaveData"}, | 476 | {0x08100200, nullptr, "CreateSystemSaveData"}, |
| 420 | {0x08110040, nullptr, "DeleteSystemSaveData"}, | 477 | {0x08110040, nullptr, "DeleteSystemSaveData"}, |
| 421 | {0x08120080, nullptr, "GetFreeBytes"}, | 478 | {0x08120080, nullptr, "GetFreeBytes"}, |
| @@ -476,7 +533,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { | |||
| 476 | {0x08490040, nullptr, "GetArchiveResource"}, | 533 | {0x08490040, nullptr, "GetArchiveResource"}, |
| 477 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, | 534 | {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"}, |
| 478 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, | 535 | {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"}, |
| 479 | {0x084C0242, nullptr, "FormatSaveData"}, | 536 | {0x084C0242, FormatSaveData, "FormatSaveData"}, |
| 480 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, | 537 | {0x084D0102, nullptr, "GetLegacySubBannerData"}, |
| 481 | {0x084E0342, nullptr, "UpdateSha256Context"}, | 538 | {0x084E0342, nullptr, "UpdateSha256Context"}, |
| 482 | {0x084F0102, nullptr, "ReadSpecialFile"}, | 539 | {0x084F0102, nullptr, "ReadSpecialFile"}, |
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index db8027142..8c9ad2712 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -145,6 +145,30 @@ static void SetBufferSwap(Service::Interface* self) { | |||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | /** | 147 | /** |
| 148 | * GSP_GPU::FlushDataCache service function | ||
| 149 | * | ||
| 150 | * This Function is a no-op, We aren't emulating the CPU cache any time soon. | ||
| 151 | * | ||
| 152 | * Inputs: | ||
| 153 | * 1 : Address | ||
| 154 | * 2 : Size | ||
| 155 | * 3 : Value 0, some descriptor for the KProcess Handle | ||
| 156 | * 4 : KProcess handle | ||
| 157 | * Outputs: | ||
| 158 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 159 | */ | ||
| 160 | static void FlushDataCache(Service::Interface* self) { | ||
| 161 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 162 | u32 address = cmd_buff[1]; | ||
| 163 | u32 size = cmd_buff[2]; | ||
| 164 | u32 process = cmd_buff[4]; | ||
| 165 | |||
| 166 | // TODO(purpasmart96): Verify return header on HW | ||
| 167 | |||
| 168 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 148 | * GSP_GPU::RegisterInterruptRelayQueue service function | 172 | * GSP_GPU::RegisterInterruptRelayQueue service function |
| 149 | * Inputs: | 173 | * Inputs: |
| 150 | * 1 : "Flags" field, purpose is unknown | 174 | * 1 : "Flags" field, purpose is unknown |
| @@ -335,7 +359,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 335 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, | 359 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, |
| 336 | {0x00060082, nullptr, "SetCommandList"}, | 360 | {0x00060082, nullptr, "SetCommandList"}, |
| 337 | {0x000700C2, nullptr, "RequestDma"}, | 361 | {0x000700C2, nullptr, "RequestDma"}, |
| 338 | {0x00080082, nullptr, "FlushDataCache"}, | 362 | {0x00080082, FlushDataCache, "FlushDataCache"}, |
| 339 | {0x00090082, nullptr, "InvalidateDataCache"}, | 363 | {0x00090082, nullptr, "InvalidateDataCache"}, |
| 340 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, | 364 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, |
| 341 | {0x000B0040, nullptr, "SetLcdForceBlack"}, | 365 | {0x000B0040, nullptr, "SetLcdForceBlack"}, |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 15cc240f4..47e9bf77e 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -64,6 +64,10 @@ static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other | |||
| 64 | case Kernel::MemoryPermission::Read: | 64 | case Kernel::MemoryPermission::Read: |
| 65 | case Kernel::MemoryPermission::Write: | 65 | case Kernel::MemoryPermission::Write: |
| 66 | case Kernel::MemoryPermission::ReadWrite: | 66 | case Kernel::MemoryPermission::ReadWrite: |
| 67 | case Kernel::MemoryPermission::Execute: | ||
| 68 | case Kernel::MemoryPermission::ReadExecute: | ||
| 69 | case Kernel::MemoryPermission::WriteExecute: | ||
| 70 | case Kernel::MemoryPermission::ReadWriteExecute: | ||
| 67 | case Kernel::MemoryPermission::DontCare: | 71 | case Kernel::MemoryPermission::DontCare: |
| 68 | Kernel::MapSharedMemory(handle, addr, permissions_type, | 72 | Kernel::MapSharedMemory(handle, addr, permissions_type, |
| 69 | static_cast<Kernel::MemoryPermission>(other_permissions)); | 73 | static_cast<Kernel::MemoryPermission>(other_permissions)); |
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 0437e5374..3d84fc5da 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp | |||
| @@ -223,9 +223,7 @@ int THREEDSXReader::Load3DSXFile(const std::string& filename, u32 base_addr) | |||
| 223 | LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); | 223 | LOG_INFO(Loader, "Loading 3DSX file %s...", filename.c_str()); |
| 224 | FileUtil::IOFile file(filename, "rb"); | 224 | FileUtil::IOFile file(filename, "rb"); |
| 225 | if (file.IsOpen()) { | 225 | if (file.IsOpen()) { |
| 226 | 226 | THREEDSXReader::Load3DSXFile(filename, 0x00100000); | |
| 227 | THREEDSXReader reader; | ||
| 228 | reader.Load3DSXFile(filename, 0x00100000); | ||
| 229 | Kernel::LoadExec(0x00100000); | 227 | Kernel::LoadExec(0x00100000); |
| 230 | } else { | 228 | } else { |
| 231 | return ResultStatus::Error; | 229 | return ResultStatus::Error; |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 463dacca3..b3b58da72 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -2,7 +2,9 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <string> |
| 6 | |||
| 7 | #include "common/make_unique.h" | ||
| 6 | 8 | ||
| 7 | #include "core/file_sys/archive_romfs.h" | 9 | #include "core/file_sys/archive_romfs.h" |
| 8 | #include "core/loader/3dsx.h" | 10 | #include "core/loader/3dsx.h" |
| @@ -74,7 +76,8 @@ ResultStatus LoadFile(const std::string& filename) { | |||
| 74 | 76 | ||
| 75 | // Load application and RomFS | 77 | // Load application and RomFS |
| 76 | if (ResultStatus::Success == app_loader.Load()) { | 78 | if (ResultStatus::Success == app_loader.Load()) { |
| 77 | Service::FS::CreateArchive(std::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); | 79 | Kernel::g_program_id = app_loader.GetProgramId(); |
| 80 | Service::FS::CreateArchive(Common::make_unique<FileSys::Archive_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS); | ||
| 78 | return ResultStatus::Success; | 81 | return ResultStatus::Success; |
| 79 | } | 82 | } |
| 80 | break; | 83 | break; |
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index ba9ba00c0..4d23656ec 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp | |||
| @@ -315,4 +315,8 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const { | |||
| 315 | return ResultStatus::Error; | 315 | return ResultStatus::Error; |
| 316 | } | 316 | } |
| 317 | 317 | ||
| 318 | u64 AppLoader_NCCH::GetProgramId() const { | ||
| 319 | return *reinterpret_cast<u64 const*>(&ncch_header.program_id[0]); | ||
| 320 | } | ||
| 321 | |||
| 318 | } // namespace Loader | 322 | } // namespace Loader |
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 03116add8..2fe2a7d82 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h | |||
| @@ -191,6 +191,12 @@ public: | |||
| 191 | */ | 191 | */ |
| 192 | ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; | 192 | ResultStatus ReadRomFS(std::vector<u8>& buffer) const override; |
| 193 | 193 | ||
| 194 | /* | ||
| 195 | * Gets the program id from the NCCH header | ||
| 196 | * @return u64 Program id | ||
| 197 | */ | ||
| 198 | u64 GetProgramId() const; | ||
| 199 | |||
| 194 | private: | 200 | private: |
| 195 | 201 | ||
| 196 | /** | 202 | /** |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index b74cd3261..9b8ecf8e3 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -56,10 +56,11 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 56 | g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr); | 56 | g_debug_context->OnEvent(DebugContext::Event::IncomingPrimitiveBatch, nullptr); |
| 57 | 57 | ||
| 58 | const auto& attribute_config = registers.vertex_attributes; | 58 | const auto& attribute_config = registers.vertex_attributes; |
| 59 | const u8* const base_address = Memory::GetPointer(attribute_config.GetBaseAddress()); | 59 | const u32 base_address = attribute_config.GetPhysicalBaseAddress(); |
| 60 | 60 | ||
| 61 | // Information about internal vertex attributes | 61 | // Information about internal vertex attributes |
| 62 | const u8* vertex_attribute_sources[16]; | 62 | u32 vertex_attribute_sources[16]; |
| 63 | std::fill(vertex_attribute_sources, &vertex_attribute_sources[16], 0xdeadbeef); | ||
| 63 | u32 vertex_attribute_strides[16]; | 64 | u32 vertex_attribute_strides[16]; |
| 64 | u32 vertex_attribute_formats[16]; | 65 | u32 vertex_attribute_formats[16]; |
| 65 | u32 vertex_attribute_elements[16]; | 66 | u32 vertex_attribute_elements[16]; |
| @@ -69,7 +70,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 69 | for (int loader = 0; loader < 12; ++loader) { | 70 | for (int loader = 0; loader < 12; ++loader) { |
| 70 | const auto& loader_config = attribute_config.attribute_loaders[loader]; | 71 | const auto& loader_config = attribute_config.attribute_loaders[loader]; |
| 71 | 72 | ||
| 72 | const u8* load_address = base_address + loader_config.data_offset; | 73 | u32 load_address = base_address + loader_config.data_offset; |
| 73 | 74 | ||
| 74 | // TODO: What happens if a loader overwrites a previous one's data? | 75 | // TODO: What happens if a loader overwrites a previous one's data? |
| 75 | for (unsigned component = 0; component < loader_config.component_count; ++component) { | 76 | for (unsigned component = 0; component < loader_config.component_count; ++component) { |
| @@ -87,7 +88,7 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 87 | bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed)); | 88 | bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed)); |
| 88 | 89 | ||
| 89 | const auto& index_info = registers.index_array; | 90 | const auto& index_info = registers.index_array; |
| 90 | const u8* index_address_8 = (u8*)base_address + index_info.offset; | 91 | const u8* index_address_8 = Memory::GetPointer(PAddrToVAddr(base_address + index_info.offset)); |
| 91 | const u16* index_address_16 = (u16*)index_address_8; | 92 | const u16* index_address_16 = (u16*)index_address_8; |
| 92 | bool index_u16 = (bool)index_info.format; | 93 | bool index_u16 = (bool)index_info.format; |
| 93 | 94 | ||
| @@ -108,7 +109,14 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 108 | 109 | ||
| 109 | for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) { | 110 | for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) { |
| 110 | for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { | 111 | for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) { |
| 111 | const u8* srcdata = vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i]; | 112 | const u8* srcdata = Memory::GetPointer(PAddrToVAddr(vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i])); |
| 113 | |||
| 114 | // TODO(neobrain): Ocarina of Time 3D has GetNumTotalAttributes return 8, | ||
| 115 | // yet only provides 2 valid source data addresses. Need to figure out | ||
| 116 | // what's wrong there, until then we just continue when address lookup fails | ||
| 117 | if (srcdata == nullptr) | ||
| 118 | continue; | ||
| 119 | |||
| 112 | const float srcval = (vertex_attribute_formats[i] == 0) ? *(s8*)srcdata : | 120 | const float srcval = (vertex_attribute_formats[i] == 0) ? *(s8*)srcdata : |
| 113 | (vertex_attribute_formats[i] == 1) ? *(u8*)srcdata : | 121 | (vertex_attribute_formats[i] == 1) ? *(u8*)srcdata : |
| 114 | (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : | 122 | (vertex_attribute_formats[i] == 2) ? *(s16*)srcdata : |
| @@ -116,13 +124,16 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 116 | input.attr[i][comp] = float24::FromFloat32(srcval); | 124 | input.attr[i][comp] = float24::FromFloat32(srcval); |
| 117 | LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", | 125 | LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f", |
| 118 | comp, i, vertex, index, | 126 | comp, i, vertex, index, |
| 119 | attribute_config.GetBaseAddress(), | 127 | attribute_config.GetPhysicalBaseAddress(), |
| 120 | vertex_attribute_sources[i] - base_address, | 128 | vertex_attribute_sources[i] - base_address, |
| 121 | srcdata - vertex_attribute_sources[i], | 129 | vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i], |
| 122 | input.attr[i][comp].ToFloat32()); | 130 | input.attr[i][comp].ToFloat32()); |
| 123 | } | 131 | } |
| 124 | } | 132 | } |
| 125 | 133 | ||
| 134 | if (g_debug_context) | ||
| 135 | g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input); | ||
| 136 | |||
| 126 | // NOTE: When dumping geometry, we simply assume that the first input attribute | 137 | // NOTE: When dumping geometry, we simply assume that the first input attribute |
| 127 | // corresponds to the position for now. | 138 | // corresponds to the position for now. |
| 128 | DebugUtils::GeometryDumper::Vertex dumped_vertex = { | 139 | DebugUtils::GeometryDumper::Vertex dumped_vertex = { |
| @@ -151,6 +162,12 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 151 | break; | 162 | break; |
| 152 | } | 163 | } |
| 153 | 164 | ||
| 165 | case PICA_REG_INDEX(vs_bool_uniforms): | ||
| 166 | for (unsigned i = 0; i < 16; ++i) | ||
| 167 | VertexShader::GetBoolUniform(i) = (registers.vs_bool_uniforms.Value() & (1 << i)); | ||
| 168 | |||
| 169 | break; | ||
| 170 | |||
| 154 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[0], 0x2c1): | 171 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[0], 0x2c1): |
| 155 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[1], 0x2c2): | 172 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[1], 0x2c2): |
| 156 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[2], 0x2c3): | 173 | case PICA_REG_INDEX_WORKAROUND(vs_uniform_setup.set_value[2], 0x2c3): |
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 1a20f19ec..328386b7e 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include <png.h> | 14 | #include <png.h> |
| 15 | #endif | 15 | #endif |
| 16 | 16 | ||
| 17 | #include <nihstro/shader_binary.h> | ||
| 18 | |||
| 17 | #include "common/log.h" | 19 | #include "common/log.h" |
| 18 | #include "common/file_util.h" | 20 | #include "common/file_util.h" |
| 19 | 21 | ||
| @@ -22,6 +24,10 @@ | |||
| 22 | 24 | ||
| 23 | #include "debug_utils.h" | 25 | #include "debug_utils.h" |
| 24 | 26 | ||
| 27 | using nihstro::DVLBHeader; | ||
| 28 | using nihstro::DVLEHeader; | ||
| 29 | using nihstro::DVLPHeader; | ||
| 30 | |||
| 25 | namespace Pica { | 31 | namespace Pica { |
| 26 | 32 | ||
| 27 | void DebugContext::OnEvent(Event event, void* data) { | 33 | void DebugContext::OnEvent(Event event, void* data) { |
| @@ -98,65 +104,6 @@ void GeometryDumper::Dump() { | |||
| 98 | } | 104 | } |
| 99 | } | 105 | } |
| 100 | 106 | ||
| 101 | #pragma pack(1) | ||
| 102 | struct DVLBHeader { | ||
| 103 | enum : u32 { | ||
| 104 | MAGIC_WORD = 0x424C5644, // "DVLB" | ||
| 105 | }; | ||
| 106 | |||
| 107 | u32 magic_word; | ||
| 108 | u32 num_programs; | ||
| 109 | // u32 dvle_offset_table[]; | ||
| 110 | }; | ||
| 111 | static_assert(sizeof(DVLBHeader) == 0x8, "Incorrect structure size"); | ||
| 112 | |||
| 113 | struct DVLPHeader { | ||
| 114 | enum : u32 { | ||
| 115 | MAGIC_WORD = 0x504C5644, // "DVLP" | ||
| 116 | }; | ||
| 117 | |||
| 118 | u32 magic_word; | ||
| 119 | u32 version; | ||
| 120 | u32 binary_offset; // relative to DVLP start | ||
| 121 | u32 binary_size_words; | ||
| 122 | u32 swizzle_patterns_offset; | ||
| 123 | u32 swizzle_patterns_num_entries; | ||
| 124 | u32 unk2; | ||
| 125 | }; | ||
| 126 | static_assert(sizeof(DVLPHeader) == 0x1C, "Incorrect structure size"); | ||
| 127 | |||
| 128 | struct DVLEHeader { | ||
| 129 | enum : u32 { | ||
| 130 | MAGIC_WORD = 0x454c5644, // "DVLE" | ||
| 131 | }; | ||
| 132 | |||
| 133 | enum class ShaderType : u8 { | ||
| 134 | VERTEX = 0, | ||
| 135 | GEOMETRY = 1, | ||
| 136 | }; | ||
| 137 | |||
| 138 | u32 magic_word; | ||
| 139 | u16 pad1; | ||
| 140 | ShaderType type; | ||
| 141 | u8 pad2; | ||
| 142 | u32 main_offset_words; // offset within binary blob | ||
| 143 | u32 endmain_offset_words; | ||
| 144 | u32 pad3; | ||
| 145 | u32 pad4; | ||
| 146 | u32 constant_table_offset; | ||
| 147 | u32 constant_table_size; // number of entries | ||
| 148 | u32 label_table_offset; | ||
| 149 | u32 label_table_size; | ||
| 150 | u32 output_register_table_offset; | ||
| 151 | u32 output_register_table_size; | ||
| 152 | u32 uniform_table_offset; | ||
| 153 | u32 uniform_table_size; | ||
| 154 | u32 symbol_table_offset; | ||
| 155 | u32 symbol_table_size; | ||
| 156 | |||
| 157 | }; | ||
| 158 | static_assert(sizeof(DVLEHeader) == 0x40, "Incorrect structure size"); | ||
| 159 | #pragma pack() | ||
| 160 | 107 | ||
| 161 | void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, | 108 | void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data, u32 swizzle_size, |
| 162 | u32 main_offset, const Regs::VSOutputAttributes* output_attributes) | 109 | u32 main_offset, const Regs::VSOutputAttributes* output_attributes) |
| @@ -276,8 +223,8 @@ void DumpShader(const u32* binary_data, u32 binary_size, const u32* swizzle_data | |||
| 276 | dvlp.binary_size_words = binary_size; | 223 | dvlp.binary_size_words = binary_size; |
| 277 | QueueForWriting((u8*)binary_data, binary_size * sizeof(u32)); | 224 | QueueForWriting((u8*)binary_data, binary_size * sizeof(u32)); |
| 278 | 225 | ||
| 279 | dvlp.swizzle_patterns_offset = write_offset - dvlp_offset; | 226 | dvlp.swizzle_info_offset = write_offset - dvlp_offset; |
| 280 | dvlp.swizzle_patterns_num_entries = swizzle_size; | 227 | dvlp.swizzle_info_num_entries = swizzle_size; |
| 281 | u32 dummy = 0; | 228 | u32 dummy = 0; |
| 282 | for (unsigned int i = 0; i < swizzle_size; ++i) { | 229 | for (unsigned int i = 0; i < swizzle_size; ++i) { |
| 283 | QueueForWriting((u8*)&swizzle_data[i], sizeof(swizzle_data[i])); | 230 | QueueForWriting((u8*)&swizzle_data[i], sizeof(swizzle_data[i])); |
| @@ -356,10 +303,29 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() | |||
| 356 | return std::move(ret); | 303 | return std::move(ret); |
| 357 | } | 304 | } |
| 358 | 305 | ||
| 359 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info) { | 306 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info, bool disable_alpha) { |
| 360 | _dbg_assert_(Debug_GPU, info.format == Pica::Regs::TextureFormat::RGB8); | 307 | |
| 361 | 308 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each | |
| 362 | // Cf. rasterizer code for an explanation of this algorithm. | 309 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. |
| 310 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. | ||
| 311 | // texels are laid out in a 2x2 subtile like this: | ||
| 312 | // 2 3 | ||
| 313 | // 0 1 | ||
| 314 | // | ||
| 315 | // The full 8x8 tile has the texels arranged like this: | ||
| 316 | // | ||
| 317 | // 42 43 46 47 58 59 62 63 | ||
| 318 | // 40 41 44 45 56 57 60 61 | ||
| 319 | // 34 35 38 39 50 51 54 55 | ||
| 320 | // 32 33 36 37 48 49 52 53 | ||
| 321 | // 10 11 14 15 26 27 30 31 | ||
| 322 | // 08 09 12 13 24 25 28 29 | ||
| 323 | // 02 03 06 07 18 19 22 23 | ||
| 324 | // 00 01 04 05 16 17 20 21 | ||
| 325 | |||
| 326 | // TODO(neobrain): Not sure if this swizzling pattern is used for all textures. | ||
| 327 | // To be flexible in case different but similar patterns are used, we keep this | ||
| 328 | // somewhat inefficient code around for now. | ||
| 363 | int texel_index_within_tile = 0; | 329 | int texel_index_within_tile = 0; |
| 364 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | 330 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { |
| 365 | int sub_tile_width = 1 << block_size_index; | 331 | int sub_tile_width = 1 << block_size_index; |
| @@ -376,19 +342,134 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 376 | int coarse_x = (x / block_width) * block_width; | 342 | int coarse_x = (x / block_width) * block_width; |
| 377 | int coarse_y = (y / block_height) * block_height; | 343 | int coarse_y = (y / block_height) * block_height; |
| 378 | 344 | ||
| 379 | const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3; | 345 | switch (info.format) { |
| 380 | return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; | 346 | case Regs::TextureFormat::RGBA8: |
| 347 | { | ||
| 348 | const u8* source_ptr = source + coarse_x * block_height * 4 + coarse_y * info.stride + texel_index_within_tile * 4; | ||
| 349 | return { source_ptr[3], source_ptr[2], source_ptr[1], disable_alpha ? (u8)255 : source_ptr[0] }; | ||
| 350 | } | ||
| 351 | |||
| 352 | case Regs::TextureFormat::RGB8: | ||
| 353 | { | ||
| 354 | const u8* source_ptr = source + coarse_x * block_height * 3 + coarse_y * info.stride + texel_index_within_tile * 3; | ||
| 355 | return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; | ||
| 356 | } | ||
| 357 | |||
| 358 | case Regs::TextureFormat::RGBA5551: | ||
| 359 | { | ||
| 360 | const u16 source_ptr = *(const u16*)(source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2); | ||
| 361 | u8 r = (source_ptr >> 11) & 0x1F; | ||
| 362 | u8 g = ((source_ptr) >> 6) & 0x1F; | ||
| 363 | u8 b = (source_ptr >> 1) & 0x1F; | ||
| 364 | u8 a = source_ptr & 1; | ||
| 365 | return Math::MakeVec<u8>((r << 3) | (r >> 2), (g << 3) | (g >> 2), (b << 3) | (b >> 2), disable_alpha ? 255 : (a * 255)); | ||
| 366 | } | ||
| 367 | |||
| 368 | case Regs::TextureFormat::RGB565: | ||
| 369 | { | ||
| 370 | const u16 source_ptr = *(const u16*)(source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2); | ||
| 371 | u8 r = (source_ptr >> 11) & 0x1F; | ||
| 372 | u8 g = ((source_ptr) >> 5) & 0x3F; | ||
| 373 | u8 b = (source_ptr) & 0x1F; | ||
| 374 | return Math::MakeVec<u8>((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2), 255); | ||
| 375 | } | ||
| 376 | |||
| 377 | case Regs::TextureFormat::RGBA4: | ||
| 378 | { | ||
| 379 | const u8* source_ptr = source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2; | ||
| 380 | u8 r = source_ptr[1] >> 4; | ||
| 381 | u8 g = source_ptr[1] & 0xFF; | ||
| 382 | u8 b = source_ptr[0] >> 4; | ||
| 383 | u8 a = source_ptr[0] & 0xFF; | ||
| 384 | r = (r << 4) | r; | ||
| 385 | g = (g << 4) | g; | ||
| 386 | b = (b << 4) | b; | ||
| 387 | a = (a << 4) | a; | ||
| 388 | return { r, g, b, disable_alpha ? (u8)255 : a }; | ||
| 389 | } | ||
| 390 | |||
| 391 | case Regs::TextureFormat::IA8: | ||
| 392 | { | ||
| 393 | const u8* source_ptr = source + coarse_x * block_height * 2 + coarse_y * info.stride + texel_index_within_tile * 2; | ||
| 394 | |||
| 395 | // TODO: component order not verified | ||
| 396 | |||
| 397 | if (disable_alpha) { | ||
| 398 | // Show intensity as red, alpha as green | ||
| 399 | return { source_ptr[0], source_ptr[1], 0, 255 }; | ||
| 400 | } else { | ||
| 401 | return { source_ptr[0], source_ptr[0], source_ptr[0], source_ptr[1]}; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | case Regs::TextureFormat::I8: | ||
| 406 | { | ||
| 407 | const u8* source_ptr = source + coarse_x * block_height + coarse_y * info.stride + texel_index_within_tile; | ||
| 408 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | ||
| 409 | } | ||
| 410 | |||
| 411 | case Regs::TextureFormat::A8: | ||
| 412 | { | ||
| 413 | const u8* source_ptr = source + coarse_x * block_height + coarse_y * info.stride + texel_index_within_tile; | ||
| 414 | |||
| 415 | if (disable_alpha) { | ||
| 416 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | ||
| 417 | } else { | ||
| 418 | return { 0, 0, 0, *source_ptr }; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | case Regs::TextureFormat::IA4: | ||
| 423 | { | ||
| 424 | const u8* source_ptr = source + coarse_x * block_height / 2 + coarse_y * info.stride + texel_index_within_tile / 2; | ||
| 425 | |||
| 426 | // TODO: component order not verified | ||
| 427 | |||
| 428 | u8 i = (*source_ptr) & 0xF; | ||
| 429 | u8 a = ((*source_ptr) & 0xF0) >> 4; | ||
| 430 | a |= a << 4; | ||
| 431 | i |= i << 4; | ||
| 432 | |||
| 433 | if (disable_alpha) { | ||
| 434 | // Show intensity as red, alpha as green | ||
| 435 | return { i, a, 0, 255 }; | ||
| 436 | } else { | ||
| 437 | return { i, i, i, a }; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | case Regs::TextureFormat::A4: | ||
| 442 | { | ||
| 443 | const u8* source_ptr = source + coarse_x * block_height / 2 + coarse_y * info.stride + texel_index_within_tile / 2; | ||
| 444 | |||
| 445 | // TODO: component order not verified | ||
| 446 | |||
| 447 | u8 a = (coarse_x % 2) ? ((*source_ptr)&0xF) : (((*source_ptr) & 0xF0) >> 4); | ||
| 448 | a |= a << 4; | ||
| 449 | |||
| 450 | if (disable_alpha) { | ||
| 451 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | ||
| 452 | } else { | ||
| 453 | return { 0, 0, 0, *source_ptr }; | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | default: | ||
| 458 | LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format); | ||
| 459 | _dbg_assert_(HW_GPU, 0); | ||
| 460 | return {}; | ||
| 461 | } | ||
| 381 | } | 462 | } |
| 382 | 463 | ||
| 383 | TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, | 464 | TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config, |
| 384 | const Regs::TextureFormat& format) | 465 | const Regs::TextureFormat& format) |
| 385 | { | 466 | { |
| 386 | TextureInfo info; | 467 | TextureInfo info; |
| 387 | info.address = config.GetPhysicalAddress(); | 468 | info.physical_address = config.GetPhysicalAddress(); |
| 388 | info.width = config.width; | 469 | info.width = config.width; |
| 389 | info.height = config.height; | 470 | info.height = config.height; |
| 390 | info.format = format; | 471 | info.format = format; |
| 391 | info.stride = Pica::Regs::BytesPerPixel(info.format) * info.width; | 472 | info.stride = Pica::Regs::NibblesPerPixel(info.format) * info.width / 2; |
| 392 | return info; | 473 | return info; |
| 393 | } | 474 | } |
| 394 | 475 | ||
| @@ -499,26 +580,32 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 499 | for (size_t index = 0; index < stages.size(); ++index) { | 580 | for (size_t index = 0; index < stages.size(); ++index) { |
| 500 | const auto& tev_stage = stages[index]; | 581 | const auto& tev_stage = stages[index]; |
| 501 | 582 | ||
| 502 | const std::map<Source, std::string> source_map = { | 583 | static const std::map<Source, std::string> source_map = { |
| 503 | { Source::PrimaryColor, "PrimaryColor" }, | 584 | { Source::PrimaryColor, "PrimaryColor" }, |
| 504 | { Source::Texture0, "Texture0" }, | 585 | { Source::Texture0, "Texture0" }, |
| 586 | { Source::Texture1, "Texture1" }, | ||
| 587 | { Source::Texture2, "Texture2" }, | ||
| 505 | { Source::Constant, "Constant" }, | 588 | { Source::Constant, "Constant" }, |
| 506 | { Source::Previous, "Previous" }, | 589 | { Source::Previous, "Previous" }, |
| 507 | }; | 590 | }; |
| 508 | 591 | ||
| 509 | const std::map<ColorModifier, std::string> color_modifier_map = { | 592 | static const std::map<ColorModifier, std::string> color_modifier_map = { |
| 510 | { ColorModifier::SourceColor, { "%source.rgb" } } | 593 | { ColorModifier::SourceColor, { "%source.rgb" } }, |
| 594 | { ColorModifier::SourceAlpha, { "%source.aaa" } }, | ||
| 511 | }; | 595 | }; |
| 512 | const std::map<AlphaModifier, std::string> alpha_modifier_map = { | 596 | static const std::map<AlphaModifier, std::string> alpha_modifier_map = { |
| 513 | { AlphaModifier::SourceAlpha, "%source.a" } | 597 | { AlphaModifier::SourceAlpha, "%source.a" }, |
| 598 | { AlphaModifier::OneMinusSourceAlpha, "(255 - %source.a)" }, | ||
| 514 | }; | 599 | }; |
| 515 | 600 | ||
| 516 | std::map<Operation, std::string> combiner_map = { | 601 | static const std::map<Operation, std::string> combiner_map = { |
| 517 | { Operation::Replace, "%source1" }, | 602 | { Operation::Replace, "%source1" }, |
| 518 | { Operation::Modulate, "(%source1 * %source2) / 255" }, | 603 | { Operation::Modulate, "(%source1 * %source2) / 255" }, |
| 604 | { Operation::Add, "(%source1 + %source2)" }, | ||
| 605 | { Operation::Lerp, "lerp(%source1, %source2, %source3)" }, | ||
| 519 | }; | 606 | }; |
| 520 | 607 | ||
| 521 | auto ReplacePattern = | 608 | static auto ReplacePattern = |
| 522 | [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string { | 609 | [](const std::string& input, const std::string& pattern, const std::string& replacement) -> std::string { |
| 523 | size_t start = input.find(pattern); | 610 | size_t start = input.find(pattern); |
| 524 | if (start == std::string::npos) | 611 | if (start == std::string::npos) |
| @@ -528,8 +615,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 528 | ret.replace(start, pattern.length(), replacement); | 615 | ret.replace(start, pattern.length(), replacement); |
| 529 | return ret; | 616 | return ret; |
| 530 | }; | 617 | }; |
| 531 | auto GetColorSourceStr = | 618 | static auto GetColorSourceStr = |
| 532 | [&source_map,&color_modifier_map,&ReplacePattern](const Source& src, const ColorModifier& modifier) { | 619 | [](const Source& src, const ColorModifier& modifier) { |
| 533 | auto src_it = source_map.find(src); | 620 | auto src_it = source_map.find(src); |
| 534 | std::string src_str = "Unknown"; | 621 | std::string src_str = "Unknown"; |
| 535 | if (src_it != source_map.end()) | 622 | if (src_it != source_map.end()) |
| @@ -542,8 +629,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 542 | 629 | ||
| 543 | return ReplacePattern(modifier_str, "%source", src_str); | 630 | return ReplacePattern(modifier_str, "%source", src_str); |
| 544 | }; | 631 | }; |
| 545 | auto GetColorCombinerStr = | 632 | static auto GetColorCombinerStr = |
| 546 | [&](const Regs::TevStageConfig& tev_stage) { | 633 | [](const Regs::TevStageConfig& tev_stage) { |
| 547 | auto op_it = combiner_map.find(tev_stage.color_op); | 634 | auto op_it = combiner_map.find(tev_stage.color_op); |
| 548 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; | 635 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; |
| 549 | if (op_it != combiner_map.end()) | 636 | if (op_it != combiner_map.end()) |
| @@ -553,8 +640,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 553 | op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2)); | 640 | op_str = ReplacePattern(op_str, "%source2", GetColorSourceStr(tev_stage.color_source2, tev_stage.color_modifier2)); |
| 554 | return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3)); | 641 | return ReplacePattern(op_str, "%source3", GetColorSourceStr(tev_stage.color_source3, tev_stage.color_modifier3)); |
| 555 | }; | 642 | }; |
| 556 | auto GetAlphaSourceStr = | 643 | static auto GetAlphaSourceStr = |
| 557 | [&source_map,&alpha_modifier_map,&ReplacePattern](const Source& src, const AlphaModifier& modifier) { | 644 | [](const Source& src, const AlphaModifier& modifier) { |
| 558 | auto src_it = source_map.find(src); | 645 | auto src_it = source_map.find(src); |
| 559 | std::string src_str = "Unknown"; | 646 | std::string src_str = "Unknown"; |
| 560 | if (src_it != source_map.end()) | 647 | if (src_it != source_map.end()) |
| @@ -567,8 +654,8 @@ void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages) | |||
| 567 | 654 | ||
| 568 | return ReplacePattern(modifier_str, "%source", src_str); | 655 | return ReplacePattern(modifier_str, "%source", src_str); |
| 569 | }; | 656 | }; |
| 570 | auto GetAlphaCombinerStr = | 657 | static auto GetAlphaCombinerStr = |
| 571 | [&](const Regs::TevStageConfig& tev_stage) { | 658 | [](const Regs::TevStageConfig& tev_stage) { |
| 572 | auto op_it = combiner_map.find(tev_stage.alpha_op); | 659 | auto op_it = combiner_map.find(tev_stage.alpha_op); |
| 573 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; | 660 | std::string op_str = "Unknown op (%source1, %source2, %source3)"; |
| 574 | if (op_it != combiner_map.end()) | 661 | if (op_it != combiner_map.end()) |
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 51f14f12f..f361a5385 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -26,6 +26,7 @@ public: | |||
| 26 | CommandProcessed, | 26 | CommandProcessed, |
| 27 | IncomingPrimitiveBatch, | 27 | IncomingPrimitiveBatch, |
| 28 | FinishedPrimitiveBatch, | 28 | FinishedPrimitiveBatch, |
| 29 | VertexLoaded, | ||
| 29 | 30 | ||
| 30 | NumEvents | 31 | NumEvents |
| 31 | }; | 32 | }; |
| @@ -192,7 +193,7 @@ void OnPicaRegWrite(u32 id, u32 value); | |||
| 192 | std::unique_ptr<PicaTrace> FinishPicaTracing(); | 193 | std::unique_ptr<PicaTrace> FinishPicaTracing(); |
| 193 | 194 | ||
| 194 | struct TextureInfo { | 195 | struct TextureInfo { |
| 195 | unsigned int address; | 196 | PAddr physical_address; |
| 196 | int width; | 197 | int width; |
| 197 | int height; | 198 | int height; |
| 198 | int stride; | 199 | int stride; |
| @@ -202,7 +203,17 @@ struct TextureInfo { | |||
| 202 | const Pica::Regs::TextureFormat& format); | 203 | const Pica::Regs::TextureFormat& format); |
| 203 | }; | 204 | }; |
| 204 | 205 | ||
| 205 | const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info); | 206 | /** |
| 207 | * Lookup texel located at the given coordinates and return an RGBA vector of its color. | ||
| 208 | * @param source Source pointer to read data from | ||
| 209 | * @param s,t Texture coordinates to read from | ||
| 210 | * @param info TextureInfo object describing the texture setup | ||
| 211 | * @param disable_alpha This is used for debug widgets which use this method to display textures without providing a good way to visualize alpha by themselves. If true, this will return 255 for the alpha component, and either drop the information entirely or store it in an "unused" color channel. | ||
| 212 | * @todo Eventually we should get rid of the disable_alpha parameter. | ||
| 213 | */ | ||
| 214 | const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info, | ||
| 215 | bool disable_alpha = false); | ||
| 216 | |||
| 206 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | 217 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); |
| 207 | 218 | ||
| 208 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); | 219 | void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig,6>& stages); |
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 16b1656bb..4eb8b3d4d 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h | |||
| @@ -85,7 +85,7 @@ public: | |||
| 85 | 85 | ||
| 86 | void UnregisterObserver(DebuggerObserver* observer) | 86 | void UnregisterObserver(DebuggerObserver* observer) |
| 87 | { | 87 | { |
| 88 | std::remove(observers.begin(), observers.end(), observer); | 88 | observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end()); |
| 89 | observer->observed = nullptr; | 89 | observer->observed = nullptr; |
| 90 | } | 90 | } |
| 91 | 91 | ||
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 4c3791ad9..06552a3ef 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <initializer_list> | 9 | #include <initializer_list> |
| 10 | #include <map> | 10 | #include <map> |
| 11 | #include <vector> | ||
| 11 | 12 | ||
| 12 | #include "common/bit_field.h" | 13 | #include "common/bit_field.h" |
| 13 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| @@ -104,6 +105,11 @@ struct Regs { | |||
| 104 | INSERT_PADDING_WORDS(0x17); | 105 | INSERT_PADDING_WORDS(0x17); |
| 105 | 106 | ||
| 106 | struct TextureConfig { | 107 | struct TextureConfig { |
| 108 | enum WrapMode : u32 { | ||
| 109 | ClampToEdge = 0, | ||
| 110 | Repeat = 2, | ||
| 111 | }; | ||
| 112 | |||
| 107 | INSERT_PADDING_WORDS(0x1); | 113 | INSERT_PADDING_WORDS(0x1); |
| 108 | 114 | ||
| 109 | union { | 115 | union { |
| @@ -111,12 +117,17 @@ struct Regs { | |||
| 111 | BitField<16, 16, u32> width; | 117 | BitField<16, 16, u32> width; |
| 112 | }; | 118 | }; |
| 113 | 119 | ||
| 114 | INSERT_PADDING_WORDS(0x2); | 120 | union { |
| 121 | BitField< 8, 2, WrapMode> wrap_s; | ||
| 122 | BitField<11, 2, WrapMode> wrap_t; | ||
| 123 | }; | ||
| 124 | |||
| 125 | INSERT_PADDING_WORDS(0x1); | ||
| 115 | 126 | ||
| 116 | u32 address; | 127 | u32 address; |
| 117 | 128 | ||
| 118 | u32 GetPhysicalAddress() const { | 129 | u32 GetPhysicalAddress() const { |
| 119 | return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; | 130 | return DecodeAddressRegister(address); |
| 120 | } | 131 | } |
| 121 | 132 | ||
| 122 | // texture1 and texture2 store the texture format directly after the address | 133 | // texture1 and texture2 store the texture format directly after the address |
| @@ -131,36 +142,70 @@ struct Regs { | |||
| 131 | RGBA5551 = 2, | 142 | RGBA5551 = 2, |
| 132 | RGB565 = 3, | 143 | RGB565 = 3, |
| 133 | RGBA4 = 4, | 144 | RGBA4 = 4, |
| 145 | IA8 = 5, | ||
| 146 | |||
| 147 | I8 = 7, | ||
| 148 | A8 = 8, | ||
| 149 | IA4 = 9, | ||
| 134 | 150 | ||
| 151 | A4 = 11, | ||
| 135 | // TODO: Support for the other formats is not implemented, yet. | 152 | // TODO: Support for the other formats is not implemented, yet. |
| 136 | // Seems like they are luminance formats and compressed textures. | 153 | // Seems like they are luminance formats and compressed textures. |
| 137 | }; | 154 | }; |
| 138 | 155 | ||
| 139 | static unsigned BytesPerPixel(TextureFormat format) { | 156 | static unsigned NibblesPerPixel(TextureFormat format) { |
| 140 | switch (format) { | 157 | switch (format) { |
| 141 | case TextureFormat::RGBA8: | 158 | case TextureFormat::RGBA8: |
| 142 | return 4; | 159 | return 8; |
| 143 | 160 | ||
| 144 | case TextureFormat::RGB8: | 161 | case TextureFormat::RGB8: |
| 145 | return 3; | 162 | return 6; |
| 146 | 163 | ||
| 147 | case TextureFormat::RGBA5551: | 164 | case TextureFormat::RGBA5551: |
| 148 | case TextureFormat::RGB565: | 165 | case TextureFormat::RGB565: |
| 149 | case TextureFormat::RGBA4: | 166 | case TextureFormat::RGBA4: |
| 150 | return 2; | 167 | case TextureFormat::IA8: |
| 168 | return 4; | ||
| 151 | 169 | ||
| 152 | default: | 170 | case TextureFormat::A4: |
| 153 | // placeholder for yet unknown formats | ||
| 154 | return 1; | 171 | return 1; |
| 172 | |||
| 173 | case TextureFormat::I8: | ||
| 174 | case TextureFormat::A8: | ||
| 175 | case TextureFormat::IA4: | ||
| 176 | default: // placeholder for yet unknown formats | ||
| 177 | return 2; | ||
| 155 | } | 178 | } |
| 156 | } | 179 | } |
| 157 | 180 | ||
| 158 | BitField< 0, 1, u32> texturing_enable; | 181 | union { |
| 182 | BitField< 0, 1, u32> texture0_enable; | ||
| 183 | BitField< 1, 1, u32> texture1_enable; | ||
| 184 | BitField< 2, 1, u32> texture2_enable; | ||
| 185 | }; | ||
| 159 | TextureConfig texture0; | 186 | TextureConfig texture0; |
| 160 | INSERT_PADDING_WORDS(0x8); | 187 | INSERT_PADDING_WORDS(0x8); |
| 161 | BitField<0, 4, TextureFormat> texture0_format; | 188 | BitField<0, 4, TextureFormat> texture0_format; |
| 162 | 189 | INSERT_PADDING_WORDS(0x2); | |
| 163 | INSERT_PADDING_WORDS(0x31); | 190 | TextureConfig texture1; |
| 191 | BitField<0, 4, TextureFormat> texture1_format; | ||
| 192 | INSERT_PADDING_WORDS(0x2); | ||
| 193 | TextureConfig texture2; | ||
| 194 | BitField<0, 4, TextureFormat> texture2_format; | ||
| 195 | INSERT_PADDING_WORDS(0x21); | ||
| 196 | |||
| 197 | struct FullTextureConfig { | ||
| 198 | const bool enabled; | ||
| 199 | const TextureConfig config; | ||
| 200 | const TextureFormat format; | ||
| 201 | }; | ||
| 202 | const std::array<FullTextureConfig, 3> GetTextures() const { | ||
| 203 | return {{ | ||
| 204 | { static_cast<bool>(texture0_enable), texture0, texture0_format }, | ||
| 205 | { static_cast<bool>(texture1_enable), texture1, texture1_format }, | ||
| 206 | { static_cast<bool>(texture2_enable), texture2, texture2_format } | ||
| 207 | }}; | ||
| 208 | } | ||
| 164 | 209 | ||
| 165 | // 0xc0-0xff: Texture Combiner (akin to glTexEnv) | 210 | // 0xc0-0xff: Texture Combiner (akin to glTexEnv) |
| 166 | struct TevStageConfig { | 211 | struct TevStageConfig { |
| @@ -282,11 +327,11 @@ struct Regs { | |||
| 282 | 327 | ||
| 283 | INSERT_PADDING_WORDS(0x1); | 328 | INSERT_PADDING_WORDS(0x1); |
| 284 | 329 | ||
| 285 | inline u32 GetColorBufferAddress() const { | 330 | inline u32 GetColorBufferPhysicalAddress() const { |
| 286 | return Memory::PhysicalToVirtualAddress(DecodeAddressRegister(color_buffer_address)); | 331 | return DecodeAddressRegister(color_buffer_address); |
| 287 | } | 332 | } |
| 288 | inline u32 GetDepthBufferAddress() const { | 333 | inline u32 GetDepthBufferPhysicalAddress() const { |
| 289 | return Memory::PhysicalToVirtualAddress(DecodeAddressRegister(depth_buffer_address)); | 334 | return DecodeAddressRegister(depth_buffer_address); |
| 290 | } | 335 | } |
| 291 | 336 | ||
| 292 | inline u32 GetWidth() const { | 337 | inline u32 GetWidth() const { |
| @@ -310,9 +355,8 @@ struct Regs { | |||
| 310 | 355 | ||
| 311 | BitField<0, 29, u32> base_address; | 356 | BitField<0, 29, u32> base_address; |
| 312 | 357 | ||
| 313 | inline u32 GetBaseAddress() const { | 358 | u32 GetPhysicalBaseAddress() const { |
| 314 | // TODO: Ugly, should fix PhysicalToVirtualAddress instead | 359 | return DecodeAddressRegister(base_address); |
| 315 | return DecodeAddressRegister(base_address) - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; | ||
| 316 | } | 360 | } |
| 317 | 361 | ||
| 318 | // Descriptor for internal vertex attributes | 362 | // Descriptor for internal vertex attributes |
| @@ -448,7 +492,11 @@ struct Regs { | |||
| 448 | 492 | ||
| 449 | BitField<8, 2, TriangleTopology> triangle_topology; | 493 | BitField<8, 2, TriangleTopology> triangle_topology; |
| 450 | 494 | ||
| 451 | INSERT_PADDING_WORDS(0x5b); | 495 | INSERT_PADDING_WORDS(0x51); |
| 496 | |||
| 497 | BitField<0, 16, u32> vs_bool_uniforms; | ||
| 498 | |||
| 499 | INSERT_PADDING_WORDS(0x9); | ||
| 452 | 500 | ||
| 453 | // Offset to shader program entry point (in words) | 501 | // Offset to shader program entry point (in words) |
| 454 | BitField<0, 16, u32> vs_main_offset; | 502 | BitField<0, 16, u32> vs_main_offset; |
| @@ -556,9 +604,13 @@ struct Regs { | |||
| 556 | ADD_FIELD(viewport_depth_range); | 604 | ADD_FIELD(viewport_depth_range); |
| 557 | ADD_FIELD(viewport_depth_far_plane); | 605 | ADD_FIELD(viewport_depth_far_plane); |
| 558 | ADD_FIELD(viewport_corner); | 606 | ADD_FIELD(viewport_corner); |
| 559 | ADD_FIELD(texturing_enable); | 607 | ADD_FIELD(texture0_enable); |
| 560 | ADD_FIELD(texture0); | 608 | ADD_FIELD(texture0); |
| 561 | ADD_FIELD(texture0_format); | 609 | ADD_FIELD(texture0_format); |
| 610 | ADD_FIELD(texture1); | ||
| 611 | ADD_FIELD(texture1_format); | ||
| 612 | ADD_FIELD(texture2); | ||
| 613 | ADD_FIELD(texture2_format); | ||
| 562 | ADD_FIELD(tev_stage0); | 614 | ADD_FIELD(tev_stage0); |
| 563 | ADD_FIELD(tev_stage1); | 615 | ADD_FIELD(tev_stage1); |
| 564 | ADD_FIELD(tev_stage2); | 616 | ADD_FIELD(tev_stage2); |
| @@ -572,6 +624,7 @@ struct Regs { | |||
| 572 | ADD_FIELD(trigger_draw); | 624 | ADD_FIELD(trigger_draw); |
| 573 | ADD_FIELD(trigger_draw_indexed); | 625 | ADD_FIELD(trigger_draw_indexed); |
| 574 | ADD_FIELD(triangle_topology); | 626 | ADD_FIELD(triangle_topology); |
| 627 | ADD_FIELD(vs_bool_uniforms); | ||
| 575 | ADD_FIELD(vs_main_offset); | 628 | ADD_FIELD(vs_main_offset); |
| 576 | ADD_FIELD(vs_input_register_map); | 629 | ADD_FIELD(vs_input_register_map); |
| 577 | ADD_FIELD(vs_uniform_setup); | 630 | ADD_FIELD(vs_uniform_setup); |
| @@ -622,9 +675,13 @@ ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e); | |||
| 622 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); | 675 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); |
| 623 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); | 676 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); |
| 624 | ASSERT_REG_POSITION(viewport_corner, 0x68); | 677 | ASSERT_REG_POSITION(viewport_corner, 0x68); |
| 625 | ASSERT_REG_POSITION(texturing_enable, 0x80); | 678 | ASSERT_REG_POSITION(texture0_enable, 0x80); |
| 626 | ASSERT_REG_POSITION(texture0, 0x81); | 679 | ASSERT_REG_POSITION(texture0, 0x81); |
| 627 | ASSERT_REG_POSITION(texture0_format, 0x8e); | 680 | ASSERT_REG_POSITION(texture0_format, 0x8e); |
| 681 | ASSERT_REG_POSITION(texture1, 0x91); | ||
| 682 | ASSERT_REG_POSITION(texture1_format, 0x96); | ||
| 683 | ASSERT_REG_POSITION(texture2, 0x99); | ||
| 684 | ASSERT_REG_POSITION(texture2_format, 0x9e); | ||
| 628 | ASSERT_REG_POSITION(tev_stage0, 0xc0); | 685 | ASSERT_REG_POSITION(tev_stage0, 0xc0); |
| 629 | ASSERT_REG_POSITION(tev_stage1, 0xc8); | 686 | ASSERT_REG_POSITION(tev_stage1, 0xc8); |
| 630 | ASSERT_REG_POSITION(tev_stage2, 0xd0); | 687 | ASSERT_REG_POSITION(tev_stage2, 0xd0); |
| @@ -638,6 +695,7 @@ ASSERT_REG_POSITION(num_vertices, 0x228); | |||
| 638 | ASSERT_REG_POSITION(trigger_draw, 0x22e); | 695 | ASSERT_REG_POSITION(trigger_draw, 0x22e); |
| 639 | ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); | 696 | ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f); |
| 640 | ASSERT_REG_POSITION(triangle_topology, 0x25e); | 697 | ASSERT_REG_POSITION(triangle_topology, 0x25e); |
| 698 | ASSERT_REG_POSITION(vs_bool_uniforms, 0x2b0); | ||
| 641 | ASSERT_REG_POSITION(vs_main_offset, 0x2ba); | 699 | ASSERT_REG_POSITION(vs_main_offset, 0x2ba); |
| 642 | ASSERT_REG_POSITION(vs_input_register_map, 0x2bb); | 700 | ASSERT_REG_POSITION(vs_input_register_map, 0x2bb); |
| 643 | ASSERT_REG_POSITION(vs_uniform_setup, 0x2c0); | 701 | ASSERT_REG_POSITION(vs_uniform_setup, 0x2c0); |
| @@ -719,6 +777,14 @@ struct float24 { | |||
| 719 | return ToFloat32() <= flt.ToFloat32(); | 777 | return ToFloat32() <= flt.ToFloat32(); |
| 720 | } | 778 | } |
| 721 | 779 | ||
| 780 | bool operator == (const float24& flt) const { | ||
| 781 | return ToFloat32() == flt.ToFloat32(); | ||
| 782 | } | ||
| 783 | |||
| 784 | bool operator != (const float24& flt) const { | ||
| 785 | return ToFloat32() != flt.ToFloat32(); | ||
| 786 | } | ||
| 787 | |||
| 722 | private: | 788 | private: |
| 723 | // Stored as a regular float, merely for convenience | 789 | // Stored as a regular float, merely for convenience |
| 724 | // TODO: Perform proper arithmetic on this! | 790 | // TODO: Perform proper arithmetic on this! |
| @@ -736,5 +802,15 @@ union CommandHeader { | |||
| 736 | BitField<31, 1, u32> group_commands; | 802 | BitField<31, 1, u32> group_commands; |
| 737 | }; | 803 | }; |
| 738 | 804 | ||
| 805 | // TODO: Ugly, should fix PhysicalToVirtualAddress instead | ||
| 806 | inline static u32 PAddrToVAddr(u32 addr) { | ||
| 807 | if (addr >= Memory::VRAM_PADDR && addr < Memory::VRAM_PADDR + Memory::VRAM_SIZE) { | ||
| 808 | return addr - Memory::VRAM_PADDR + Memory::VRAM_VADDR; | ||
| 809 | } else if (addr >= Memory::FCRAM_PADDR && addr < Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) { | ||
| 810 | return addr - Memory::FCRAM_PADDR + Memory::HEAP_LINEAR_VADDR; | ||
| 811 | } else { | ||
| 812 | return 0; | ||
| 813 | } | ||
| 814 | } | ||
| 739 | 815 | ||
| 740 | } // namespace | 816 | } // namespace |
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp index 102693ed9..ff46c7b52 100644 --- a/src/video_core/primitive_assembly.cpp +++ b/src/video_core/primitive_assembly.cpp | |||
| @@ -30,20 +30,27 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl | |||
| 30 | } | 30 | } |
| 31 | break; | 31 | break; |
| 32 | 32 | ||
| 33 | case Regs::TriangleTopology::Strip: | ||
| 33 | case Regs::TriangleTopology::Fan: | 34 | case Regs::TriangleTopology::Fan: |
| 34 | if (buffer_index == 2) { | 35 | if (strip_ready) { |
| 35 | buffer_index = 0; | 36 | // TODO: Should be "buffer[0], buffer[1], vtx" instead! |
| 36 | 37 | // Not quite sure why we need this order for things to show up properly. | |
| 37 | triangle_handler(buffer[0], buffer[1], vtx); | 38 | // Maybe a bug in the rasterizer? |
| 39 | triangle_handler(buffer[1], buffer[0], vtx); | ||
| 40 | } | ||
| 41 | buffer[buffer_index] = vtx; | ||
| 38 | 42 | ||
| 39 | buffer[1] = vtx; | 43 | if (topology == Regs::TriangleTopology::Strip) { |
| 40 | } else { | 44 | strip_ready |= (buffer_index == 1); |
| 41 | buffer[buffer_index++] = vtx; | 45 | buffer_index = !buffer_index; |
| 46 | } else if (topology == Regs::TriangleTopology::Fan) { | ||
| 47 | buffer_index = 1; | ||
| 48 | strip_ready = true; | ||
| 42 | } | 49 | } |
| 43 | break; | 50 | break; |
| 44 | 51 | ||
| 45 | default: | 52 | default: |
| 46 | LOG_ERROR(Render_Software, "Unknown triangle topology %x:", (int)topology); | 53 | LOG_ERROR(HW_GPU, "Unknown triangle topology %x:", (int)topology); |
| 47 | break; | 54 | break; |
| 48 | } | 55 | } |
| 49 | } | 56 | } |
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h index ea2e2f61e..decf0fd64 100644 --- a/src/video_core/primitive_assembly.h +++ b/src/video_core/primitive_assembly.h | |||
| @@ -37,6 +37,7 @@ private: | |||
| 37 | 37 | ||
| 38 | int buffer_index; | 38 | int buffer_index; |
| 39 | VertexType buffer[2]; | 39 | VertexType buffer[2]; |
| 40 | bool strip_ready = false; | ||
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| 42 | 43 | ||
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index b7e04a560..bf9c36661 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -18,7 +18,7 @@ namespace Pica { | |||
| 18 | namespace Rasterizer { | 18 | namespace Rasterizer { |
| 19 | 19 | ||
| 20 | static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { | 20 | static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { |
| 21 | u32* color_buffer = (u32*)Memory::GetPointer(registers.framebuffer.GetColorBufferAddress()); | 21 | u32* color_buffer = reinterpret_cast<u32*>(Memory::GetPointer(PAddrToVAddr(registers.framebuffer.GetColorBufferPhysicalAddress()))); |
| 22 | u32 value = (color.a() << 24) | (color.r() << 16) | (color.g() << 8) | color.b(); | 22 | u32 value = (color.a() << 24) | (color.r() << 16) | (color.g() << 8) | color.b(); |
| 23 | 23 | ||
| 24 | // Assuming RGBA8 format until actual framebuffer format handling is implemented | 24 | // Assuming RGBA8 format until actual framebuffer format handling is implemented |
| @@ -26,14 +26,14 @@ static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { | |||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | static u32 GetDepth(int x, int y) { | 28 | static u32 GetDepth(int x, int y) { |
| 29 | u16* depth_buffer = (u16*)Memory::GetPointer(registers.framebuffer.GetDepthBufferAddress()); | 29 | u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(registers.framebuffer.GetDepthBufferPhysicalAddress()))); |
| 30 | 30 | ||
| 31 | // Assuming 16-bit depth buffer format until actual format handling is implemented | 31 | // Assuming 16-bit depth buffer format until actual format handling is implemented |
| 32 | return *(depth_buffer + x + y * registers.framebuffer.GetWidth()); | 32 | return *(depth_buffer + x + y * registers.framebuffer.GetWidth()); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | static void SetDepth(int x, int y, u16 value) { | 35 | static void SetDepth(int x, int y, u16 value) { |
| 36 | u16* depth_buffer = (u16*)Memory::GetPointer(registers.framebuffer.GetDepthBufferAddress()); | 36 | u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(registers.framebuffer.GetDepthBufferPhysicalAddress()))); |
| 37 | 37 | ||
| 38 | // Assuming 16-bit depth buffer format until actual format handling is implemented | 38 | // Assuming 16-bit depth buffer format until actual format handling is implemented |
| 39 | *(depth_buffer + x + y * registers.framebuffer.GetWidth()) = value; | 39 | *(depth_buffer + x + y * registers.framebuffer.GetWidth()) = value; |
| @@ -167,60 +167,48 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 167 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) | 167 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) |
| 168 | }; | 168 | }; |
| 169 | 169 | ||
| 170 | Math::Vec4<u8> texture_color{}; | 170 | Math::Vec2<float24> uv[3]; |
| 171 | float24 u = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u()); | 171 | uv[0].u() = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u()); |
| 172 | float24 v = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v()); | 172 | uv[0].v() = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v()); |
| 173 | if (registers.texturing_enable) { | 173 | uv[1].u() = GetInterpolatedAttribute(v0.tc1.u(), v1.tc1.u(), v2.tc1.u()); |
| 174 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each | 174 | uv[1].v() = GetInterpolatedAttribute(v0.tc1.v(), v1.tc1.v(), v2.tc1.v()); |
| 175 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. | 175 | uv[2].u() = GetInterpolatedAttribute(v0.tc2.u(), v1.tc2.u(), v2.tc2.u()); |
| 176 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. | 176 | uv[2].v() = GetInterpolatedAttribute(v0.tc2.v(), v1.tc2.v(), v2.tc2.v()); |
| 177 | // texels are laid out in a 2x2 subtile like this: | 177 | |
| 178 | // 2 3 | 178 | Math::Vec4<u8> texture_color[3]{}; |
| 179 | // 0 1 | 179 | for (int i = 0; i < 3; ++i) { |
| 180 | // | 180 | auto texture = registers.GetTextures()[i]; |
| 181 | // The full 8x8 tile has the texels arranged like this: | 181 | if (!texture.enabled) |
| 182 | // | 182 | continue; |
| 183 | // 42 43 46 47 58 59 62 63 | 183 | |
| 184 | // 40 41 44 45 56 57 60 61 | 184 | _dbg_assert_(HW_GPU, 0 != texture.config.address); |
| 185 | // 34 35 38 39 50 51 54 55 | 185 | |
| 186 | // 32 33 36 37 48 49 52 53 | 186 | int s = (int)(uv[i].u() * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32(); |
| 187 | // 10 11 14 15 26 27 30 31 | 187 | int t = (int)(uv[i].v() * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32(); |
| 188 | // 08 09 12 13 24 25 28 29 | 188 | auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val, unsigned size) { |
| 189 | // 02 03 06 07 18 19 22 23 | 189 | switch (mode) { |
| 190 | // 00 01 04 05 16 17 20 21 | 190 | case Regs::TextureConfig::ClampToEdge: |
| 191 | 191 | val = std::max(val, 0); | |
| 192 | // TODO: This is currently hardcoded for RGB8 | 192 | val = std::min(val, (int)size - 1); |
| 193 | u32* texture_data = (u32*)Memory::GetPointer(registers.texture0.GetPhysicalAddress()); | 193 | return val; |
| 194 | 194 | ||
| 195 | // TODO(neobrain): Not sure if this swizzling pattern is used for all textures. | 195 | case Regs::TextureConfig::Repeat: |
| 196 | // To be flexible in case different but similar patterns are used, we keep this | 196 | return (int)(((unsigned)val) % size); |
| 197 | // somewhat inefficient code around for now. | 197 | |
| 198 | int s = (int)(u * float24::FromFloat32(static_cast<float>(registers.texture0.width))).ToFloat32(); | 198 | default: |
| 199 | int t = (int)(v * float24::FromFloat32(static_cast<float>(registers.texture0.height))).ToFloat32(); | 199 | LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x\n", (int)mode); |
| 200 | int texel_index_within_tile = 0; | 200 | _dbg_assert_(HW_GPU, 0); |
| 201 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | 201 | return 0; |
| 202 | int sub_tile_width = 1 << block_size_index; | 202 | } |
| 203 | int sub_tile_height = 1 << block_size_index; | 203 | }; |
| 204 | 204 | s = GetWrappedTexCoord(registers.texture0.wrap_s, s, registers.texture0.width); | |
| 205 | int sub_tile_index = (s & sub_tile_width) << block_size_index; | 205 | t = GetWrappedTexCoord(registers.texture0.wrap_t, t, registers.texture0.height); |
| 206 | sub_tile_index += 2 * ((t & sub_tile_height) << block_size_index); | 206 | |
| 207 | texel_index_within_tile += sub_tile_index; | 207 | u8* texture_data = Memory::GetPointer(PAddrToVAddr(texture.config.GetPhysicalAddress())); |
| 208 | } | 208 | auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format); |
| 209 | 209 | ||
| 210 | const int block_width = 8; | 210 | texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info); |
| 211 | const int block_height = 8; | 211 | DebugUtils::DumpTexture(texture.config, texture_data); |
| 212 | |||
| 213 | int coarse_s = (s / block_width) * block_width; | ||
| 214 | int coarse_t = (t / block_height) * block_height; | ||
| 215 | |||
| 216 | const int row_stride = registers.texture0.width * 3; | ||
| 217 | u8* source_ptr = (u8*)texture_data + coarse_s * block_height * 3 + coarse_t * row_stride + texel_index_within_tile * 3; | ||
| 218 | texture_color.r() = source_ptr[2]; | ||
| 219 | texture_color.g() = source_ptr[1]; | ||
| 220 | texture_color.b() = source_ptr[0]; | ||
| 221 | texture_color.a() = 0xFF; | ||
| 222 | |||
| 223 | DebugUtils::DumpTexture(registers.texture0, (u8*)texture_data); | ||
| 224 | } | 212 | } |
| 225 | 213 | ||
| 226 | // Texture environment - consists of 6 stages of color and alpha combining. | 214 | // Texture environment - consists of 6 stages of color and alpha combining. |
| @@ -237,22 +225,29 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 237 | using AlphaModifier = Regs::TevStageConfig::AlphaModifier; | 225 | using AlphaModifier = Regs::TevStageConfig::AlphaModifier; |
| 238 | using Operation = Regs::TevStageConfig::Operation; | 226 | using Operation = Regs::TevStageConfig::Operation; |
| 239 | 227 | ||
| 240 | auto GetColorSource = [&](Source source) -> Math::Vec3<u8> { | 228 | auto GetColorSource = [&](Source source) -> Math::Vec4<u8> { |
| 241 | switch (source) { | 229 | switch (source) { |
| 242 | case Source::PrimaryColor: | 230 | case Source::PrimaryColor: |
| 243 | return primary_color.rgb(); | 231 | return primary_color; |
| 244 | 232 | ||
| 245 | case Source::Texture0: | 233 | case Source::Texture0: |
| 246 | return texture_color.rgb(); | 234 | return texture_color[0]; |
| 235 | |||
| 236 | case Source::Texture1: | ||
| 237 | return texture_color[1]; | ||
| 238 | |||
| 239 | case Source::Texture2: | ||
| 240 | return texture_color[2]; | ||
| 247 | 241 | ||
| 248 | case Source::Constant: | 242 | case Source::Constant: |
| 249 | return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b}; | 243 | return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b, tev_stage.const_a}; |
| 250 | 244 | ||
| 251 | case Source::Previous: | 245 | case Source::Previous: |
| 252 | return combiner_output.rgb(); | 246 | return combiner_output; |
| 253 | 247 | ||
| 254 | default: | 248 | default: |
| 255 | LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); | 249 | LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); |
| 250 | _dbg_assert_(HW_GPU, 0); | ||
| 256 | return {}; | 251 | return {}; |
| 257 | } | 252 | } |
| 258 | }; | 253 | }; |
| @@ -263,7 +258,13 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 263 | return primary_color.a(); | 258 | return primary_color.a(); |
| 264 | 259 | ||
| 265 | case Source::Texture0: | 260 | case Source::Texture0: |
| 266 | return texture_color.a(); | 261 | return texture_color[0].a(); |
| 262 | |||
| 263 | case Source::Texture1: | ||
| 264 | return texture_color[1].a(); | ||
| 265 | |||
| 266 | case Source::Texture2: | ||
| 267 | return texture_color[2].a(); | ||
| 267 | 268 | ||
| 268 | case Source::Constant: | 269 | case Source::Constant: |
| 269 | return tev_stage.const_a; | 270 | return tev_stage.const_a; |
| @@ -273,17 +274,23 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 273 | 274 | ||
| 274 | default: | 275 | default: |
| 275 | LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source); | 276 | LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source); |
| 277 | _dbg_assert_(HW_GPU, 0); | ||
| 276 | return 0; | 278 | return 0; |
| 277 | } | 279 | } |
| 278 | }; | 280 | }; |
| 279 | 281 | ||
| 280 | auto GetColorModifier = [](ColorModifier factor, const Math::Vec3<u8>& values) -> Math::Vec3<u8> { | 282 | auto GetColorModifier = [](ColorModifier factor, const Math::Vec4<u8>& values) -> Math::Vec3<u8> { |
| 281 | switch (factor) | 283 | switch (factor) |
| 282 | { | 284 | { |
| 283 | case ColorModifier::SourceColor: | 285 | case ColorModifier::SourceColor: |
| 284 | return values; | 286 | return values.rgb(); |
| 287 | |||
| 288 | case ColorModifier::SourceAlpha: | ||
| 289 | return { values.a(), values.a(), values.a() }; | ||
| 290 | |||
| 285 | default: | 291 | default: |
| 286 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); | 292 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); |
| 293 | _dbg_assert_(HW_GPU, 0); | ||
| 287 | return {}; | 294 | return {}; |
| 288 | } | 295 | } |
| 289 | }; | 296 | }; |
| @@ -292,8 +299,13 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 292 | switch (factor) { | 299 | switch (factor) { |
| 293 | case AlphaModifier::SourceAlpha: | 300 | case AlphaModifier::SourceAlpha: |
| 294 | return value; | 301 | return value; |
| 302 | |||
| 303 | case AlphaModifier::OneMinusSourceAlpha: | ||
| 304 | return 255 - value; | ||
| 305 | |||
| 295 | default: | 306 | default: |
| 296 | LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); | 307 | LOG_ERROR(HW_GPU, "Unknown alpha factor %d\n", (int)factor); |
| 308 | _dbg_assert_(HW_GPU, 0); | ||
| 297 | return 0; | 309 | return 0; |
| 298 | } | 310 | } |
| 299 | }; | 311 | }; |
| @@ -306,8 +318,21 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 306 | case Operation::Modulate: | 318 | case Operation::Modulate: |
| 307 | return ((input[0] * input[1]) / 255).Cast<u8>(); | 319 | return ((input[0] * input[1]) / 255).Cast<u8>(); |
| 308 | 320 | ||
| 321 | case Operation::Add: | ||
| 322 | { | ||
| 323 | auto result = input[0] + input[1]; | ||
| 324 | result.r() = std::min(255, result.r()); | ||
| 325 | result.g() = std::min(255, result.g()); | ||
| 326 | result.b() = std::min(255, result.b()); | ||
| 327 | return result.Cast<u8>(); | ||
| 328 | } | ||
| 329 | |||
| 330 | case Operation::Lerp: | ||
| 331 | return ((input[0] * input[2] + input[1] * (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) / 255).Cast<u8>(); | ||
| 332 | |||
| 309 | default: | 333 | default: |
| 310 | LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); | 334 | LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); |
| 335 | _dbg_assert_(HW_GPU, 0); | ||
| 311 | return {}; | 336 | return {}; |
| 312 | } | 337 | } |
| 313 | }; | 338 | }; |
| @@ -320,8 +345,15 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 320 | case Operation::Modulate: | 345 | case Operation::Modulate: |
| 321 | return input[0] * input[1] / 255; | 346 | return input[0] * input[1] / 255; |
| 322 | 347 | ||
| 348 | case Operation::Add: | ||
| 349 | return std::min(255, input[0] + input[1]); | ||
| 350 | |||
| 351 | case Operation::Lerp: | ||
| 352 | return (input[0] * input[2] + input[1] * (255 - input[2])) / 255; | ||
| 353 | |||
| 323 | default: | 354 | default: |
| 324 | LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); | 355 | LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); |
| 356 | _dbg_assert_(HW_GPU, 0); | ||
| 325 | return 0; | 357 | return 0; |
| 326 | } | 358 | } |
| 327 | }; | 359 | }; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e2caeeb8f..e20d7adb7 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -240,14 +240,14 @@ MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() { | |||
| 240 | MathUtil::Rectangle<unsigned> viewport_extent; | 240 | MathUtil::Rectangle<unsigned> viewport_extent; |
| 241 | if (window_aspect_ratio > emulation_aspect_ratio) { | 241 | if (window_aspect_ratio > emulation_aspect_ratio) { |
| 242 | // Window is narrower than the emulation content => apply borders to the top and bottom | 242 | // Window is narrower than the emulation content => apply borders to the top and bottom |
| 243 | unsigned viewport_height = std::round(emulation_aspect_ratio * framebuffer_width); | 243 | unsigned viewport_height = static_cast<unsigned>(std::round(emulation_aspect_ratio * framebuffer_width)); |
| 244 | viewport_extent.left = 0; | 244 | viewport_extent.left = 0; |
| 245 | viewport_extent.top = (framebuffer_height - viewport_height) / 2; | 245 | viewport_extent.top = (framebuffer_height - viewport_height) / 2; |
| 246 | viewport_extent.right = viewport_extent.left + framebuffer_width; | 246 | viewport_extent.right = viewport_extent.left + framebuffer_width; |
| 247 | viewport_extent.bottom = viewport_extent.top + viewport_height; | 247 | viewport_extent.bottom = viewport_extent.top + viewport_height; |
| 248 | } else { | 248 | } else { |
| 249 | // Otherwise, apply borders to the left and right sides of the window. | 249 | // Otherwise, apply borders to the left and right sides of the window. |
| 250 | unsigned viewport_width = std::round(framebuffer_height / emulation_aspect_ratio); | 250 | unsigned viewport_width = static_cast<unsigned>(std::round(framebuffer_height / emulation_aspect_ratio)); |
| 251 | viewport_extent.left = (framebuffer_width - viewport_width) / 2; | 251 | viewport_extent.left = (framebuffer_width - viewport_width) / 2; |
| 252 | viewport_extent.top = 0; | 252 | viewport_extent.top = 0; |
| 253 | viewport_extent.right = viewport_extent.left + viewport_width; | 253 | viewport_extent.right = viewport_extent.left + viewport_width; |
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp index 477e78cfe..4ba69fa51 100644 --- a/src/video_core/vertex_shader.cpp +++ b/src/video_core/vertex_shader.cpp | |||
| @@ -2,16 +2,25 @@ | |||
| 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 <stack> | ||
| 6 | |||
| 5 | #include <boost/range/algorithm.hpp> | 7 | #include <boost/range/algorithm.hpp> |
| 6 | 8 | ||
| 7 | #include <common/file_util.h> | 9 | #include <common/file_util.h> |
| 8 | 10 | ||
| 9 | #include <core/mem_map.h> | 11 | #include <core/mem_map.h> |
| 10 | 12 | ||
| 11 | #include "debug_utils/debug_utils.h" | 13 | #include <nihstro/shader_bytecode.h> |
| 14 | |||
| 12 | 15 | ||
| 13 | #include "pica.h" | 16 | #include "pica.h" |
| 14 | #include "vertex_shader.h" | 17 | #include "vertex_shader.h" |
| 18 | #include "debug_utils/debug_utils.h" | ||
| 19 | |||
| 20 | using nihstro::Instruction; | ||
| 21 | using nihstro::RegisterType; | ||
| 22 | using nihstro::SourceRegister; | ||
| 23 | using nihstro::SwizzlePattern; | ||
| 15 | 24 | ||
| 16 | namespace Pica { | 25 | namespace Pica { |
| 17 | 26 | ||
| @@ -19,13 +28,14 @@ namespace VertexShader { | |||
| 19 | 28 | ||
| 20 | static struct { | 29 | static struct { |
| 21 | Math::Vec4<float24> f[96]; | 30 | Math::Vec4<float24> f[96]; |
| 22 | } shader_uniforms; | ||
| 23 | 31 | ||
| 32 | std::array<bool,16> b; | ||
| 33 | } shader_uniforms; | ||
| 24 | 34 | ||
| 25 | // TODO: Not sure where the shader binary and swizzle patterns are supposed to be loaded to! | 35 | // TODO: Not sure where the shader binary and swizzle patterns are supposed to be loaded to! |
| 26 | // For now, we just keep these local arrays around. | 36 | // For now, we just keep these local arrays around. |
| 27 | static u32 shader_memory[1024]; | 37 | static std::array<u32, 1024> shader_memory; |
| 28 | static u32 swizzle_data[1024]; | 38 | static std::array<u32, 1024> swizzle_data; |
| 29 | 39 | ||
| 30 | void SubmitShaderMemoryChange(u32 addr, u32 value) | 40 | void SubmitShaderMemoryChange(u32 addr, u32 value) |
| 31 | { | 41 | { |
| @@ -42,6 +52,21 @@ Math::Vec4<float24>& GetFloatUniform(u32 index) | |||
| 42 | return shader_uniforms.f[index]; | 52 | return shader_uniforms.f[index]; |
| 43 | } | 53 | } |
| 44 | 54 | ||
| 55 | bool& GetBoolUniform(u32 index) | ||
| 56 | { | ||
| 57 | return shader_uniforms.b[index]; | ||
| 58 | } | ||
| 59 | |||
| 60 | const std::array<u32, 1024>& GetShaderBinary() | ||
| 61 | { | ||
| 62 | return shader_memory; | ||
| 63 | } | ||
| 64 | |||
| 65 | const std::array<u32, 1024>& GetSwizzlePatterns() | ||
| 66 | { | ||
| 67 | return swizzle_data; | ||
| 68 | } | ||
| 69 | |||
| 45 | struct VertexShaderState { | 70 | struct VertexShaderState { |
| 46 | u32* program_counter; | 71 | u32* program_counter; |
| 47 | 72 | ||
| @@ -49,13 +74,23 @@ struct VertexShaderState { | |||
| 49 | float24* output_register_table[7*4]; | 74 | float24* output_register_table[7*4]; |
| 50 | 75 | ||
| 51 | Math::Vec4<float24> temporary_registers[16]; | 76 | Math::Vec4<float24> temporary_registers[16]; |
| 52 | bool status_registers[2]; | 77 | bool conditional_code[2]; |
| 78 | |||
| 79 | // Two Address registers and one loop counter | ||
| 80 | // TODO: How many bits do these actually have? | ||
| 81 | s32 address_registers[3]; | ||
| 53 | 82 | ||
| 54 | enum { | 83 | enum { |
| 55 | INVALID_ADDRESS = 0xFFFFFFFF | 84 | INVALID_ADDRESS = 0xFFFFFFFF |
| 56 | }; | 85 | }; |
| 57 | u32 call_stack[8]; // TODO: What is the maximal call stack depth? | 86 | |
| 58 | u32* call_stack_pointer; | 87 | struct CallStackElement { |
| 88 | u32 final_address; | ||
| 89 | u32 return_address; | ||
| 90 | }; | ||
| 91 | |||
| 92 | // TODO: Is there a maximal size for this? | ||
| 93 | std::stack<CallStackElement> call_stack; | ||
| 59 | 94 | ||
| 60 | struct { | 95 | struct { |
| 61 | u32 max_offset; // maximum program counter ever reached | 96 | u32 max_offset; // maximum program counter ever reached |
| @@ -64,49 +99,105 @@ struct VertexShaderState { | |||
| 64 | }; | 99 | }; |
| 65 | 100 | ||
| 66 | static void ProcessShaderCode(VertexShaderState& state) { | 101 | static void ProcessShaderCode(VertexShaderState& state) { |
| 102 | |||
| 103 | // Placeholder for invalid inputs | ||
| 104 | static float24 dummy_vec4_float24[4]; | ||
| 105 | |||
| 67 | while (true) { | 106 | while (true) { |
| 68 | bool increment_pc = true; | 107 | if (!state.call_stack.empty()) { |
| 108 | if (state.program_counter - shader_memory.data() == state.call_stack.top().final_address) { | ||
| 109 | state.program_counter = &shader_memory[state.call_stack.top().return_address]; | ||
| 110 | state.call_stack.pop(); | ||
| 111 | |||
| 112 | // TODO: Is "trying again" accurate to hardware? | ||
| 113 | continue; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 69 | bool exit_loop = false; | 117 | bool exit_loop = false; |
| 70 | const Instruction& instr = *(const Instruction*)state.program_counter; | 118 | const Instruction& instr = *(const Instruction*)state.program_counter; |
| 71 | state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + (state.program_counter - shader_memory)); | ||
| 72 | |||
| 73 | const float24* src1_ = (instr.common.src1 < 0x10) ? state.input_register_table[instr.common.src1.GetIndex()] | ||
| 74 | : (instr.common.src1 < 0x20) ? &state.temporary_registers[instr.common.src1.GetIndex()].x | ||
| 75 | : (instr.common.src1 < 0x80) ? &shader_uniforms.f[instr.common.src1.GetIndex()].x | ||
| 76 | : nullptr; | ||
| 77 | const float24* src2_ = (instr.common.src2 < 0x10) ? state.input_register_table[instr.common.src2.GetIndex()] | ||
| 78 | : &state.temporary_registers[instr.common.src2.GetIndex()].x; | ||
| 79 | float24* dest = (instr.common.dest < 0x08) ? state.output_register_table[4*instr.common.dest.GetIndex()] | ||
| 80 | : (instr.common.dest < 0x10) ? nullptr | ||
| 81 | : (instr.common.dest < 0x20) ? &state.temporary_registers[instr.common.dest.GetIndex()][0] | ||
| 82 | : nullptr; | ||
| 83 | |||
| 84 | const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; | 119 | const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.common.operand_desc_id]; |
| 85 | const bool negate_src1 = (swizzle.negate != 0); | ||
| 86 | 120 | ||
| 87 | float24 src1[4] = { | 121 | auto call = [&](VertexShaderState& state, u32 offset, u32 num_instructions, u32 return_offset) { |
| 88 | src1_[(int)swizzle.GetSelectorSrc1(0)], | 122 | state.program_counter = &shader_memory[offset] - 1; // -1 to make sure when incrementing the PC we end up at the correct offset |
| 89 | src1_[(int)swizzle.GetSelectorSrc1(1)], | 123 | state.call_stack.push({ offset + num_instructions, return_offset }); |
| 90 | src1_[(int)swizzle.GetSelectorSrc1(2)], | ||
| 91 | src1_[(int)swizzle.GetSelectorSrc1(3)], | ||
| 92 | }; | 124 | }; |
| 93 | if (negate_src1) { | 125 | u32 binary_offset = state.program_counter - shader_memory.data(); |
| 94 | src1[0] = src1[0] * float24::FromFloat32(-1); | 126 | |
| 95 | src1[1] = src1[1] * float24::FromFloat32(-1); | 127 | state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + binary_offset); |
| 96 | src1[2] = src1[2] * float24::FromFloat32(-1); | 128 | |
| 97 | src1[3] = src1[3] * float24::FromFloat32(-1); | 129 | auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { |
| 98 | } | 130 | switch (source_reg.GetRegisterType()) { |
| 99 | const float24 src2[4] = { | 131 | case RegisterType::Input: |
| 100 | src2_[(int)swizzle.GetSelectorSrc2(0)], | 132 | return state.input_register_table[source_reg.GetIndex()]; |
| 101 | src2_[(int)swizzle.GetSelectorSrc2(1)], | 133 | |
| 102 | src2_[(int)swizzle.GetSelectorSrc2(2)], | 134 | case RegisterType::Temporary: |
| 103 | src2_[(int)swizzle.GetSelectorSrc2(3)], | 135 | return &state.temporary_registers[source_reg.GetIndex()].x; |
| 136 | |||
| 137 | case RegisterType::FloatUniform: | ||
| 138 | return &shader_uniforms.f[source_reg.GetIndex()].x; | ||
| 139 | |||
| 140 | default: | ||
| 141 | return dummy_vec4_float24; | ||
| 142 | } | ||
| 104 | }; | 143 | }; |
| 105 | 144 | ||
| 106 | switch (instr.opcode) { | 145 | switch (instr.opcode.GetInfo().type) { |
| 146 | case Instruction::OpCodeType::Arithmetic: | ||
| 147 | { | ||
| 148 | bool is_inverted = 0 != (instr.opcode.GetInfo().subtype & Instruction::OpCodeInfo::SrcInversed); | ||
| 149 | if (is_inverted) { | ||
| 150 | // TODO: We don't really support this properly: For instance, the address register | ||
| 151 | // offset needs to be applied to SRC2 instead, etc. | ||
| 152 | // For now, we just abort in this situation. | ||
| 153 | LOG_CRITICAL(HW_GPU, "Bad condition..."); | ||
| 154 | exit(0); | ||
| 155 | } | ||
| 156 | |||
| 157 | const int address_offset = (instr.common.address_register_index == 0) | ||
| 158 | ? 0 : state.address_registers[instr.common.address_register_index - 1]; | ||
| 159 | |||
| 160 | const float24* src1_ = LookupSourceRegister(instr.common.GetSrc1(is_inverted) + address_offset); | ||
| 161 | const float24* src2_ = LookupSourceRegister(instr.common.GetSrc2(is_inverted)); | ||
| 162 | |||
| 163 | const bool negate_src1 = (swizzle.negate_src1 != false); | ||
| 164 | const bool negate_src2 = (swizzle.negate_src2 != false); | ||
| 165 | |||
| 166 | float24 src1[4] = { | ||
| 167 | src1_[(int)swizzle.GetSelectorSrc1(0)], | ||
| 168 | src1_[(int)swizzle.GetSelectorSrc1(1)], | ||
| 169 | src1_[(int)swizzle.GetSelectorSrc1(2)], | ||
| 170 | src1_[(int)swizzle.GetSelectorSrc1(3)], | ||
| 171 | }; | ||
| 172 | if (negate_src1) { | ||
| 173 | src1[0] = src1[0] * float24::FromFloat32(-1); | ||
| 174 | src1[1] = src1[1] * float24::FromFloat32(-1); | ||
| 175 | src1[2] = src1[2] * float24::FromFloat32(-1); | ||
| 176 | src1[3] = src1[3] * float24::FromFloat32(-1); | ||
| 177 | } | ||
| 178 | float24 src2[4] = { | ||
| 179 | src2_[(int)swizzle.GetSelectorSrc2(0)], | ||
| 180 | src2_[(int)swizzle.GetSelectorSrc2(1)], | ||
| 181 | src2_[(int)swizzle.GetSelectorSrc2(2)], | ||
| 182 | src2_[(int)swizzle.GetSelectorSrc2(3)], | ||
| 183 | }; | ||
| 184 | if (negate_src2) { | ||
| 185 | src2[0] = src2[0] * float24::FromFloat32(-1); | ||
| 186 | src2[1] = src2[1] * float24::FromFloat32(-1); | ||
| 187 | src2[2] = src2[2] * float24::FromFloat32(-1); | ||
| 188 | src2[3] = src2[3] * float24::FromFloat32(-1); | ||
| 189 | } | ||
| 190 | |||
| 191 | float24* dest = (instr.common.dest < 0x08) ? state.output_register_table[4*instr.common.dest.GetIndex()] | ||
| 192 | : (instr.common.dest < 0x10) ? dummy_vec4_float24 | ||
| 193 | : (instr.common.dest < 0x20) ? &state.temporary_registers[instr.common.dest.GetIndex()][0] | ||
| 194 | : dummy_vec4_float24; | ||
| 195 | |||
| 196 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 197 | |||
| 198 | switch (instr.opcode.EffectiveOpCode()) { | ||
| 107 | case Instruction::OpCode::ADD: | 199 | case Instruction::OpCode::ADD: |
| 108 | { | 200 | { |
| 109 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 110 | for (int i = 0; i < 4; ++i) { | 201 | for (int i = 0; i < 4; ++i) { |
| 111 | if (!swizzle.DestComponentEnabled(i)) | 202 | if (!swizzle.DestComponentEnabled(i)) |
| 112 | continue; | 203 | continue; |
| @@ -119,7 +210,6 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 119 | 210 | ||
| 120 | case Instruction::OpCode::MUL: | 211 | case Instruction::OpCode::MUL: |
| 121 | { | 212 | { |
| 122 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 123 | for (int i = 0; i < 4; ++i) { | 213 | for (int i = 0; i < 4; ++i) { |
| 124 | if (!swizzle.DestComponentEnabled(i)) | 214 | if (!swizzle.DestComponentEnabled(i)) |
| 125 | continue; | 215 | continue; |
| @@ -130,10 +220,18 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 130 | break; | 220 | break; |
| 131 | } | 221 | } |
| 132 | 222 | ||
| 223 | case Instruction::OpCode::MAX: | ||
| 224 | for (int i = 0; i < 4; ++i) { | ||
| 225 | if (!swizzle.DestComponentEnabled(i)) | ||
| 226 | continue; | ||
| 227 | |||
| 228 | dest[i] = std::max(src1[i], src2[i]); | ||
| 229 | } | ||
| 230 | break; | ||
| 231 | |||
| 133 | case Instruction::OpCode::DP3: | 232 | case Instruction::OpCode::DP3: |
| 134 | case Instruction::OpCode::DP4: | 233 | case Instruction::OpCode::DP4: |
| 135 | { | 234 | { |
| 136 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 137 | float24 dot = float24::FromFloat32(0.f); | 235 | float24 dot = float24::FromFloat32(0.f); |
| 138 | int num_components = (instr.opcode == Instruction::OpCode::DP3) ? 3 : 4; | 236 | int num_components = (instr.opcode == Instruction::OpCode::DP3) ? 3 : 4; |
| 139 | for (int i = 0; i < num_components; ++i) | 237 | for (int i = 0; i < num_components; ++i) |
| @@ -151,7 +249,6 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 151 | // Reciprocal | 249 | // Reciprocal |
| 152 | case Instruction::OpCode::RCP: | 250 | case Instruction::OpCode::RCP: |
| 153 | { | 251 | { |
| 154 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 155 | for (int i = 0; i < 4; ++i) { | 252 | for (int i = 0; i < 4; ++i) { |
| 156 | if (!swizzle.DestComponentEnabled(i)) | 253 | if (!swizzle.DestComponentEnabled(i)) |
| 157 | continue; | 254 | continue; |
| @@ -167,7 +264,6 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 167 | // Reciprocal Square Root | 264 | // Reciprocal Square Root |
| 168 | case Instruction::OpCode::RSQ: | 265 | case Instruction::OpCode::RSQ: |
| 169 | { | 266 | { |
| 170 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 171 | for (int i = 0; i < 4; ++i) { | 267 | for (int i = 0; i < 4; ++i) { |
| 172 | if (!swizzle.DestComponentEnabled(i)) | 268 | if (!swizzle.DestComponentEnabled(i)) |
| 173 | continue; | 269 | continue; |
| @@ -180,9 +276,21 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 180 | break; | 276 | break; |
| 181 | } | 277 | } |
| 182 | 278 | ||
| 279 | case Instruction::OpCode::MOVA: | ||
| 280 | { | ||
| 281 | for (int i = 0; i < 2; ++i) { | ||
| 282 | if (!swizzle.DestComponentEnabled(i)) | ||
| 283 | continue; | ||
| 284 | |||
| 285 | // TODO: Figure out how the rounding is done on hardware | ||
| 286 | state.address_registers[i] = static_cast<s32>(src1[i].ToFloat32()); | ||
| 287 | } | ||
| 288 | |||
| 289 | break; | ||
| 290 | } | ||
| 291 | |||
| 183 | case Instruction::OpCode::MOV: | 292 | case Instruction::OpCode::MOV: |
| 184 | { | 293 | { |
| 185 | state.debug.max_opdesc_id = std::max<u32>(state.debug.max_opdesc_id, 1+instr.common.operand_desc_id); | ||
| 186 | for (int i = 0; i < 4; ++i) { | 294 | for (int i = 0; i < 4; ++i) { |
| 187 | if (!swizzle.DestComponentEnabled(i)) | 295 | if (!swizzle.DestComponentEnabled(i)) |
| 188 | continue; | 296 | continue; |
| @@ -192,39 +300,137 @@ static void ProcessShaderCode(VertexShaderState& state) { | |||
| 192 | break; | 300 | break; |
| 193 | } | 301 | } |
| 194 | 302 | ||
| 195 | case Instruction::OpCode::RET: | 303 | case Instruction::OpCode::CMP: |
| 196 | if (*state.call_stack_pointer == VertexShaderState::INVALID_ADDRESS) { | 304 | for (int i = 0; i < 2; ++i) { |
| 197 | exit_loop = true; | 305 | // TODO: Can you restrict to one compare via dest masking? |
| 198 | } else { | 306 | |
| 199 | // Jump back to call stack position, invalidate call stack entry, move up call stack pointer | 307 | auto compare_op = instr.common.compare_op; |
| 200 | state.program_counter = &shader_memory[*state.call_stack_pointer]; | 308 | auto op = (i == 0) ? compare_op.x.Value() : compare_op.y.Value(); |
| 201 | *state.call_stack_pointer-- = VertexShaderState::INVALID_ADDRESS; | 309 | |
| 310 | switch (op) { | ||
| 311 | case compare_op.Equal: | ||
| 312 | state.conditional_code[i] = (src1[i] == src2[i]); | ||
| 313 | break; | ||
| 314 | |||
| 315 | case compare_op.NotEqual: | ||
| 316 | state.conditional_code[i] = (src1[i] != src2[i]); | ||
| 317 | break; | ||
| 318 | |||
| 319 | case compare_op.LessThan: | ||
| 320 | state.conditional_code[i] = (src1[i] < src2[i]); | ||
| 321 | break; | ||
| 322 | |||
| 323 | case compare_op.LessEqual: | ||
| 324 | state.conditional_code[i] = (src1[i] <= src2[i]); | ||
| 325 | break; | ||
| 326 | |||
| 327 | case compare_op.GreaterThan: | ||
| 328 | state.conditional_code[i] = (src1[i] > src2[i]); | ||
| 329 | break; | ||
| 330 | |||
| 331 | case compare_op.GreaterEqual: | ||
| 332 | state.conditional_code[i] = (src1[i] >= src2[i]); | ||
| 333 | break; | ||
| 334 | |||
| 335 | default: | ||
| 336 | LOG_ERROR(HW_GPU, "Unknown compare mode %x", static_cast<int>(op)); | ||
| 337 | break; | ||
| 338 | } | ||
| 202 | } | 339 | } |
| 340 | break; | ||
| 203 | 341 | ||
| 342 | default: | ||
| 343 | LOG_ERROR(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x", | ||
| 344 | (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex); | ||
| 345 | _dbg_assert_(HW_GPU, 0); | ||
| 346 | break; | ||
| 347 | } | ||
| 348 | |||
| 349 | break; | ||
| 350 | } | ||
| 351 | default: | ||
| 352 | // Handle each instruction on its own | ||
| 353 | switch (instr.opcode) { | ||
| 354 | case Instruction::OpCode::END: | ||
| 355 | exit_loop = true; | ||
| 204 | break; | 356 | break; |
| 205 | 357 | ||
| 206 | case Instruction::OpCode::CALL: | 358 | case Instruction::OpCode::CALL: |
| 207 | increment_pc = false; | 359 | call(state, |
| 360 | instr.flow_control.dest_offset, | ||
| 361 | instr.flow_control.num_instructions, | ||
| 362 | binary_offset + 1); | ||
| 363 | break; | ||
| 364 | |||
| 365 | case Instruction::OpCode::NOP: | ||
| 366 | break; | ||
| 208 | 367 | ||
| 209 | _dbg_assert_(HW_GPU, state.call_stack_pointer - state.call_stack < sizeof(state.call_stack)); | 368 | case Instruction::OpCode::IFU: |
| 369 | if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) { | ||
| 370 | call(state, | ||
| 371 | binary_offset + 1, | ||
| 372 | instr.flow_control.dest_offset - binary_offset - 1, | ||
| 373 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 374 | } else { | ||
| 375 | call(state, | ||
| 376 | instr.flow_control.dest_offset, | ||
| 377 | instr.flow_control.num_instructions, | ||
| 378 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 379 | } | ||
| 210 | 380 | ||
| 211 | *++state.call_stack_pointer = state.program_counter - shader_memory; | ||
| 212 | // TODO: Does this offset refer to the beginning of shader memory? | ||
| 213 | state.program_counter = &shader_memory[instr.flow_control.offset_words]; | ||
| 214 | break; | 381 | break; |
| 215 | 382 | ||
| 216 | case Instruction::OpCode::FLS: | 383 | case Instruction::OpCode::IFC: |
| 217 | // TODO: Do whatever needs to be done here? | 384 | { |
| 385 | // TODO: Do we need to consider swizzlers here? | ||
| 386 | |||
| 387 | auto flow_control = instr.flow_control; | ||
| 388 | bool results[3] = { flow_control.refx == state.conditional_code[0], | ||
| 389 | flow_control.refy == state.conditional_code[1] }; | ||
| 390 | |||
| 391 | switch (flow_control.op) { | ||
| 392 | case flow_control.Or: | ||
| 393 | results[2] = results[0] || results[1]; | ||
| 394 | break; | ||
| 395 | |||
| 396 | case flow_control.And: | ||
| 397 | results[2] = results[0] && results[1]; | ||
| 398 | break; | ||
| 399 | |||
| 400 | case flow_control.JustX: | ||
| 401 | results[2] = results[0]; | ||
| 402 | break; | ||
| 403 | |||
| 404 | case flow_control.JustY: | ||
| 405 | results[2] = results[1]; | ||
| 406 | break; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (results[2]) { | ||
| 410 | call(state, | ||
| 411 | binary_offset + 1, | ||
| 412 | instr.flow_control.dest_offset - binary_offset - 1, | ||
| 413 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 414 | } else { | ||
| 415 | call(state, | ||
| 416 | instr.flow_control.dest_offset, | ||
| 417 | instr.flow_control.num_instructions, | ||
| 418 | instr.flow_control.dest_offset + instr.flow_control.num_instructions); | ||
| 419 | } | ||
| 420 | |||
| 218 | break; | 421 | break; |
| 422 | } | ||
| 219 | 423 | ||
| 220 | default: | 424 | default: |
| 221 | LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", | 425 | LOG_ERROR(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", |
| 222 | (int)instr.opcode.Value(), instr.GetOpCodeName().c_str(), instr.hex); | 426 | (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex); |
| 223 | break; | 427 | break; |
| 428 | } | ||
| 429 | |||
| 430 | break; | ||
| 224 | } | 431 | } |
| 225 | 432 | ||
| 226 | if (increment_pc) | 433 | ++state.program_counter; |
| 227 | ++state.program_counter; | ||
| 228 | 434 | ||
| 229 | if (exit_loop) | 435 | if (exit_loop) |
| 230 | break; | 436 | break; |
| @@ -275,13 +481,11 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes) | |||
| 275 | state.output_register_table[4*i+comp] = ((float24*)&ret) + semantics[comp]; | 481 | state.output_register_table[4*i+comp] = ((float24*)&ret) + semantics[comp]; |
| 276 | } | 482 | } |
| 277 | 483 | ||
| 278 | state.status_registers[0] = false; | 484 | state.conditional_code[0] = false; |
| 279 | state.status_registers[1] = false; | 485 | state.conditional_code[1] = false; |
| 280 | boost::fill(state.call_stack, VertexShaderState::INVALID_ADDRESS); | ||
| 281 | state.call_stack_pointer = &state.call_stack[0]; | ||
| 282 | 486 | ||
| 283 | ProcessShaderCode(state); | 487 | ProcessShaderCode(state); |
| 284 | DebugUtils::DumpShader(shader_memory, state.debug.max_offset, swizzle_data, | 488 | DebugUtils::DumpShader(shader_memory.data(), state.debug.max_offset, swizzle_data.data(), |
| 285 | state.debug.max_opdesc_id, registers.vs_main_offset, | 489 | state.debug.max_opdesc_id, registers.vs_main_offset, |
| 286 | registers.vs_output_attributes); | 490 | registers.vs_output_attributes); |
| 287 | 491 | ||
diff --git a/src/video_core/vertex_shader.h b/src/video_core/vertex_shader.h index bfb6fb6e3..047dde046 100644 --- a/src/video_core/vertex_shader.h +++ b/src/video_core/vertex_shader.h | |||
| @@ -27,15 +27,18 @@ struct OutputVertex { | |||
| 27 | Math::Vec4<float24> dummy; // quaternions (not implemented, yet) | 27 | Math::Vec4<float24> dummy; // quaternions (not implemented, yet) |
| 28 | Math::Vec4<float24> color; | 28 | Math::Vec4<float24> color; |
| 29 | Math::Vec2<float24> tc0; | 29 | Math::Vec2<float24> tc0; |
| 30 | Math::Vec2<float24> tc1; | ||
| 31 | float24 pad[6]; | ||
| 32 | Math::Vec2<float24> tc2; | ||
| 30 | 33 | ||
| 31 | // Padding for optimal alignment | 34 | // Padding for optimal alignment |
| 32 | float24 pad[14]; | 35 | float24 pad2[4]; |
| 33 | 36 | ||
| 34 | // Attributes used to store intermediate results | 37 | // Attributes used to store intermediate results |
| 35 | 38 | ||
| 36 | // position after perspective divide | 39 | // position after perspective divide |
| 37 | Math::Vec3<float24> screenpos; | 40 | Math::Vec3<float24> screenpos; |
| 38 | float24 pad2; | 41 | float24 pad3; |
| 39 | 42 | ||
| 40 | // Linear interpolation | 43 | // Linear interpolation |
| 41 | // factor: 0=this, 1=vtx | 44 | // factor: 0=this, 1=vtx |
| @@ -44,6 +47,8 @@ struct OutputVertex { | |||
| 44 | 47 | ||
| 45 | // TODO: Should perform perspective correct interpolation here... | 48 | // TODO: Should perform perspective correct interpolation here... |
| 46 | tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor); | 49 | tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor); |
| 50 | tc1 = tc1 * factor + vtx.tc1 * (float24::FromFloat32(1) - factor); | ||
| 51 | tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor); | ||
| 47 | 52 | ||
| 48 | screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor); | 53 | screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor); |
| 49 | 54 | ||
| @@ -61,222 +66,16 @@ struct OutputVertex { | |||
| 61 | static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); | 66 | static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD"); |
| 62 | static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); | 67 | static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size"); |
| 63 | 68 | ||
| 64 | union Instruction { | ||
| 65 | enum class OpCode : u32 { | ||
| 66 | ADD = 0x0, | ||
| 67 | DP3 = 0x1, | ||
| 68 | DP4 = 0x2, | ||
| 69 | |||
| 70 | MUL = 0x8, | ||
| 71 | |||
| 72 | MAX = 0xC, | ||
| 73 | MIN = 0xD, | ||
| 74 | RCP = 0xE, | ||
| 75 | RSQ = 0xF, | ||
| 76 | |||
| 77 | MOV = 0x13, | ||
| 78 | |||
| 79 | RET = 0x21, | ||
| 80 | FLS = 0x22, // Flush | ||
| 81 | CALL = 0x24, | ||
| 82 | }; | ||
| 83 | |||
| 84 | std::string GetOpCodeName() const { | ||
| 85 | std::map<OpCode, std::string> map = { | ||
| 86 | { OpCode::ADD, "ADD" }, | ||
| 87 | { OpCode::DP3, "DP3" }, | ||
| 88 | { OpCode::DP4, "DP4" }, | ||
| 89 | { OpCode::MUL, "MUL" }, | ||
| 90 | { OpCode::MAX, "MAX" }, | ||
| 91 | { OpCode::MIN, "MIN" }, | ||
| 92 | { OpCode::RCP, "RCP" }, | ||
| 93 | { OpCode::RSQ, "RSQ" }, | ||
| 94 | { OpCode::MOV, "MOV" }, | ||
| 95 | { OpCode::RET, "RET" }, | ||
| 96 | { OpCode::FLS, "FLS" }, | ||
| 97 | }; | ||
| 98 | auto it = map.find(opcode); | ||
| 99 | if (it == map.end()) | ||
| 100 | return "UNK"; | ||
| 101 | else | ||
| 102 | return it->second; | ||
| 103 | } | ||
| 104 | |||
| 105 | u32 hex; | ||
| 106 | |||
| 107 | BitField<0x1a, 0x6, OpCode> opcode; | ||
| 108 | |||
| 109 | // General notes: | ||
| 110 | // | ||
| 111 | // When two input registers are used, one of them uses a 5-bit index while the other | ||
| 112 | // one uses a 7-bit index. This is because at most one floating point uniform may be used | ||
| 113 | // as an input. | ||
| 114 | |||
| 115 | |||
| 116 | // Format used e.g. by arithmetic instructions and comparisons | ||
| 117 | // "src1" and "src2" specify register indices (i.e. indices referring to groups of 4 floats), | ||
| 118 | // while "dest" addresses individual floats. | ||
| 119 | union { | ||
| 120 | BitField<0x00, 0x5, u32> operand_desc_id; | ||
| 121 | |||
| 122 | template<class BitFieldType> | ||
| 123 | struct SourceRegister : BitFieldType { | ||
| 124 | enum RegisterType { | ||
| 125 | Input, | ||
| 126 | Temporary, | ||
| 127 | FloatUniform | ||
| 128 | }; | ||
| 129 | |||
| 130 | RegisterType GetRegisterType() const { | ||
| 131 | if (BitFieldType::Value() < 0x10) | ||
| 132 | return Input; | ||
| 133 | else if (BitFieldType::Value() < 0x20) | ||
| 134 | return Temporary; | ||
| 135 | else | ||
| 136 | return FloatUniform; | ||
| 137 | } | ||
| 138 | |||
| 139 | int GetIndex() const { | ||
| 140 | if (GetRegisterType() == Input) | ||
| 141 | return BitFieldType::Value(); | ||
| 142 | else if (GetRegisterType() == Temporary) | ||
| 143 | return BitFieldType::Value() - 0x10; | ||
| 144 | else // if (GetRegisterType() == FloatUniform) | ||
| 145 | return BitFieldType::Value() - 0x20; | ||
| 146 | } | ||
| 147 | |||
| 148 | std::string GetRegisterName() const { | ||
| 149 | std::map<RegisterType, std::string> type = { | ||
| 150 | { Input, "i" }, | ||
| 151 | { Temporary, "t" }, | ||
| 152 | { FloatUniform, "f" }, | ||
| 153 | }; | ||
| 154 | return type[GetRegisterType()] + std::to_string(GetIndex()); | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | |||
| 158 | SourceRegister<BitField<0x07, 0x5, u32>> src2; | ||
| 159 | SourceRegister<BitField<0x0c, 0x7, u32>> src1; | ||
| 160 | |||
| 161 | struct : BitField<0x15, 0x5, u32> | ||
| 162 | { | ||
| 163 | enum RegisterType { | ||
| 164 | Output, | ||
| 165 | Temporary, | ||
| 166 | Unknown | ||
| 167 | }; | ||
| 168 | RegisterType GetRegisterType() const { | ||
| 169 | if (Value() < 0x8) | ||
| 170 | return Output; | ||
| 171 | else if (Value() < 0x10) | ||
| 172 | return Unknown; | ||
| 173 | else | ||
| 174 | return Temporary; | ||
| 175 | } | ||
| 176 | int GetIndex() const { | ||
| 177 | if (GetRegisterType() == Output) | ||
| 178 | return Value(); | ||
| 179 | else if (GetRegisterType() == Temporary) | ||
| 180 | return Value() - 0x10; | ||
| 181 | else | ||
| 182 | return Value(); | ||
| 183 | } | ||
| 184 | std::string GetRegisterName() const { | ||
| 185 | std::map<RegisterType, std::string> type = { | ||
| 186 | { Output, "o" }, | ||
| 187 | { Temporary, "t" }, | ||
| 188 | { Unknown, "u" } | ||
| 189 | }; | ||
| 190 | return type[GetRegisterType()] + std::to_string(GetIndex()); | ||
| 191 | } | ||
| 192 | } dest; | ||
| 193 | } common; | ||
| 194 | |||
| 195 | // Format used for flow control instructions ("if") | ||
| 196 | union { | ||
| 197 | BitField<0x00, 0x8, u32> num_instructions; | ||
| 198 | BitField<0x0a, 0xc, u32> offset_words; | ||
| 199 | } flow_control; | ||
| 200 | }; | ||
| 201 | static_assert(std::is_standard_layout<Instruction>::value, "Structure is not using standard layout!"); | ||
| 202 | |||
| 203 | union SwizzlePattern { | ||
| 204 | u32 hex; | ||
| 205 | |||
| 206 | enum class Selector : u32 { | ||
| 207 | x = 0, | ||
| 208 | y = 1, | ||
| 209 | z = 2, | ||
| 210 | w = 3 | ||
| 211 | }; | ||
| 212 | |||
| 213 | Selector GetSelectorSrc1(int comp) const { | ||
| 214 | Selector selectors[] = { | ||
| 215 | src1_selector_0, src1_selector_1, src1_selector_2, src1_selector_3 | ||
| 216 | }; | ||
| 217 | return selectors[comp]; | ||
| 218 | } | ||
| 219 | |||
| 220 | Selector GetSelectorSrc2(int comp) const { | ||
| 221 | Selector selectors[] = { | ||
| 222 | src2_selector_0, src2_selector_1, src2_selector_2, src2_selector_3 | ||
| 223 | }; | ||
| 224 | return selectors[comp]; | ||
| 225 | } | ||
| 226 | |||
| 227 | bool DestComponentEnabled(int i) const { | ||
| 228 | return (dest_mask & (0x8 >> i)) != 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | std::string SelectorToString(bool src2) const { | ||
| 232 | std::map<Selector, std::string> map = { | ||
| 233 | { Selector::x, "x" }, | ||
| 234 | { Selector::y, "y" }, | ||
| 235 | { Selector::z, "z" }, | ||
| 236 | { Selector::w, "w" } | ||
| 237 | }; | ||
| 238 | std::string ret; | ||
| 239 | for (int i = 0; i < 4; ++i) { | ||
| 240 | ret += map.at(src2 ? GetSelectorSrc2(i) : GetSelectorSrc1(i)); | ||
| 241 | } | ||
| 242 | return ret; | ||
| 243 | } | ||
| 244 | |||
| 245 | std::string DestMaskToString() const { | ||
| 246 | std::string ret; | ||
| 247 | for (int i = 0; i < 4; ++i) { | ||
| 248 | if (!DestComponentEnabled(i)) | ||
| 249 | ret += "_"; | ||
| 250 | else | ||
| 251 | ret += "xyzw"[i]; | ||
| 252 | } | ||
| 253 | return ret; | ||
| 254 | } | ||
| 255 | |||
| 256 | // Components of "dest" that should be written to: LSB=dest.w, MSB=dest.x | ||
| 257 | BitField< 0, 4, u32> dest_mask; | ||
| 258 | |||
| 259 | BitField< 4, 1, u32> negate; // negates src1 | ||
| 260 | |||
| 261 | BitField< 5, 2, Selector> src1_selector_3; | ||
| 262 | BitField< 7, 2, Selector> src1_selector_2; | ||
| 263 | BitField< 9, 2, Selector> src1_selector_1; | ||
| 264 | BitField<11, 2, Selector> src1_selector_0; | ||
| 265 | |||
| 266 | BitField<14, 2, Selector> src2_selector_3; | ||
| 267 | BitField<16, 2, Selector> src2_selector_2; | ||
| 268 | BitField<18, 2, Selector> src2_selector_1; | ||
| 269 | BitField<20, 2, Selector> src2_selector_0; | ||
| 270 | |||
| 271 | BitField<31, 1, u32> flag; // not sure what this means, maybe it's the sign? | ||
| 272 | }; | ||
| 273 | |||
| 274 | void SubmitShaderMemoryChange(u32 addr, u32 value); | 69 | void SubmitShaderMemoryChange(u32 addr, u32 value); |
| 275 | void SubmitSwizzleDataChange(u32 addr, u32 value); | 70 | void SubmitSwizzleDataChange(u32 addr, u32 value); |
| 276 | 71 | ||
| 277 | OutputVertex RunShader(const InputVertex& input, int num_attributes); | 72 | OutputVertex RunShader(const InputVertex& input, int num_attributes); |
| 278 | 73 | ||
| 279 | Math::Vec4<float24>& GetFloatUniform(u32 index); | 74 | Math::Vec4<float24>& GetFloatUniform(u32 index); |
| 75 | bool& GetBoolUniform(u32 index); | ||
| 76 | |||
| 77 | const std::array<u32, 1024>& GetShaderBinary(); | ||
| 78 | const std::array<u32, 1024>& GetSwizzlePatterns(); | ||
| 280 | 79 | ||
| 281 | } // namespace | 80 | } // namespace |
| 282 | 81 | ||