mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge branch 'master' into CompatibiltyReporting
This commit is contained in:
		
						commit
						c3afd73592
					
				
					 75 changed files with 1527 additions and 745 deletions
				
			
		|  | @ -23,7 +23,7 @@ add_library(audio_core STATIC | |||
|     time_stretch.cpp | ||||
|     time_stretch.h | ||||
| 
 | ||||
|     $<$<BOOL:SDL2_FOUND>:sdl2_sink.cpp sdl2_sink.h> | ||||
|     $<$<BOOL:${SDL2_FOUND}>:sdl2_sink.cpp sdl2_sink.h> | ||||
| ) | ||||
| 
 | ||||
| create_target_directory_groups(audio_core) | ||||
|  |  | |||
|  | @ -11,10 +11,8 @@ | |||
| // This needs to be included before getopt.h because the latter #defines symbols used by it
 | ||||
| #include "common/microprofile.h" | ||||
| 
 | ||||
| #ifdef _MSC_VER | ||||
| #include <getopt.h> | ||||
| #else | ||||
| #include <getopt.h> | ||||
| #ifndef _MSC_VER | ||||
| #include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
|  | @ -157,13 +155,14 @@ int main(int argc, char** argv) { | |||
|                     errno = EINVAL; | ||||
|                 if (errno != 0) | ||||
|                     exit(1); | ||||
|                 break; | ||||
|             } | ||||
|             case 'm': { | ||||
|                 use_multiplayer = true; | ||||
|                 std::string str_arg(optarg); | ||||
|                 const std::string str_arg(optarg); | ||||
|                 // regex to check if the format is nickname:password@ip:port
 | ||||
|                 // with optional :password
 | ||||
|                 std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); | ||||
|                 const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); | ||||
|                 if (!std::regex_match(str_arg, re)) { | ||||
|                     std::cout << "Wrong format for option --multiplayer\n"; | ||||
|                     PrintHelp(argv[0]); | ||||
|  | @ -188,10 +187,6 @@ int main(int argc, char** argv) { | |||
|                     std::cout << "Address to room must not be empty.\n"; | ||||
|                     return 0; | ||||
|                 } | ||||
|                 if (port > 65535) { | ||||
|                     std::cout << "Port must be between 0 and 65535.\n"; | ||||
|                     return 0; | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case 'h': | ||||
|  |  | |||
|  | @ -76,8 +76,9 @@ void Config::ReadValues() { | |||
|             Settings::values.analogs[i] = default_param; | ||||
|     } | ||||
| 
 | ||||
|     Settings::values.motion_device = sdl2_config->Get( | ||||
|         "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); | ||||
|     Settings::values.motion_device = | ||||
|         sdl2_config->Get("Controls", "motion_device", | ||||
|                          "engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0"); | ||||
|     Settings::values.touch_device = | ||||
|         sdl2_config->Get("Controls", "touch_device", "engine:emu_window"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -89,19 +89,19 @@ EmuWindow_SDL2::EmuWindow_SDL2() { | |||
|                          SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | ||||
| 
 | ||||
|     if (render_window == nullptr) { | ||||
|         LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); | ||||
|         LOG_CRITICAL(Frontend, "Failed to create SDL2 window: %s", SDL_GetError()); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     gl_context = SDL_GL_CreateContext(render_window); | ||||
| 
 | ||||
|     if (gl_context == nullptr) { | ||||
|         LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting..."); | ||||
|         LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: %s", SDL_GetError()); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|     if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { | ||||
|         LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting..."); | ||||
|         LOG_CRITICAL(Frontend, "Failed to initialize GL functions: %s", SDL_GetError()); | ||||
|         exit(1); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -90,12 +90,46 @@ file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) | |||
| 
 | ||||
| qt5_wrap_ui(UI_HDRS ${UIS}) | ||||
| 
 | ||||
| if (ENABLE_QT_TRANSLATION) | ||||
|     set(CITRA_QT_LANGUAGES "${CMAKE_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") | ||||
|     option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) | ||||
| 
 | ||||
|     # Update source TS file if enabled | ||||
|     if (GENERATE_QT_TRANSLATION) | ||||
|         get_target_property(SRCS citra-qt SOURCES) | ||||
|         qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${CITRA_QT_LANGUAGES}/en.ts) | ||||
|         add_custom_target(translation ALL DEPENDS ${CITRA_QT_LANGUAGES}/en.ts) | ||||
|     endif() | ||||
| 
 | ||||
|     # Find all TS files except en.ts | ||||
|     file(GLOB_RECURSE LANGUAGES_TS ${CITRA_QT_LANGUAGES}/*.ts) | ||||
|     list(REMOVE_ITEM LANGUAGES_TS ${CITRA_QT_LANGUAGES}/en.ts) | ||||
| 
 | ||||
|     # Compile TS files to QM files | ||||
|     qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) | ||||
| 
 | ||||
|     # Build a QRC file from the QM file list | ||||
|     set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) | ||||
|     file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n") | ||||
|     foreach (QM ${LANGUAGES_QM}) | ||||
|         get_filename_component(QM_FILE ${QM} NAME) | ||||
|         file(APPEND ${LANGUAGES_QRC} "<file>${QM_FILE}</file>\n") | ||||
|     endforeach (QM) | ||||
|     file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") | ||||
| 
 | ||||
|     # Add the QRC file to package in all QM files | ||||
|     qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) | ||||
| else() | ||||
|     set(LANGUAGES) | ||||
| endif() | ||||
| 
 | ||||
| target_sources(citra-qt | ||||
|     PRIVATE | ||||
|         ${ICONS} | ||||
|         ${THEMES} | ||||
|         ${UI_HDRS} | ||||
|         ${UIS} | ||||
|         ${LANGUAGES} | ||||
| ) | ||||
| 
 | ||||
| if (APPLE) | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ void EmuThread::run() { | |||
| 
 | ||||
|             Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); | ||||
|             if (result != Core::System::ResultStatus::Success) { | ||||
|                 this->SetRunning(false); | ||||
|                 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); | ||||
|             } | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,7 +58,9 @@ void Config::ReadValues() { | |||
|     } | ||||
| 
 | ||||
|     Settings::values.motion_device = | ||||
|         qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") | ||||
|         qt_config | ||||
|             ->value("motion_device", | ||||
|                     "engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0") | ||||
|             .toString() | ||||
|             .toStdString(); | ||||
|     Settings::values.touch_device = | ||||
|  | @ -182,6 +184,7 @@ void Config::ReadValues() { | |||
|     UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString(); | ||||
|     UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool(); | ||||
|     UISettings::values.recent_files = qt_config->value("recentFiles").toStringList(); | ||||
|     UISettings::values.language = qt_config->value("language", "").toString(); | ||||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|     qt_config->beginGroup("Shortcuts"); | ||||
|  | @ -333,6 +336,7 @@ void Config::SaveValues() { | |||
|     qt_config->setValue("gameListRootDir", UISettings::values.gamedir); | ||||
|     qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); | ||||
|     qt_config->setValue("recentFiles", UISettings::values.recent_files); | ||||
|     qt_config->setValue("language", UISettings::values.language); | ||||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|     qt_config->beginGroup("Shortcuts"); | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>740</width> | ||||
|     <width>461</width> | ||||
|     <height>500</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|  | @ -34,15 +34,15 @@ | |||
|        <string>Input</string> | ||||
|       </attribute> | ||||
|      </widget> | ||||
|       <widget class="ConfigureGraphics" name="graphicsTab"> | ||||
|         <attribute name="title"> | ||||
|           <string>Graphics</string> | ||||
|         </attribute> | ||||
|       </widget> | ||||
|      <widget class="ConfigureGraphics" name="graphicsTab"> | ||||
|       <attribute name="title"> | ||||
|        <string>Graphics</string> | ||||
|       </attribute> | ||||
|      </widget> | ||||
|      <widget class="ConfigureAudio" name="audioTab"> | ||||
|        <attribute name="title"> | ||||
|          <string>Audio</string> | ||||
|        </attribute> | ||||
|       <attribute name="title"> | ||||
|        <string>Audio</string> | ||||
|       </attribute> | ||||
|      </widget> | ||||
|      <widget class="ConfigureDebug" name="debugTab"> | ||||
|       <attribute name="title"> | ||||
|  |  | |||
|  | @ -76,3 +76,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { | |||
|         ui->audio_device_combo_box->addItem(device.c_str()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ConfigureAudio::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ public: | |||
|     ~ConfigureAudio(); | ||||
| 
 | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| public slots: | ||||
|     void updateAudioDevices(int sink_index); | ||||
|  |  | |||
|  | @ -24,3 +24,7 @@ void ConfigureDebug::applyConfiguration() { | |||
|     Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); | ||||
|     Settings::Apply(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureDebug::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ public: | |||
|     ~ConfigureDebug(); | ||||
| 
 | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| private: | ||||
|     void setConfiguration(); | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ | |||
| ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) { | ||||
|     ui->setupUi(this); | ||||
|     this->setConfiguration(); | ||||
|     connect(ui->generalTab, &ConfigureGeneral::languageChanged, this, | ||||
|             &ConfigureDialog::onLanguageChanged); | ||||
| } | ||||
| 
 | ||||
| ConfigureDialog::~ConfigureDialog() {} | ||||
|  | @ -26,3 +28,15 @@ void ConfigureDialog::applyConfiguration() { | |||
|     ui->webTab->applyConfiguration(); | ||||
|     Settings::Apply(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureDialog::onLanguageChanged(const QString& locale) { | ||||
|     emit languageChanged(locale); | ||||
|     ui->retranslateUi(this); | ||||
|     ui->generalTab->retranslateUi(); | ||||
|     ui->systemTab->retranslateUi(); | ||||
|     ui->inputTab->retranslateUi(); | ||||
|     ui->graphicsTab->retranslateUi(); | ||||
|     ui->audioTab->retranslateUi(); | ||||
|     ui->debugTab->retranslateUi(); | ||||
|     ui->webTab->retranslateUi(); | ||||
| } | ||||
|  |  | |||
|  | @ -20,6 +20,12 @@ public: | |||
| 
 | ||||
|     void applyConfiguration(); | ||||
| 
 | ||||
| private slots: | ||||
|     void onLanguageChanged(const QString& locale); | ||||
| 
 | ||||
| signals: | ||||
|     void languageChanged(const QString& locale); | ||||
| 
 | ||||
| private: | ||||
|     void setConfiguration(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QDirIterator> | ||||
| #include "citra_qt/configuration/configure_general.h" | ||||
| #include "citra_qt/ui_settings.h" | ||||
| #include "core/core.h" | ||||
|  | @ -12,6 +13,23 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
|     : QWidget(parent), ui(new Ui::ConfigureGeneral) { | ||||
| 
 | ||||
|     ui->setupUi(this); | ||||
|     ui->language_combobox->addItem(tr("<System>"), QString("")); | ||||
|     ui->language_combobox->addItem(tr("English"), QString("en")); | ||||
|     QDirIterator it(":/languages", QDirIterator::NoIteratorFlags); | ||||
|     while (it.hasNext()) { | ||||
|         QString locale = it.next(); | ||||
|         locale.truncate(locale.lastIndexOf('.')); | ||||
|         locale.remove(0, locale.lastIndexOf('/') + 1); | ||||
|         QString lang = QLocale::languageToString(QLocale(locale).language()); | ||||
|         ui->language_combobox->addItem(lang, locale); | ||||
|     } | ||||
| 
 | ||||
|     // Unlike other configuration changes, interface language changes need to be reflected on the
 | ||||
|     // interface immediately. This is done by passing a signal to the main window, and then
 | ||||
|     // retranslating when passing back.
 | ||||
|     connect(ui->language_combobox, | ||||
|             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||||
|             &ConfigureGeneral::onLanguageChanged); | ||||
| 
 | ||||
|     for (auto theme : UISettings::themes) { | ||||
|         ui->theme_combobox->addItem(theme.first, theme.second); | ||||
|  | @ -37,6 +55,8 @@ void ConfigureGeneral::setConfiguration() { | |||
|     ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1); | ||||
| 
 | ||||
|     ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | ||||
|     ui->language_combobox->setCurrentIndex( | ||||
|         ui->language_combobox->findData(UISettings::values.language)); | ||||
| } | ||||
| 
 | ||||
| void ConfigureGeneral::applyConfiguration() { | ||||
|  | @ -52,3 +72,14 @@ void ConfigureGeneral::applyConfiguration() { | |||
|     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); | ||||
|     Settings::Apply(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureGeneral::onLanguageChanged(int index) { | ||||
|     if (index == -1) | ||||
|         return; | ||||
| 
 | ||||
|     emit languageChanged(ui->language_combobox->itemData(index).toString()); | ||||
| } | ||||
| 
 | ||||
| void ConfigureGeneral::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  |  | |||
|  | @ -19,6 +19,13 @@ public: | |||
|     ~ConfigureGeneral(); | ||||
| 
 | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| private slots: | ||||
|     void onLanguageChanged(int index); | ||||
| 
 | ||||
| signals: | ||||
|     void languageChanged(const QString& locale); | ||||
| 
 | ||||
| private: | ||||
|     void setConfiguration(); | ||||
|  |  | |||
|  | @ -6,8 +6,8 @@ | |||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>300</width> | ||||
|     <height>377</height> | ||||
|     <width>345</width> | ||||
|     <height>493</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|  | @ -38,6 +38,20 @@ | |||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="language_label"> | ||||
|               <property name="text"> | ||||
|                <string>Interface language</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QComboBox" name="language_combobox"/> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </item> | ||||
|        </layout> | ||||
|  |  | |||
|  | @ -113,3 +113,7 @@ void ConfigureGraphics::applyConfiguration() { | |||
|     Settings::values.swap_screen = ui->swap_screen->isChecked(); | ||||
|     Settings::Apply(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureGraphics::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ public: | |||
|     ~ConfigureGraphics(); | ||||
| 
 | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| private: | ||||
|     void setConfiguration(); | ||||
|  |  | |||
|  | @ -277,3 +277,7 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) { | |||
|     } | ||||
|     setPollingResult({}, true); | ||||
| } | ||||
| 
 | ||||
| void ConfigureInput::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ public: | |||
| 
 | ||||
|     /// Save all button configurations to settings file
 | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<Ui::ConfigureInput> ui; | ||||
|  |  | |||
|  | @ -167,3 +167,7 @@ void ConfigureSystem::refreshConsoleID() { | |||
|     Service::CFG::UpdateConfigNANDSavegame(); | ||||
|     ui->label_console_id->setText("Console ID: 0x" + QString::number(console_id, 16).toUpper()); | ||||
| } | ||||
| 
 | ||||
| void ConfigureSystem::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ public: | |||
| 
 | ||||
|     void applyConfiguration(); | ||||
|     void setConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| public slots: | ||||
|     void updateBirthdayComboBox(int birthmonth_index); | ||||
|  |  | |||
|  | @ -100,3 +100,7 @@ void ConfigureWeb::OnLoginVerified() { | |||
|                "correctly, and that your internet connection is working.")); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ConfigureWeb::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ public: | |||
|     ~ConfigureWeb(); | ||||
| 
 | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
| public slots: | ||||
|     void RefreshTelemetryID(); | ||||
|  |  | |||
|  | @ -98,6 +98,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
|     // register size_t to use in slots and signals
 | ||||
|     qRegisterMetaType<size_t>("size_t"); | ||||
| 
 | ||||
|     LoadTranslation(); | ||||
| 
 | ||||
|     Pica::g_debug_context = Pica::DebugContext::Construct(); | ||||
|     setAcceptDrops(true); | ||||
|     ui.setupUi(this); | ||||
|  | @ -115,8 +117,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
|     ConnectMenuEvents(); | ||||
|     ConnectWidgetEvents(); | ||||
| 
 | ||||
|     setWindowTitle(QString("Citra %1| %2-%3") | ||||
|                        .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | ||||
|     SetupUIStrings(); | ||||
| 
 | ||||
|     show(); | ||||
| 
 | ||||
|     game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | ||||
|  | @ -892,6 +894,8 @@ void GMainWindow::ToggleWindowMode() { | |||
| 
 | ||||
| void GMainWindow::OnConfigure() { | ||||
|     ConfigureDialog configureDialog(this); | ||||
|     connect(&configureDialog, &ConfigureDialog::languageChanged, this, | ||||
|             &GMainWindow::OnLanguageChanged); | ||||
|     auto result = configureDialog.exec(); | ||||
|     if (result == QDialog::Accepted) { | ||||
|         configureDialog.applyConfiguration(); | ||||
|  | @ -995,6 +999,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
|     } else { | ||||
|         // Only show the message if the game is still running.
 | ||||
|         if (emu_thread) { | ||||
|             emu_thread->SetRunning(true); | ||||
|             message_label->setText(status_message); | ||||
|             message_label->setVisible(true); | ||||
|         } | ||||
|  | @ -1105,6 +1110,45 @@ void GMainWindow::UpdateUITheme() { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::LoadTranslation() { | ||||
|     // If the selected language is English, no need to install any translation
 | ||||
|     if (UISettings::values.language == "en") { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     bool loaded; | ||||
| 
 | ||||
|     if (UISettings::values.language.isEmpty()) { | ||||
|         // If the selected language is empty, use system locale
 | ||||
|         loaded = translator.load(QLocale(), "", "", ":/languages/"); | ||||
|     } else { | ||||
|         // Otherwise load from the specified file
 | ||||
|         loaded = translator.load(UISettings::values.language, ":/languages/"); | ||||
|     } | ||||
| 
 | ||||
|     if (loaded) { | ||||
|         qApp->installTranslator(&translator); | ||||
|     } else { | ||||
|         UISettings::values.language = "en"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnLanguageChanged(const QString& locale) { | ||||
|     if (UISettings::values.language != "en") { | ||||
|         qApp->removeTranslator(&translator); | ||||
|     } | ||||
| 
 | ||||
|     UISettings::values.language = locale; | ||||
|     LoadTranslation(); | ||||
|     ui.retranslateUi(this); | ||||
|     SetupUIStrings(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::SetupUIStrings() { | ||||
|     setWindowTitle( | ||||
|         tr("Citra %1| %2-%3").arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | ||||
| } | ||||
| 
 | ||||
| #ifdef main | ||||
| #undef main | ||||
| #endif | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <memory> | ||||
| #include <QMainWindow> | ||||
| #include <QTimer> | ||||
| #include <QTranslator> | ||||
| #include "core/core.h" | ||||
| #include "core/hle/service/am/am.h" | ||||
| #include "ui_main.h" | ||||
|  | @ -152,9 +153,12 @@ private slots: | |||
|     void OnUpdateFound(bool found, bool error); | ||||
|     void OnCheckForUpdates(); | ||||
|     void OnOpenUpdater(); | ||||
|     void OnLanguageChanged(const QString& locale); | ||||
| 
 | ||||
| private: | ||||
|     void UpdateStatusBar(); | ||||
|     void LoadTranslation(); | ||||
|     void SetupUIStrings(); | ||||
| 
 | ||||
|     Ui::MainWindow ui; | ||||
| 
 | ||||
|  | @ -193,6 +197,8 @@ private: | |||
| 
 | ||||
|     QAction* actions_recent_files[max_recent_files_item]; | ||||
| 
 | ||||
|     QTranslator translator; | ||||
| 
 | ||||
| protected: | ||||
|     void dropEvent(QDropEvent* event) override; | ||||
|     void dragEnterEvent(QDragEnterEvent* event) override; | ||||
|  |  | |||
|  | @ -48,6 +48,7 @@ struct Values { | |||
|     QString gamedir; | ||||
|     bool gamedir_deepscan; | ||||
|     QStringList recent_files; | ||||
|     QString language; | ||||
| 
 | ||||
|     QString theme; | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,6 +32,8 @@ add_library(common STATIC | |||
|     break_points.cpp | ||||
|     break_points.h | ||||
|     chunk_file.h | ||||
|     cityhash.cpp | ||||
|     cityhash.h | ||||
|     code_block.h | ||||
|     color.h | ||||
|     common_funcs.h | ||||
|  | @ -39,7 +41,6 @@ add_library(common STATIC | |||
|     common_types.h | ||||
|     file_util.cpp | ||||
|     file_util.h | ||||
|     hash.cpp | ||||
|     hash.h | ||||
|     linear_disk_cache.h | ||||
|     logging/backend.cpp | ||||
|  |  | |||
							
								
								
									
										340
									
								
								src/common/cityhash.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								src/common/cityhash.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,340 @@ | |||
| // Copyright (c) 2011 Google, Inc.
 | ||||
| //
 | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||||
| // of this software and associated documentation files (the "Software"), to deal
 | ||||
| // in the Software without restriction, including without limitation the rights
 | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||||
| // copies of the Software, and to permit persons to whom the Software is
 | ||||
| // furnished to do so, subject to the following conditions:
 | ||||
| //
 | ||||
| // The above copyright notice and this permission notice shall be included in
 | ||||
| // all copies or substantial portions of the Software.
 | ||||
| //
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||||
| // THE SOFTWARE.
 | ||||
| //
 | ||||
| // CityHash, by Geoff Pike and Jyrki Alakuijala
 | ||||
| //
 | ||||
| // This file provides CityHash64() and related functions.
 | ||||
| //
 | ||||
| // It's probably possible to create even faster hash functions by
 | ||||
| // writing a program that systematically explores some of the space of
 | ||||
| // possible hash functions, by using SIMD instructions, or by
 | ||||
| // compromising on hash quality.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <string.h> // for memcpy and memset
 | ||||
| #include "cityhash.h" | ||||
| #include "common/swap.h" | ||||
| 
 | ||||
| // #include "config.h"
 | ||||
| #ifdef __GNUC__ | ||||
| #define HAVE_BUILTIN_EXPECT 1 | ||||
| #endif | ||||
| #ifdef COMMON_BIG_ENDIAN | ||||
| #define WORDS_BIGENDIAN 1 | ||||
| #endif | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| typedef uint8_t uint8; | ||||
| typedef uint32_t uint32; | ||||
| typedef uint64_t uint64; | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| static uint64 UNALIGNED_LOAD64(const char* p) { | ||||
|     uint64 result; | ||||
|     memcpy(&result, p, sizeof(result)); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| static uint32 UNALIGNED_LOAD32(const char* p) { | ||||
|     uint32 result; | ||||
|     memcpy(&result, p, sizeof(result)); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| #ifdef WORDS_BIGENDIAN | ||||
| #define uint32_in_expected_order(x) (swap32(x)) | ||||
| #define uint64_in_expected_order(x) (swap64(x)) | ||||
| #else | ||||
| #define uint32_in_expected_order(x) (x) | ||||
| #define uint64_in_expected_order(x) (x) | ||||
| #endif | ||||
| 
 | ||||
| #if !defined(LIKELY) | ||||
| #if HAVE_BUILTIN_EXPECT | ||||
| #define LIKELY(x) (__builtin_expect(!!(x), 1)) | ||||
| #else | ||||
| #define LIKELY(x) (x) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| static uint64 Fetch64(const char* p) { | ||||
|     return uint64_in_expected_order(UNALIGNED_LOAD64(p)); | ||||
| } | ||||
| 
 | ||||
| static uint32 Fetch32(const char* p) { | ||||
|     return uint32_in_expected_order(UNALIGNED_LOAD32(p)); | ||||
| } | ||||
| 
 | ||||
| // Some primes between 2^63 and 2^64 for various uses.
 | ||||
| static const uint64 k0 = 0xc3a5c85c97cb3127ULL; | ||||
| static const uint64 k1 = 0xb492b66fbe98f273ULL; | ||||
| static const uint64 k2 = 0x9ae16a3b2f90404fULL; | ||||
| 
 | ||||
| // Bitwise right rotate.  Normally this will compile to a single
 | ||||
| // instruction, especially if the shift is a manifest constant.
 | ||||
| static uint64 Rotate(uint64 val, int shift) { | ||||
|     // Avoid shifting by 64: doing so yields an undefined result.
 | ||||
|     return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); | ||||
| } | ||||
| 
 | ||||
| static uint64 ShiftMix(uint64 val) { | ||||
|     return val ^ (val >> 47); | ||||
| } | ||||
| 
 | ||||
| static uint64 HashLen16(uint64 u, uint64 v) { | ||||
|     return Hash128to64(uint128(u, v)); | ||||
| } | ||||
| 
 | ||||
| static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { | ||||
|     // Murmur-inspired hashing.
 | ||||
|     uint64 a = (u ^ v) * mul; | ||||
|     a ^= (a >> 47); | ||||
|     uint64 b = (v ^ a) * mul; | ||||
|     b ^= (b >> 47); | ||||
|     b *= mul; | ||||
|     return b; | ||||
| } | ||||
| 
 | ||||
| static uint64 HashLen0to16(const char* s, size_t len) { | ||||
|     if (len >= 8) { | ||||
|         uint64 mul = k2 + len * 2; | ||||
|         uint64 a = Fetch64(s) + k2; | ||||
|         uint64 b = Fetch64(s + len - 8); | ||||
|         uint64 c = Rotate(b, 37) * mul + a; | ||||
|         uint64 d = (Rotate(a, 25) + b) * mul; | ||||
|         return HashLen16(c, d, mul); | ||||
|     } | ||||
|     if (len >= 4) { | ||||
|         uint64 mul = k2 + len * 2; | ||||
|         uint64 a = Fetch32(s); | ||||
|         return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); | ||||
|     } | ||||
|     if (len > 0) { | ||||
|         uint8 a = s[0]; | ||||
|         uint8 b = s[len >> 1]; | ||||
|         uint8 c = s[len - 1]; | ||||
|         uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8); | ||||
|         uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2); | ||||
|         return ShiftMix(y * k2 ^ z * k0) * k2; | ||||
|     } | ||||
|     return k2; | ||||
| } | ||||
| 
 | ||||
| // This probably works well for 16-byte strings as well, but it may be overkill
 | ||||
| // in that case.
 | ||||
| static uint64 HashLen17to32(const char* s, size_t len) { | ||||
|     uint64 mul = k2 + len * 2; | ||||
|     uint64 a = Fetch64(s) * k1; | ||||
|     uint64 b = Fetch64(s + 8); | ||||
|     uint64 c = Fetch64(s + len - 8) * mul; | ||||
|     uint64 d = Fetch64(s + len - 16) * k2; | ||||
|     return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul); | ||||
| } | ||||
| 
 | ||||
| // Return a 16-byte hash for 48 bytes.  Quick and dirty.
 | ||||
| // Callers do best to use "random-looking" values for a and b.
 | ||||
| static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, | ||||
|                                                    uint64 b) { | ||||
|     a += w; | ||||
|     b = Rotate(b + a + z, 21); | ||||
|     uint64 c = a; | ||||
|     a += x; | ||||
|     a += y; | ||||
|     b += Rotate(a, 44); | ||||
|     return make_pair(a + z, b + c); | ||||
| } | ||||
| 
 | ||||
| // Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.
 | ||||
| static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) { | ||||
|     return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, | ||||
|                                   b); | ||||
| } | ||||
| 
 | ||||
| // Return an 8-byte hash for 33 to 64 bytes.
 | ||||
| static uint64 HashLen33to64(const char* s, size_t len) { | ||||
|     uint64 mul = k2 + len * 2; | ||||
|     uint64 a = Fetch64(s) * k2; | ||||
|     uint64 b = Fetch64(s + 8); | ||||
|     uint64 c = Fetch64(s + len - 24); | ||||
|     uint64 d = Fetch64(s + len - 32); | ||||
|     uint64 e = Fetch64(s + 16) * k2; | ||||
|     uint64 f = Fetch64(s + 24) * 9; | ||||
|     uint64 g = Fetch64(s + len - 8); | ||||
|     uint64 h = Fetch64(s + len - 16) * mul; | ||||
|     uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; | ||||
|     uint64 v = ((a + g) ^ d) + f + 1; | ||||
|     uint64 w = swap64((u + v) * mul) + h; | ||||
|     uint64 x = Rotate(e + f, 42) + c; | ||||
|     uint64 y = (swap64((v + w) * mul) + g) * mul; | ||||
|     uint64 z = e + f + c; | ||||
|     a = swap64((x + z) * mul + y) + b; | ||||
|     b = ShiftMix((z + a) * mul + d + h) * mul; | ||||
|     return b + x; | ||||
| } | ||||
| 
 | ||||
| uint64 CityHash64(const char* s, size_t len) { | ||||
|     if (len <= 32) { | ||||
|         if (len <= 16) { | ||||
|             return HashLen0to16(s, len); | ||||
|         } else { | ||||
|             return HashLen17to32(s, len); | ||||
|         } | ||||
|     } else if (len <= 64) { | ||||
|         return HashLen33to64(s, len); | ||||
|     } | ||||
| 
 | ||||
|     // For strings over 64 bytes we hash the end first, and then as we
 | ||||
|     // loop we keep 56 bytes of state: v, w, x, y, and z.
 | ||||
|     uint64 x = Fetch64(s + len - 40); | ||||
|     uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); | ||||
|     uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); | ||||
|     pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z); | ||||
|     pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); | ||||
|     x = x * k1 + Fetch64(s); | ||||
| 
 | ||||
|     // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
 | ||||
|     len = (len - 1) & ~static_cast<size_t>(63); | ||||
|     do { | ||||
|         x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; | ||||
|         y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; | ||||
|         x ^= w.second; | ||||
|         y += v.first + Fetch64(s + 40); | ||||
|         z = Rotate(z + w.first, 33) * k1; | ||||
|         v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); | ||||
|         w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); | ||||
|         std::swap(z, x); | ||||
|         s += 64; | ||||
|         len -= 64; | ||||
|     } while (len != 0); | ||||
|     return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, | ||||
|                      HashLen16(v.second, w.second) + x); | ||||
| } | ||||
| 
 | ||||
| uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) { | ||||
|     return CityHash64WithSeeds(s, len, k2, seed); | ||||
| } | ||||
| 
 | ||||
| uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) { | ||||
|     return HashLen16(CityHash64(s, len) - seed0, seed1); | ||||
| } | ||||
| 
 | ||||
| // A subroutine for CityHash128().  Returns a decent 128-bit hash for strings
 | ||||
| // of any length representable in signed long.  Based on City and Murmur.
 | ||||
| static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { | ||||
|     uint64 a = Uint128Low64(seed); | ||||
|     uint64 b = Uint128High64(seed); | ||||
|     uint64 c = 0; | ||||
|     uint64 d = 0; | ||||
|     signed long l = static_cast<long>(len) - 16; | ||||
|     if (l <= 0) { // len <= 16
 | ||||
|         a = ShiftMix(a * k1) * k1; | ||||
|         c = b * k1 + HashLen0to16(s, len); | ||||
|         d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); | ||||
|     } else { // len > 16
 | ||||
|         c = HashLen16(Fetch64(s + len - 8) + k1, a); | ||||
|         d = HashLen16(b + len, c + Fetch64(s + len - 16)); | ||||
|         a += d; | ||||
|         do { | ||||
|             a ^= ShiftMix(Fetch64(s) * k1) * k1; | ||||
|             a *= k1; | ||||
|             b ^= a; | ||||
|             c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; | ||||
|             c *= k1; | ||||
|             d ^= c; | ||||
|             s += 16; | ||||
|             l -= 16; | ||||
|         } while (l > 0); | ||||
|     } | ||||
|     a = HashLen16(a, c); | ||||
|     b = HashLen16(d, b); | ||||
|     return uint128(a ^ b, HashLen16(b, a)); | ||||
| } | ||||
| 
 | ||||
| uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { | ||||
|     if (len < 128) { | ||||
|         return CityMurmur(s, len, seed); | ||||
|     } | ||||
| 
 | ||||
|     // We expect len >= 128 to be the common case.  Keep 56 bytes of state:
 | ||||
|     // v, w, x, y, and z.
 | ||||
|     pair<uint64, uint64> v, w; | ||||
|     uint64 x = Uint128Low64(seed); | ||||
|     uint64 y = Uint128High64(seed); | ||||
|     uint64 z = len * k1; | ||||
|     v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); | ||||
|     v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); | ||||
|     w.first = Rotate(y + z, 35) * k1 + x; | ||||
|     w.second = Rotate(x + Fetch64(s + 88), 53) * k1; | ||||
| 
 | ||||
|     // This is the same inner loop as CityHash64(), manually unrolled.
 | ||||
|     do { | ||||
|         x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; | ||||
|         y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; | ||||
|         x ^= w.second; | ||||
|         y += v.first + Fetch64(s + 40); | ||||
|         z = Rotate(z + w.first, 33) * k1; | ||||
|         v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); | ||||
|         w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); | ||||
|         std::swap(z, x); | ||||
|         s += 64; | ||||
|         x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; | ||||
|         y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; | ||||
|         x ^= w.second; | ||||
|         y += v.first + Fetch64(s + 40); | ||||
|         z = Rotate(z + w.first, 33) * k1; | ||||
|         v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); | ||||
|         w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); | ||||
|         std::swap(z, x); | ||||
|         s += 64; | ||||
|         len -= 128; | ||||
|     } while (LIKELY(len >= 128)); | ||||
|     x += Rotate(v.first + z, 49) * k0; | ||||
|     y = y * k0 + Rotate(w.second, 37); | ||||
|     z = z * k0 + Rotate(w.first, 27); | ||||
|     w.first *= 9; | ||||
|     v.first *= k0; | ||||
|     // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
 | ||||
|     for (size_t tail_done = 0; tail_done < len;) { | ||||
|         tail_done += 32; | ||||
|         y = Rotate(x + y, 42) * k0 + v.second; | ||||
|         w.first += Fetch64(s + len - tail_done + 16); | ||||
|         x = x * k0 + w.first; | ||||
|         z += w.second + Fetch64(s + len - tail_done); | ||||
|         w.second += v.first; | ||||
|         v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); | ||||
|         v.first *= k0; | ||||
|     } | ||||
|     // At this point our 56 bytes of state should contain more than
 | ||||
|     // enough information for a strong 128-bit hash.  We use two
 | ||||
|     // different 56-byte-to-8-byte hashes to get a 16-byte final result.
 | ||||
|     x = HashLen16(x, v.first); | ||||
|     y = HashLen16(y + z, w.first); | ||||
|     return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); | ||||
| } | ||||
| 
 | ||||
| uint128 CityHash128(const char* s, size_t len) { | ||||
|     return len >= 16 | ||||
|                ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) | ||||
|                : CityHash128WithSeed(s, len, uint128(k0, k1)); | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
							
								
								
									
										110
									
								
								src/common/cityhash.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/common/cityhash.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | |||
| // Copyright (c) 2011 Google, Inc.
 | ||||
| //
 | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||||
| // of this software and associated documentation files (the "Software"), to deal
 | ||||
| // in the Software without restriction, including without limitation the rights
 | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||||
| // copies of the Software, and to permit persons to whom the Software is
 | ||||
| // furnished to do so, subject to the following conditions:
 | ||||
| //
 | ||||
| // The above copyright notice and this permission notice shall be included in
 | ||||
| // all copies or substantial portions of the Software.
 | ||||
| //
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||||
| // THE SOFTWARE.
 | ||||
| //
 | ||||
| // CityHash, by Geoff Pike and Jyrki Alakuijala
 | ||||
| //
 | ||||
| // http://code.google.com/p/cityhash/
 | ||||
| //
 | ||||
| // This file provides a few functions for hashing strings.  All of them are
 | ||||
| // high-quality functions in the sense that they pass standard tests such
 | ||||
| // as Austin Appleby's SMHasher.  They are also fast.
 | ||||
| //
 | ||||
| // For 64-bit x86 code, on short strings, we don't know of anything faster than
 | ||||
| // CityHash64 that is of comparable quality.  We believe our nearest competitor
 | ||||
| // is Murmur3.  For 64-bit x86 code, CityHash64 is an excellent choice for hash
 | ||||
| // tables and most other hashing (excluding cryptography).
 | ||||
| //
 | ||||
| // For 64-bit x86 code, on long strings, the picture is more complicated.
 | ||||
| // On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc.,
 | ||||
| // CityHashCrc128 appears to be faster than all competitors of comparable
 | ||||
| // quality.  CityHash128 is also good but not quite as fast.  We believe our
 | ||||
| // nearest competitor is Bob Jenkins' Spooky.  We don't have great data for
 | ||||
| // other 64-bit CPUs, but for long strings we know that Spooky is slightly
 | ||||
| // faster than CityHash on some relatively recent AMD x86-64 CPUs, for example.
 | ||||
| // Note that CityHashCrc128 is declared in citycrc.h.
 | ||||
| //
 | ||||
| // For 32-bit x86 code, we don't know of anything faster than CityHash32 that
 | ||||
| // is of comparable quality.  We believe our nearest competitor is Murmur3A.
 | ||||
| // (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
 | ||||
| //
 | ||||
| // Functions in the CityHash family are not suitable for cryptography.
 | ||||
| //
 | ||||
| // Please see CityHash's README file for more details on our performance
 | ||||
| // measurements and so on.
 | ||||
| //
 | ||||
| // WARNING: This code has been only lightly tested on big-endian platforms!
 | ||||
| // It is known to work well on little-endian platforms that have a small penalty
 | ||||
| // for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
 | ||||
| // It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
 | ||||
| // bug reports are welcome.
 | ||||
| //
 | ||||
| // By the way, for some hash functions, given strings a and b, the hash
 | ||||
| // of a+b is easily derived from the hashes of a and b.  This property
 | ||||
| // doesn't hold for any hash functions in this file.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> // for size_t. | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| typedef std::pair<uint64_t, uint64_t> uint128; | ||||
| 
 | ||||
| inline uint64_t Uint128Low64(const uint128& x) { | ||||
|     return x.first; | ||||
| } | ||||
| inline uint64_t Uint128High64(const uint128& x) { | ||||
|     return x.second; | ||||
| } | ||||
| 
 | ||||
| // Hash function for a byte array.
 | ||||
| uint64_t CityHash64(const char* buf, size_t len); | ||||
| 
 | ||||
| // Hash function for a byte array.  For convenience, a 64-bit seed is also
 | ||||
| // hashed into the result.
 | ||||
| uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed); | ||||
| 
 | ||||
| // Hash function for a byte array.  For convenience, two seeds are also
 | ||||
| // hashed into the result.
 | ||||
| uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1); | ||||
| 
 | ||||
| // Hash function for a byte array.
 | ||||
| uint128 CityHash128(const char* s, size_t len); | ||||
| 
 | ||||
| // Hash function for a byte array.  For convenience, a 128-bit seed is also
 | ||||
| // hashed into the result.
 | ||||
| uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); | ||||
| 
 | ||||
| // Hash 128 input bits down to 64 bits of output.
 | ||||
| // This is intended to be a reasonably good hash function.
 | ||||
| inline uint64_t Hash128to64(const uint128& x) { | ||||
|     // Murmur-inspired hashing.
 | ||||
|     const uint64_t kMul = 0x9ddfea08eb382d69ULL; | ||||
|     uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; | ||||
|     a ^= (a >> 47); | ||||
|     uint64_t b = (Uint128High64(x) ^ a) * kMul; | ||||
|     b ^= (b >> 47); | ||||
|     b *= kMul; | ||||
|     return b; | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
|  | @ -1,141 +0,0 @@ | |||
| // Copyright 2015 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #if defined(_MSC_VER) | ||||
| #include <stdlib.h> | ||||
| #endif | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/hash.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| // MurmurHash3 was written by Austin Appleby, and is placed in the public
 | ||||
| // domain. The author hereby disclaims copyright to this source code.
 | ||||
| 
 | ||||
| // Block read - if your platform needs to do endian-swapping or can only handle aligned reads, do
 | ||||
| // the conversion here
 | ||||
| static FORCE_INLINE u64 getblock64(const u64* p, size_t i) { | ||||
|     return p[i]; | ||||
| } | ||||
| 
 | ||||
| // Finalization mix - force all bits of a hash block to avalanche
 | ||||
| static FORCE_INLINE u64 fmix64(u64 k) { | ||||
|     k ^= k >> 33; | ||||
|     k *= 0xff51afd7ed558ccdllu; | ||||
|     k ^= k >> 33; | ||||
|     k *= 0xc4ceb9fe1a85ec53llu; | ||||
|     k ^= k >> 33; | ||||
| 
 | ||||
|     return k; | ||||
| } | ||||
| 
 | ||||
| // This is the 128-bit variant of the MurmurHash3 hash function that is targeted for 64-bit
 | ||||
| // platforms (MurmurHash3_x64_128). It was taken from:
 | ||||
| // https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
 | ||||
| void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out) { | ||||
|     const u8* data = (const u8*)key; | ||||
|     const size_t nblocks = len / 16; | ||||
| 
 | ||||
|     u64 h1 = seed; | ||||
|     u64 h2 = seed; | ||||
| 
 | ||||
|     const u64 c1 = 0x87c37b91114253d5llu; | ||||
|     const u64 c2 = 0x4cf5ad432745937fllu; | ||||
| 
 | ||||
|     // Body
 | ||||
| 
 | ||||
|     const u64* blocks = (const u64*)(data); | ||||
| 
 | ||||
|     for (size_t i = 0; i < nblocks; i++) { | ||||
|         u64 k1 = getblock64(blocks, i * 2 + 0); | ||||
|         u64 k2 = getblock64(blocks, i * 2 + 1); | ||||
| 
 | ||||
|         k1 *= c1; | ||||
|         k1 = _rotl64(k1, 31); | ||||
|         k1 *= c2; | ||||
|         h1 ^= k1; | ||||
| 
 | ||||
|         h1 = _rotl64(h1, 27); | ||||
|         h1 += h2; | ||||
|         h1 = h1 * 5 + 0x52dce729; | ||||
| 
 | ||||
|         k2 *= c2; | ||||
|         k2 = _rotl64(k2, 33); | ||||
|         k2 *= c1; | ||||
|         h2 ^= k2; | ||||
| 
 | ||||
|         h2 = _rotl64(h2, 31); | ||||
|         h2 += h1; | ||||
|         h2 = h2 * 5 + 0x38495ab5; | ||||
|     } | ||||
| 
 | ||||
|     // Tail
 | ||||
| 
 | ||||
|     const u8* tail = (const u8*)(data + nblocks * 16); | ||||
| 
 | ||||
|     u64 k1 = 0; | ||||
|     u64 k2 = 0; | ||||
| 
 | ||||
|     switch (len & 15) { | ||||
|     case 15: | ||||
|         k2 ^= ((u64)tail[14]) << 48; | ||||
|     case 14: | ||||
|         k2 ^= ((u64)tail[13]) << 40; | ||||
|     case 13: | ||||
|         k2 ^= ((u64)tail[12]) << 32; | ||||
|     case 12: | ||||
|         k2 ^= ((u64)tail[11]) << 24; | ||||
|     case 11: | ||||
|         k2 ^= ((u64)tail[10]) << 16; | ||||
|     case 10: | ||||
|         k2 ^= ((u64)tail[9]) << 8; | ||||
|     case 9: | ||||
|         k2 ^= ((u64)tail[8]) << 0; | ||||
|         k2 *= c2; | ||||
|         k2 = _rotl64(k2, 33); | ||||
|         k2 *= c1; | ||||
|         h2 ^= k2; | ||||
| 
 | ||||
|     case 8: | ||||
|         k1 ^= ((u64)tail[7]) << 56; | ||||
|     case 7: | ||||
|         k1 ^= ((u64)tail[6]) << 48; | ||||
|     case 6: | ||||
|         k1 ^= ((u64)tail[5]) << 40; | ||||
|     case 5: | ||||
|         k1 ^= ((u64)tail[4]) << 32; | ||||
|     case 4: | ||||
|         k1 ^= ((u64)tail[3]) << 24; | ||||
|     case 3: | ||||
|         k1 ^= ((u64)tail[2]) << 16; | ||||
|     case 2: | ||||
|         k1 ^= ((u64)tail[1]) << 8; | ||||
|     case 1: | ||||
|         k1 ^= ((u64)tail[0]) << 0; | ||||
|         k1 *= c1; | ||||
|         k1 = _rotl64(k1, 31); | ||||
|         k1 *= c2; | ||||
|         h1 ^= k1; | ||||
|     }; | ||||
| 
 | ||||
|     // Finalization
 | ||||
| 
 | ||||
|     h1 ^= len; | ||||
|     h2 ^= len; | ||||
| 
 | ||||
|     h1 += h2; | ||||
|     h2 += h1; | ||||
| 
 | ||||
|     h1 = fmix64(h1); | ||||
|     h2 = fmix64(h2); | ||||
| 
 | ||||
|     h1 += h2; | ||||
|     h2 += h1; | ||||
| 
 | ||||
|     ((u64*)out)[0] = h1; | ||||
|     ((u64*)out)[1] = h2; | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
|  | @ -5,12 +5,11 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include "common/cityhash.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out); | ||||
| 
 | ||||
| /**
 | ||||
|  * Computes a 64-bit hash over the specified block of data | ||||
|  * @param data Block of data to compute hash over | ||||
|  | @ -18,9 +17,20 @@ void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out); | |||
|  * @returns 64-bit hash value that was computed over the data block | ||||
|  */ | ||||
| static inline u64 ComputeHash64(const void* data, size_t len) { | ||||
|     u64 res[2]; | ||||
|     MurmurHash3_128(data, len, 0, res); | ||||
|     return res[0]; | ||||
|     return CityHash64(static_cast<const char*>(data), len); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Computes a 64-bit hash of a struct. In addition to being POD (trivially copyable and having | ||||
|  * standard layout), it is also critical that either the struct includes no padding, or that any | ||||
|  * padding is initialized to a known value by memsetting the struct to 0 before filling it in. | ||||
|  */ | ||||
| template <typename T> | ||||
| static inline u64 ComputeStructHash64(const T& data) { | ||||
|     static_assert( | ||||
|         std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value, | ||||
|         "Type passed to ComputeStructHash64 must be trivially copyable and standard layout"); | ||||
|     return ComputeHash64(&data, sizeof(data)); | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
|  |  | |||
|  | @ -1,10 +1,6 @@ | |||
| add_library(core STATIC | ||||
|     3ds.h | ||||
|     arm/arm_interface.h | ||||
|     arm/dynarmic/arm_dynarmic.cpp | ||||
|     arm/dynarmic/arm_dynarmic.h | ||||
|     arm/dynarmic/arm_dynarmic_cp15.cpp | ||||
|     arm/dynarmic/arm_dynarmic_cp15.h | ||||
|     arm/dyncom/arm_dyncom.cpp | ||||
|     arm/dyncom/arm_dyncom.h | ||||
|     arm/dyncom/arm_dyncom_dec.cpp | ||||
|  | @ -404,7 +400,17 @@ add_library(core STATIC | |||
| create_target_directory_groups(core) | ||||
| 
 | ||||
| target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) | ||||
| target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) | ||||
| target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt) | ||||
| if (ENABLE_WEB_SERVICE) | ||||
|     target_link_libraries(core PUBLIC json-headers web_service) | ||||
| endif() | ||||
| 
 | ||||
| if (ARCHITECTURE_x86_64) | ||||
|     target_sources(core PRIVATE | ||||
|         arm/dynarmic/arm_dynarmic.cpp | ||||
|         arm/dynarmic/arm_dynarmic.h | ||||
|         arm/dynarmic/arm_dynarmic_cp15.cpp | ||||
|         arm/dynarmic/arm_dynarmic_cp15.h | ||||
|     ) | ||||
|     target_link_libraries(core PRIVATE dynarmic) | ||||
| endif() | ||||
|  |  | |||
|  | @ -7,7 +7,9 @@ | |||
| #include "audio_core/audio_core.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
| #include "core/arm/dynarmic/arm_dynarmic.h" | ||||
| #endif | ||||
| #include "core/arm/dyncom/arm_dyncom.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
|  | @ -147,7 +149,12 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | |||
|     LOG_DEBUG(HW_Memory, "initialized OK"); | ||||
| 
 | ||||
|     if (Settings::values.use_cpu_jit) { | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
|         cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); | ||||
| #else | ||||
|         cpu_core = std::make_unique<ARM_DynCom>(USER32MODE); | ||||
|         LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||||
| #endif | ||||
|     } else { | ||||
|         cpu_core = std::make_unique<ARM_DynCom>(USER32MODE); | ||||
|     } | ||||
|  |  | |||
|  | @ -59,7 +59,9 @@ public: | |||
| 
 | ||||
|     /// Empty placeholder structure for services with no per-session data. The session data classes
 | ||||
|     /// in each service must inherit from this.
 | ||||
|     struct SessionDataBase {}; | ||||
|     struct SessionDataBase { | ||||
|         virtual ~SessionDataBase() = default; | ||||
|     }; | ||||
| 
 | ||||
| protected: | ||||
|     /// Creates the storage for the session data of the service.
 | ||||
|  |  | |||
|  | @ -40,6 +40,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { | |||
|     process->codeset = std::move(code_set); | ||||
|     process->flags.raw = 0; | ||||
|     process->flags.memory_region.Assign(MemoryRegion::APPLICATION); | ||||
|     process->status = ProcessStatus::Created; | ||||
| 
 | ||||
|     process_list.push_back(process); | ||||
|     return process; | ||||
|  | @ -151,6 +152,8 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | |||
|         HandleSpecialMapping(vm_manager, mapping); | ||||
|     } | ||||
| 
 | ||||
|     status = ProcessStatus::Running; | ||||
| 
 | ||||
|     vm_manager.LogLayout(Log::Level::Debug); | ||||
|     Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this); | ||||
| } | ||||
|  |  | |||
|  | @ -49,6 +49,8 @@ union ProcessFlags { | |||
|     BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
 | ||||
| }; | ||||
| 
 | ||||
| enum class ProcessStatus { Created, Running, Exited }; | ||||
| 
 | ||||
| class ResourceLimit; | ||||
| struct MemoryRegionInfo; | ||||
| 
 | ||||
|  | @ -122,6 +124,8 @@ public: | |||
|     u16 kernel_version = 0; | ||||
|     /// The default CPU for this process, threads are scheduled on this cpu by default.
 | ||||
|     u8 ideal_processor = 0; | ||||
|     /// Current status of the process
 | ||||
|     ProcessStatus status; | ||||
| 
 | ||||
|     /// The id of this process
 | ||||
|     u32 process_id = next_process_id++; | ||||
|  |  | |||
|  | @ -143,6 +143,36 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add | |||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static void ExitProcess() { | ||||
|     LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id); | ||||
| 
 | ||||
|     ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited"); | ||||
| 
 | ||||
|     g_current_process->status = ProcessStatus::Exited; | ||||
| 
 | ||||
|     // Stop all the process threads that are currently waiting for objects.
 | ||||
|     auto& thread_list = GetThreadList(); | ||||
|     for (auto& thread : thread_list) { | ||||
|         if (thread->owner_process != g_current_process) | ||||
|             continue; | ||||
| 
 | ||||
|         if (thread == GetCurrentThread()) | ||||
|             continue; | ||||
| 
 | ||||
|         // TODO(Subv): When are the other running/ready threads terminated?
 | ||||
|         ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||||
|                        thread->status == THREADSTATUS_WAIT_SYNCH_ALL, | ||||
|                    "Exiting processes with non-waiting threads is currently unimplemented"); | ||||
| 
 | ||||
|         thread->Stop(); | ||||
|     } | ||||
| 
 | ||||
|     // Kill the current thread
 | ||||
|     GetCurrentThread()->Stop(); | ||||
| 
 | ||||
|     Core::System::GetInstance().PrepareReschedule(); | ||||
| } | ||||
| 
 | ||||
| /// Maps a memory block to specified address
 | ||||
| static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { | ||||
|     LOG_TRACE(Kernel_SVC, | ||||
|  | @ -685,7 +715,7 @@ static ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limi | |||
| 
 | ||||
|     for (unsigned int i = 0; i < name_count; ++i) { | ||||
|         u32 name = Memory::Read32(names + i * sizeof(u32)); | ||||
|         s64 value = resource_limit->GetMaxResourceValue(names); | ||||
|         s64 value = resource_limit->GetMaxResourceValue(name); | ||||
|         Memory::Write64(values + i * sizeof(u64), value); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1232,7 +1262,7 @@ static const FunctionDef SVC_Table[] = { | |||
|     {0x00, nullptr, "Unknown"}, | ||||
|     {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, | ||||
|     {0x02, HLE::Wrap<QueryMemory>, "QueryMemory"}, | ||||
|     {0x03, nullptr, "ExitProcess"}, | ||||
|     {0x03, ExitProcess, "ExitProcess"}, | ||||
|     {0x04, nullptr, "GetProcessAffinityMask"}, | ||||
|     {0x05, nullptr, "SetProcessAffinityMask"}, | ||||
|     {0x06, nullptr, "GetProcessIdealProcessor"}, | ||||
|  | @ -1373,6 +1403,9 @@ void CallSVC(u32 immediate) { | |||
|     // Lock the global kernel mutex when we enter the kernel HLE.
 | ||||
|     std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||||
| 
 | ||||
|     ASSERT_MSG(g_current_process->status == ProcessStatus::Running, | ||||
|                "Running threads from exiting processes is unimplemented"); | ||||
| 
 | ||||
|     const FunctionDef* info = GetSVCInfo(immediate); | ||||
|     if (info) { | ||||
|         if (info->func) { | ||||
|  |  | |||
|  | @ -9,7 +9,6 @@ | |||
| #include "core/core.h" | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/result.h" | ||||
|  | @ -51,6 +50,20 @@ constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorM | |||
|                                            ErrorSummary::InvalidArgument, | ||||
|                                            ErrorLevel::Usage); // 0xE0E02BEC
 | ||||
| 
 | ||||
| /// Maximum number of threads that can be registered at the same time in the GSP module.
 | ||||
| constexpr u32 MaxGSPThreads = 4; | ||||
| 
 | ||||
| /// Thread ids currently in use by the sessions connected to the GSPGPU service.
 | ||||
| static std::array<bool, MaxGSPThreads> used_thread_ids = {false, false, false, false}; | ||||
| 
 | ||||
| static u32 GetUnusedThreadId() { | ||||
|     for (u32 id = 0; id < MaxGSPThreads; ++id) { | ||||
|         if (!used_thread_ids[id]) | ||||
|             return id; | ||||
|     } | ||||
|     ASSERT_MSG(false, "All GSP threads are in use"); | ||||
| } | ||||
| 
 | ||||
| /// Gets a pointer to a thread command buffer in GSP shared memory
 | ||||
| static inline u8* GetCommandBuffer(Kernel::SharedPtr<Kernel::SharedMemory> shared_memory, | ||||
|                                    u32 thread_id) { | ||||
|  | @ -319,12 +332,16 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { | |||
|     IPC::RequestParser rp(ctx, 0x13, 1, 2); | ||||
|     u32 flags = rp.Pop<u32>(); | ||||
| 
 | ||||
|     interrupt_event = rp.PopObject<Kernel::Event>(); | ||||
|     auto interrupt_event = rp.PopObject<Kernel::Event>(); | ||||
|     // TODO(mailwl): return right error code instead assert
 | ||||
|     ASSERT_MSG((interrupt_event != nullptr), "handle is not valid!"); | ||||
| 
 | ||||
|     interrupt_event->name = "GSP_GSP_GPU::interrupt_event"; | ||||
| 
 | ||||
|     SessionData* session_data = GetSessionData(ctx.Session()); | ||||
|     session_data->interrupt_event = std::move(interrupt_event); | ||||
|     session_data->registered = true; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||
| 
 | ||||
|     if (first_initialization) { | ||||
|  | @ -335,25 +352,60 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { | |||
|         rb.Push(RESULT_SUCCESS); | ||||
|     } | ||||
| 
 | ||||
|     rb.Push(thread_id); | ||||
|     rb.Push(session_data->thread_id); | ||||
|     rb.PushCopyObjects(shared_memory); | ||||
| 
 | ||||
|     thread_id++; | ||||
|     interrupt_event->Signal(); // TODO(bunnei): Is this correct?
 | ||||
| 
 | ||||
|     LOG_WARNING(Service_GSP, "called, flags=0x%08X", flags); | ||||
|     LOG_DEBUG(Service_GSP, "called, flags=0x%08X", flags); | ||||
| } | ||||
| 
 | ||||
| void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x14, 0, 0); | ||||
| 
 | ||||
|     thread_id = 0; | ||||
|     interrupt_event = nullptr; | ||||
|     SessionData* session_data = GetSessionData(ctx.Session()); | ||||
|     session_data->interrupt_event = nullptr; | ||||
|     session_data->registered = false; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_WARNING(Service_GSP, "(STUBBED) called"); | ||||
|     LOG_DEBUG(Service_GSP, "called"); | ||||
| } | ||||
| 
 | ||||
| void GSP_GPU::SignalInterruptForThread(InterruptId interrupt_id, u32 thread_id) { | ||||
|     SessionData* session_data = FindRegisteredThreadData(thread_id); | ||||
|     if (session_data == nullptr) | ||||
|         return; | ||||
| 
 | ||||
|     auto interrupt_event = session_data->interrupt_event; | ||||
|     if (interrupt_event == nullptr) { | ||||
|         LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); | ||||
|         return; | ||||
|     } | ||||
|     InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(shared_memory, thread_id); | ||||
|     u8 next = interrupt_relay_queue->index; | ||||
|     next += interrupt_relay_queue->number_interrupts; | ||||
|     next = next % 0x34; // 0x34 is the number of interrupt slots
 | ||||
| 
 | ||||
|     interrupt_relay_queue->number_interrupts += 1; | ||||
| 
 | ||||
|     interrupt_relay_queue->slot[next] = interrupt_id; | ||||
|     interrupt_relay_queue->error_code = 0x0; // No error
 | ||||
| 
 | ||||
|     // Update framebuffer information if requested
 | ||||
|     // TODO(yuriks): Confirm where this code should be called. It is definitely updated without
 | ||||
|     //               executing any GSP commands, only waiting on the event.
 | ||||
|     // TODO(Subv): The real GSP module triggers PDC0 after updating both the top and bottom
 | ||||
|     // screen, it is currently unknown what PDC1 does.
 | ||||
|     int screen_id = | ||||
|         (interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1; | ||||
|     if (screen_id != -1) { | ||||
|         FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); | ||||
|         if (info->is_dirty) { | ||||
|             GSP::SetBufferSwap(screen_id, info->framebuffer_info[info->index]); | ||||
|             info->is_dirty.Assign(false); | ||||
|         } | ||||
|     } | ||||
|     interrupt_event->Signal(); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -363,43 +415,26 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { | |||
|  * @todo This probably does not belong in the GSP module, instead move to video_core | ||||
|  */ | ||||
| void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { | ||||
|     if (!gpu_right_acquired) { | ||||
|         return; | ||||
|     } | ||||
|     if (nullptr == interrupt_event) { | ||||
|         LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); | ||||
|         return; | ||||
|     } | ||||
|     if (nullptr == shared_memory) { | ||||
|         LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); | ||||
|         return; | ||||
|     } | ||||
|     for (int thread_id = 0; thread_id < 0x4; ++thread_id) { | ||||
|         InterruptRelayQueue* interrupt_relay_queue = | ||||
|             GetInterruptRelayQueue(shared_memory, thread_id); | ||||
|         u8 next = interrupt_relay_queue->index; | ||||
|         next += interrupt_relay_queue->number_interrupts; | ||||
|         next = next % 0x34; // 0x34 is the number of interrupt slots
 | ||||
| 
 | ||||
|         interrupt_relay_queue->number_interrupts += 1; | ||||
| 
 | ||||
|         interrupt_relay_queue->slot[next] = interrupt_id; | ||||
|         interrupt_relay_queue->error_code = 0x0; // No error
 | ||||
| 
 | ||||
|         // Update framebuffer information if requested
 | ||||
|         // TODO(yuriks): Confirm where this code should be called. It is definitely updated without
 | ||||
|         //               executing any GSP commands, only waiting on the event.
 | ||||
|         int screen_id = | ||||
|             (interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1; | ||||
|         if (screen_id != -1) { | ||||
|             FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); | ||||
|             if (info->is_dirty) { | ||||
|                 GSP::SetBufferSwap(screen_id, info->framebuffer_info[info->index]); | ||||
|                 info->is_dirty.Assign(false); | ||||
|             } | ||||
|     // The PDC0 and PDC1 interrupts are fired even if the GPU right hasn't been acquired.
 | ||||
|     // Normal interrupts are only signaled for the active thread (ie, the thread that has the GPU
 | ||||
|     // right), but the PDC0/1 interrupts are signaled for every registered thread.
 | ||||
|     if (interrupt_id == InterruptId::PDC0 || interrupt_id == InterruptId::PDC1) { | ||||
|         for (u32 thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) { | ||||
|             SignalInterruptForThread(interrupt_id, thread_id); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     interrupt_event->Signal(); | ||||
| 
 | ||||
|     // For normal interrupts, don't do anything if no process has acquired the GPU right.
 | ||||
|     if (active_thread_id == -1) | ||||
|         return; | ||||
| 
 | ||||
|     SignalInterruptForThread(interrupt_id, active_thread_id); | ||||
| } | ||||
| 
 | ||||
| MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); | ||||
|  | @ -622,18 +657,34 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { | |||
|     u32 flag = rp.Pop<u32>(); | ||||
|     auto process = rp.PopObject<Kernel::Process>(); | ||||
| 
 | ||||
|     gpu_right_acquired = true; | ||||
|     SessionData* session_data = GetSessionData(ctx.Session()); | ||||
| 
 | ||||
|     LOG_WARNING(Service_GSP, "called flag=%08X process=%u thread_id=%u", flag, process->process_id, | ||||
|                 session_data->thread_id); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_WARNING(Service_GSP, "called flag=%08X process=%u", flag, process->process_id); | ||||
|     if (active_thread_id == session_data->thread_id) { | ||||
|         rb.Push(ResultCode(ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success, | ||||
|                            ErrorLevel::Success)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // TODO(Subv): This case should put the caller thread to sleep until the right is released.
 | ||||
|     ASSERT_MSG(active_thread_id == -1, "GPU right has already been acquired"); | ||||
| 
 | ||||
|     active_thread_id = session_data->thread_id; | ||||
| 
 | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| } | ||||
| 
 | ||||
| void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x17, 0, 0); | ||||
| 
 | ||||
|     gpu_right_acquired = false; | ||||
|     SessionData* session_data = GetSessionData(ctx.Session()); | ||||
|     ASSERT_MSG(active_thread_id == session_data->thread_id, | ||||
|                "Wrong thread tried to release GPU right"); | ||||
|     active_thread_id = -1; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|  | @ -655,6 +706,17 @@ void GSP_GPU::StoreDataCache(Kernel::HLERequestContext& ctx) { | |||
|               size, process->process_id); | ||||
| } | ||||
| 
 | ||||
| SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) { | ||||
|     for (auto& session_info : connected_sessions) { | ||||
|         SessionData* data = static_cast<SessionData*>(session_info.data.get()); | ||||
|         if (!data->registered) | ||||
|             continue; | ||||
|         if (data->thread_id == thread_id) | ||||
|             return data; | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0x00010082, &GSP_GPU::WriteHWRegs, "WriteHWRegs"}, | ||||
|  | @ -691,17 +753,26 @@ GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { | |||
|     }; | ||||
|     RegisterHandlers(functions); | ||||
| 
 | ||||
|     interrupt_event = nullptr; | ||||
| 
 | ||||
|     using Kernel::MemoryPermission; | ||||
|     shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, | ||||
|                                                  MemoryPermission::ReadWrite, 0, | ||||
|                                                  Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); | ||||
| 
 | ||||
|     thread_id = 0; | ||||
|     gpu_right_acquired = false; | ||||
|     first_initialization = true; | ||||
| }; | ||||
| 
 | ||||
| SessionData::SessionData() { | ||||
|     // Assign a new thread id to this session when it connects. Note: In the real GSP service this
 | ||||
|     // is done through a real thread (svcCreateThread) but we have to simulate it since our HLE
 | ||||
|     // services don't have threads.
 | ||||
|     thread_id = GetUnusedThreadId(); | ||||
|     used_thread_ids[thread_id] = true; | ||||
| } | ||||
| 
 | ||||
| SessionData::~SessionData() { | ||||
|     // Free the thread id slot so that other sessions can use it.
 | ||||
|     used_thread_ids[thread_id] = false; | ||||
| } | ||||
| 
 | ||||
| } // namespace GSP
 | ||||
| } // namespace Service
 | ||||
|  |  | |||
|  | @ -8,12 +8,12 @@ | |||
| #include <string> | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| class Event; | ||||
| class SharedMemory; | ||||
| } // namespace Kernel
 | ||||
| 
 | ||||
|  | @ -179,7 +179,19 @@ struct CommandBuffer { | |||
| }; | ||||
| static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size"); | ||||
| 
 | ||||
| class GSP_GPU final : public ServiceFramework<GSP_GPU> { | ||||
| struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { | ||||
|     SessionData(); | ||||
|     ~SessionData(); | ||||
| 
 | ||||
|     /// Event triggered when GSP interrupt has been signalled
 | ||||
|     Kernel::SharedPtr<Kernel::Event> interrupt_event; | ||||
|     /// Thread index into interrupt relay queue
 | ||||
|     u32 thread_id; | ||||
|     /// Whether RegisterInterruptRelayQueue was called for this session
 | ||||
|     bool registered = false; | ||||
| }; | ||||
| 
 | ||||
| class GSP_GPU final : public ServiceFramework<GSP_GPU, SessionData> { | ||||
| public: | ||||
|     GSP_GPU(); | ||||
|     ~GSP_GPU() = default; | ||||
|  | @ -201,6 +213,14 @@ public: | |||
|     FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * Signals that the specified interrupt type has occurred to userland code for the specified GSP | ||||
|      * thread id. | ||||
|      * @param interrupt_id ID of interrupt that is being signalled. | ||||
|      * @param thread_id GSP thread that will receive the interrupt. | ||||
|      */ | ||||
|     void SignalInterruptForThread(InterruptId interrupt_id, u32 thread_id); | ||||
| 
 | ||||
|     /**
 | ||||
|      * GSP_GPU::WriteHWRegs service function | ||||
|      * | ||||
|  | @ -351,14 +371,15 @@ private: | |||
|      */ | ||||
|     void StoreDataCache(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|     /// Event triggered when GSP interrupt has been signalled
 | ||||
|     Kernel::SharedPtr<Kernel::Event> interrupt_event; | ||||
|     /// GSP shared memoryings
 | ||||
|     Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; | ||||
|     /// Thread index into interrupt relay queue
 | ||||
|     u32 thread_id = 0; | ||||
|     /// Returns the session data for the specified registered thread id, or nullptr if not found.
 | ||||
|     SessionData* FindRegisteredThreadData(u32 thread_id); | ||||
| 
 | ||||
|     /// GSP shared memory
 | ||||
|     Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; | ||||
| 
 | ||||
|     /// Thread id that currently has GPU rights or -1 if none.
 | ||||
|     int active_thread_id = -1; | ||||
| 
 | ||||
|     bool gpu_right_acquired = false; | ||||
|     bool first_initialization = true; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,15 +3,12 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <atomic> | ||||
| #include <cmath> | ||||
| #include <memory> | ||||
| #include "common/logging/log.h" | ||||
| #include "core/3ds.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/frontend/input.h" | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
|  | @ -23,27 +20,7 @@ | |||
| namespace Service { | ||||
| namespace HID { | ||||
| 
 | ||||
| // Handle to shared memory region designated to HID_User service
 | ||||
| static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; | ||||
| 
 | ||||
| // Event handles
 | ||||
| static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1; | ||||
| static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2; | ||||
| static Kernel::SharedPtr<Kernel::Event> event_accelerometer; | ||||
| static Kernel::SharedPtr<Kernel::Event> event_gyroscope; | ||||
| static Kernel::SharedPtr<Kernel::Event> event_debug_pad; | ||||
| 
 | ||||
| static u32 next_pad_index; | ||||
| static u32 next_touch_index; | ||||
| static u32 next_accelerometer_index; | ||||
| static u32 next_gyroscope_index; | ||||
| 
 | ||||
| static int enable_accelerometer_count; // positive means enabled
 | ||||
| static int enable_gyroscope_count;     // positive means enabled
 | ||||
| 
 | ||||
| static CoreTiming::EventType* pad_update_event; | ||||
| static CoreTiming::EventType* accelerometer_update_event; | ||||
| static CoreTiming::EventType* gyroscope_update_event; | ||||
| static std::weak_ptr<Module> current_module; | ||||
| 
 | ||||
| // Updating period for each HID device. These empirical values are measured from a 11.2 3DS.
 | ||||
| constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; | ||||
|  | @ -53,13 +30,6 @@ constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; | |||
| constexpr float accelerometer_coef = 512.0f; // measured from hw test result
 | ||||
| constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
 | ||||
| 
 | ||||
| static std::atomic<bool> is_device_reload_pending; | ||||
| static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | ||||
|     buttons; | ||||
| static std::unique_ptr<Input::AnalogDevice> circle_pad; | ||||
| static std::unique_ptr<Input::MotionDevice> motion_device; | ||||
| static std::unique_ptr<Input::TouchDevice> touch_device; | ||||
| 
 | ||||
| DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | ||||
|     // 30 degree and 60 degree are angular thresholds for directions
 | ||||
|     constexpr float TAN30 = 0.577350269f; | ||||
|  | @ -89,7 +59,7 @@ DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | |||
|     return state; | ||||
| } | ||||
| 
 | ||||
| static void LoadInputDevices() { | ||||
| void Module::LoadInputDevices() { | ||||
|     std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | ||||
|                    Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, | ||||
|                    buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||||
|  | @ -99,16 +69,7 @@ static void LoadInputDevices() { | |||
|     touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); | ||||
| } | ||||
| 
 | ||||
| static void UnloadInputDevices() { | ||||
|     for (auto& button : buttons) { | ||||
|         button.reset(); | ||||
|     } | ||||
|     circle_pad.reset(); | ||||
|     motion_device.reset(); | ||||
|     touch_device.reset(); | ||||
| } | ||||
| 
 | ||||
| static void UpdatePadCallback(u64 userdata, int cycles_late) { | ||||
| void Module::UpdatePadCallback(u64 userdata, int cycles_late) { | ||||
|     SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); | ||||
| 
 | ||||
|     if (is_device_reload_pending.exchange(false)) | ||||
|  | @ -198,7 +159,7 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) { | |||
|     CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); | ||||
| } | ||||
| 
 | ||||
| static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) { | ||||
| void Module::UpdateAccelerometerCallback(u64 userdata, int cycles_late) { | ||||
|     SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); | ||||
| 
 | ||||
|     mem->accelerometer.index = next_accelerometer_index; | ||||
|  | @ -240,7 +201,7 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) { | |||
|     CoreTiming::ScheduleEvent(accelerometer_update_ticks - cycles_late, accelerometer_update_event); | ||||
| } | ||||
| 
 | ||||
| static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { | ||||
| void Module::UpdateGyroscopeCallback(u64 userdata, int cycles_late) { | ||||
|     SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); | ||||
| 
 | ||||
|     mem->gyroscope.index = next_gyroscope_index; | ||||
|  | @ -273,134 +234,122 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { | |||
|     CoreTiming::ScheduleEvent(gyroscope_update_ticks - cycles_late, gyroscope_update_event); | ||||
| } | ||||
| 
 | ||||
| void GetIPCHandles(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| 
 | ||||
|     cmd_buff[1] = 0;          // No error
 | ||||
|     cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header
 | ||||
|     // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
 | ||||
|     cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).Unwrap(); | ||||
|     cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).Unwrap(); | ||||
|     cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).Unwrap(); | ||||
|     cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).Unwrap(); | ||||
|     cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).Unwrap(); | ||||
|     cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).Unwrap(); | ||||
| void Module::Interface::GetIPCHandles(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0xA, 0, 0}; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 7); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushCopyObjects(hid->shared_mem, hid->event_pad_or_touch_1, hid->event_pad_or_touch_2, | ||||
|                        hid->event_accelerometer, hid->event_gyroscope, hid->event_debug_pad); | ||||
| } | ||||
| 
 | ||||
| void EnableAccelerometer(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| void Module::Interface::EnableAccelerometer(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0x11, 0, 0}; | ||||
| 
 | ||||
|     ++enable_accelerometer_count; | ||||
|     ++hid->enable_accelerometer_count; | ||||
| 
 | ||||
|     // Schedules the accelerometer update event if the accelerometer was just enabled
 | ||||
|     if (enable_accelerometer_count == 1) { | ||||
|         CoreTiming::ScheduleEvent(accelerometer_update_ticks, accelerometer_update_event); | ||||
|     if (hid->enable_accelerometer_count == 1) { | ||||
|         CoreTiming::ScheduleEvent(accelerometer_update_ticks, hid->accelerometer_update_event); | ||||
|     } | ||||
| 
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
| void DisableAccelerometer(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| void Module::Interface::DisableAccelerometer(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0x12, 0, 0}; | ||||
| 
 | ||||
|     --enable_accelerometer_count; | ||||
|     --hid->enable_accelerometer_count; | ||||
| 
 | ||||
|     // Unschedules the accelerometer update event if the accelerometer was just disabled
 | ||||
|     if (enable_accelerometer_count == 0) { | ||||
|         CoreTiming::UnscheduleEvent(accelerometer_update_event, 0); | ||||
|     if (hid->enable_accelerometer_count == 0) { | ||||
|         CoreTiming::UnscheduleEvent(hid->accelerometer_update_event, 0); | ||||
|     } | ||||
| 
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
| void EnableGyroscopeLow(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| void Module::Interface::EnableGyroscopeLow(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0x13, 0, 0}; | ||||
| 
 | ||||
|     ++enable_gyroscope_count; | ||||
|     ++hid->enable_gyroscope_count; | ||||
| 
 | ||||
|     // Schedules the gyroscope update event if the gyroscope was just enabled
 | ||||
|     if (enable_gyroscope_count == 1) { | ||||
|         CoreTiming::ScheduleEvent(gyroscope_update_ticks, gyroscope_update_event); | ||||
|     if (hid->enable_gyroscope_count == 1) { | ||||
|         CoreTiming::ScheduleEvent(gyroscope_update_ticks, hid->gyroscope_update_event); | ||||
|     } | ||||
| 
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
| void DisableGyroscopeLow(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| void Module::Interface::DisableGyroscopeLow(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0x14, 0, 0}; | ||||
| 
 | ||||
|     --enable_gyroscope_count; | ||||
|     --hid->enable_gyroscope_count; | ||||
| 
 | ||||
|     // Unschedules the gyroscope update event if the gyroscope was just disabled
 | ||||
|     if (enable_gyroscope_count == 0) { | ||||
|         CoreTiming::UnscheduleEvent(gyroscope_update_event, 0); | ||||
|     if (hid->enable_gyroscope_count == 0) { | ||||
|         CoreTiming::UnscheduleEvent(hid->gyroscope_update_event, 0); | ||||
|     } | ||||
| 
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_HID, "called"); | ||||
| } | ||||
| 
 | ||||
| void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| void Module::Interface::GetGyroscopeLowRawToDpsCoefficient(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0x15, 0, 0}; | ||||
| 
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
| 
 | ||||
|     f32 coef = gyroscope_coef; | ||||
|     memcpy(&cmd_buff[2], &coef, 4); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushRaw<f32>(gyroscope_coef); | ||||
| } | ||||
| 
 | ||||
| void GetGyroscopeLowCalibrateParam(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| void Module::Interface::GetGyroscopeLowCalibrateParam(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0x16, 0, 0}; | ||||
| 
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(6, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     const s16 param_unit = 6700; // an approximate value taken from hw
 | ||||
|     GyroscopeCalibrateParam param = { | ||||
|         {0, param_unit, -param_unit}, {0, param_unit, -param_unit}, {0, param_unit, -param_unit}, | ||||
|     }; | ||||
|     memcpy(&cmd_buff[2], ¶m, sizeof(param)); | ||||
|     rb.PushRaw(param); | ||||
| 
 | ||||
|     LOG_WARNING(Service_HID, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| void GetSoundVolume(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
| void Module::Interface::GetSoundVolume(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx, 0x17, 0, 0}; | ||||
| 
 | ||||
|     const u8 volume = 0x3F; // TODO(purpasmart): Find out if this is the max value for the volume
 | ||||
| 
 | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = volume; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(volume); | ||||
| 
 | ||||
|     LOG_WARNING(Service_HID, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| void Init() { | ||||
| Module::Interface::Interface(std::shared_ptr<Module> hid, const char* name, u32 max_session) | ||||
|     : ServiceFramework(name, max_session), hid(std::move(hid)) {} | ||||
| 
 | ||||
| Module::Module() { | ||||
|     using namespace Kernel; | ||||
| 
 | ||||
|     AddService(new HID_U_Interface); | ||||
|     AddService(new HID_SPVR_Interface); | ||||
| 
 | ||||
|     is_device_reload_pending.store(true); | ||||
| 
 | ||||
|     using Kernel::MemoryPermission; | ||||
|     shared_mem = | ||||
|         SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, | ||||
|                              0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); | ||||
| 
 | ||||
|     next_pad_index = 0; | ||||
|     next_touch_index = 0; | ||||
|     next_accelerometer_index = 0; | ||||
|     next_gyroscope_index = 0; | ||||
| 
 | ||||
|     enable_accelerometer_count = 0; | ||||
|     enable_gyroscope_count = 0; | ||||
|                              0, MemoryRegion::BASE, "HID:SharedMemory"); | ||||
| 
 | ||||
|     // Create event handles
 | ||||
|     event_pad_or_touch_1 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch1"); | ||||
|  | @ -410,27 +359,35 @@ void Init() { | |||
|     event_debug_pad = Event::Create(ResetType::OneShot, "HID:EventDebugPad"); | ||||
| 
 | ||||
|     // Register update callbacks
 | ||||
|     pad_update_event = CoreTiming::RegisterEvent("HID::UpdatePadCallback", UpdatePadCallback); | ||||
|     accelerometer_update_event = | ||||
|         CoreTiming::RegisterEvent("HID::UpdateAccelerometerCallback", UpdateAccelerometerCallback); | ||||
|     gyroscope_update_event = | ||||
|         CoreTiming::RegisterEvent("HID::UpdateGyroscopeCallback", UpdateGyroscopeCallback); | ||||
|     pad_update_event = | ||||
|         CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) { | ||||
|             UpdatePadCallback(userdata, cycles_late); | ||||
|         }); | ||||
|     accelerometer_update_event = CoreTiming::RegisterEvent( | ||||
|         "HID::UpdateAccelerometerCallback", [this](u64 userdata, int cycles_late) { | ||||
|             UpdateAccelerometerCallback(userdata, cycles_late); | ||||
|         }); | ||||
|     gyroscope_update_event = CoreTiming::RegisterEvent( | ||||
|         "HID::UpdateGyroscopeCallback", | ||||
|         [this](u64 userdata, int cycles_late) { UpdateGyroscopeCallback(userdata, cycles_late); }); | ||||
| 
 | ||||
|     CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); | ||||
| } | ||||
| 
 | ||||
| void Shutdown() { | ||||
|     shared_mem = nullptr; | ||||
|     event_pad_or_touch_1 = nullptr; | ||||
|     event_pad_or_touch_2 = nullptr; | ||||
|     event_accelerometer = nullptr; | ||||
|     event_gyroscope = nullptr; | ||||
|     event_debug_pad = nullptr; | ||||
|     UnloadInputDevices(); | ||||
| void Module::ReloadInputDevices() { | ||||
|     is_device_reload_pending.store(true); | ||||
| } | ||||
| 
 | ||||
| void ReloadInputDevices() { | ||||
|     is_device_reload_pending.store(true); | ||||
|     if (auto hid = current_module.lock()) | ||||
|         hid->ReloadInputDevices(); | ||||
| } | ||||
| 
 | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager) { | ||||
|     auto hid = std::make_shared<Module>(); | ||||
|     std::make_shared<User>(hid)->InstallAsService(service_manager); | ||||
|     std::make_shared<Spvr>(hid)->InstallAsService(service_manager); | ||||
|     current_module = hid; | ||||
| } | ||||
| 
 | ||||
| } // namespace HID
 | ||||
|  |  | |||
|  | @ -5,17 +5,29 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <atomic> | ||||
| #ifndef _MSC_VER | ||||
| #include <cstddef> | ||||
| #endif | ||||
| #include <memory> | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/frontend/input.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace Kernel { | ||||
| class Event; | ||||
| class SharedMemory; | ||||
| } | ||||
| 
 | ||||
| class Interface; | ||||
| namespace CoreTiming { | ||||
| class EventType; | ||||
| }; | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| namespace HID { | ||||
| 
 | ||||
|  | @ -186,93 +198,140 @@ struct DirectionState { | |||
| /// Translates analog stick axes to directions. This is exposed for ir_rst module to use.
 | ||||
| DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::GetIPCHandles service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  *      2 : IPC Command Structure translate-header | ||||
|  *      3 : Handle to HID shared memory | ||||
|  *      4 : Event signaled by HID | ||||
|  *      5 : Event signaled by HID | ||||
|  *      6 : Event signaled by HID | ||||
|  *      7 : Gyroscope event | ||||
|  *      8 : Event signaled by HID | ||||
|  */ | ||||
| void GetIPCHandles(Interface* self); | ||||
| class Module final { | ||||
| public: | ||||
|     Module(); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::EnableAccelerometer service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| void EnableAccelerometer(Interface* self); | ||||
|     class Interface : public ServiceFramework<Interface> { | ||||
|     public: | ||||
|         Interface(std::shared_ptr<Module> hid, const char* name, u32 max_session); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::DisableAccelerometer service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| void DisableAccelerometer(Interface* self); | ||||
|     protected: | ||||
|         /**
 | ||||
|          * HID::GetIPCHandles service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *      2 : IPC Command Structure translate-header | ||||
|          *      3 : Handle to HID shared memory | ||||
|          *      4 : Event signaled by HID | ||||
|          *      5 : Event signaled by HID | ||||
|          *      6 : Event signaled by HID | ||||
|          *      7 : Gyroscope event | ||||
|          *      8 : Event signaled by HID | ||||
|          */ | ||||
|         void GetIPCHandles(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::EnableGyroscopeLow service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| void EnableGyroscopeLow(Interface* self); | ||||
|         /**
 | ||||
|          * HID::EnableAccelerometer service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          */ | ||||
|         void EnableAccelerometer(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::DisableGyroscopeLow service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| void DisableGyroscopeLow(Interface* self); | ||||
|         /**
 | ||||
|          * HID::DisableAccelerometer service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          */ | ||||
|         void DisableAccelerometer(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::GetSoundVolume service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  *      2 : u8 output value | ||||
|  */ | ||||
| void GetSoundVolume(Interface* self); | ||||
|         /**
 | ||||
|          * HID::EnableGyroscopeLow service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          */ | ||||
|         void EnableGyroscopeLow(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::GetGyroscopeLowRawToDpsCoefficient service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  *      2 : float output value | ||||
|  */ | ||||
| void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self); | ||||
|         /**
 | ||||
|          * HID::DisableGyroscopeLow service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          */ | ||||
|         void DisableGyroscopeLow(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
| /**
 | ||||
|  * HID::GetGyroscopeLowCalibrateParam service function | ||||
|  *  Inputs: | ||||
|  *      None | ||||
|  *  Outputs: | ||||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  *      2~6 (18 bytes) : struct GyroscopeCalibrateParam | ||||
|  */ | ||||
| void GetGyroscopeLowCalibrateParam(Service::Interface* self); | ||||
|         /**
 | ||||
|          * HID::GetSoundVolume service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *      2 : u8 output value | ||||
|          */ | ||||
|         void GetSoundVolume(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
| /// Initialize HID service
 | ||||
| void Init(); | ||||
|         /**
 | ||||
|          * HID::GetGyroscopeLowRawToDpsCoefficient service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *      2 : float output value | ||||
|          */ | ||||
|         void GetGyroscopeLowRawToDpsCoefficient(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
| /// Shutdown HID service
 | ||||
| void Shutdown(); | ||||
|         /**
 | ||||
|          * HID::GetGyroscopeLowCalibrateParam service function | ||||
|          *  Inputs: | ||||
|          *      None | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *      2~6 (18 bytes) : struct GyroscopeCalibrateParam | ||||
|          */ | ||||
|         void GetGyroscopeLowCalibrateParam(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|     private: | ||||
|         std::shared_ptr<Module> hid; | ||||
|     }; | ||||
| 
 | ||||
|     void ReloadInputDevices(); | ||||
| 
 | ||||
| private: | ||||
|     void LoadInputDevices(); | ||||
|     void UpdatePadCallback(u64 userdata, int cycles_late); | ||||
|     void UpdateAccelerometerCallback(u64 userdata, int cycles_late); | ||||
|     void UpdateGyroscopeCallback(u64 userdata, int cycles_late); | ||||
| 
 | ||||
|     // Handle to shared memory region designated to HID_User service
 | ||||
|     Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; | ||||
| 
 | ||||
|     // Event handles
 | ||||
|     Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1; | ||||
|     Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2; | ||||
|     Kernel::SharedPtr<Kernel::Event> event_accelerometer; | ||||
|     Kernel::SharedPtr<Kernel::Event> event_gyroscope; | ||||
|     Kernel::SharedPtr<Kernel::Event> event_debug_pad; | ||||
| 
 | ||||
|     u32 next_pad_index = 0; | ||||
|     u32 next_touch_index = 0; | ||||
|     u32 next_accelerometer_index = 0; | ||||
|     u32 next_gyroscope_index = 0; | ||||
| 
 | ||||
|     int enable_accelerometer_count = 0; // positive means enabled
 | ||||
|     int enable_gyroscope_count = 0;     // positive means enabled
 | ||||
| 
 | ||||
|     CoreTiming::EventType* pad_update_event; | ||||
|     CoreTiming::EventType* accelerometer_update_event; | ||||
|     CoreTiming::EventType* gyroscope_update_event; | ||||
| 
 | ||||
|     std::atomic<bool> is_device_reload_pending{true}; | ||||
|     std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | ||||
|         buttons; | ||||
|     std::unique_ptr<Input::AnalogDevice> circle_pad; | ||||
|     std::unique_ptr<Input::MotionDevice> motion_device; | ||||
|     std::unique_ptr<Input::TouchDevice> touch_device; | ||||
| }; | ||||
| 
 | ||||
| void InstallInterfaces(SM::ServiceManager& service_manager); | ||||
| 
 | ||||
| /// Reload input devices. Used when input configuration changed
 | ||||
| void ReloadInputDevices(); | ||||
|  |  | |||
|  | @ -2,27 +2,26 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/hle/service/hid/hid.h" | ||||
| #include "core/hle/service/hid/hid_spvr.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace HID { | ||||
| 
 | ||||
| const Interface::FunctionInfo FunctionTable[] = { | ||||
|     {0x000A0000, GetIPCHandles, "GetIPCHandles"}, | ||||
|     {0x000B0000, nullptr, "StartAnalogStickCalibration"}, | ||||
|     {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, | ||||
|     {0x00110000, EnableAccelerometer, "EnableAccelerometer"}, | ||||
|     {0x00120000, DisableAccelerometer, "DisableAccelerometer"}, | ||||
|     {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"}, | ||||
|     {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"}, | ||||
|     {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"}, | ||||
|     {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"}, | ||||
|     {0x00170000, GetSoundVolume, "GetSoundVolume"}, | ||||
| }; | ||||
| 
 | ||||
| HID_SPVR_Interface::HID_SPVR_Interface() { | ||||
|     Register(FunctionTable); | ||||
| Spvr::Spvr(std::shared_ptr<Module> hid) : Module::Interface(std::move(hid), "hid:SPVR", 6) { | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0x000A0000, &Spvr::GetIPCHandles, "GetIPCHandles"}, | ||||
|         {0x000B0000, nullptr, "StartAnalogStickCalibration"}, | ||||
|         {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, | ||||
|         {0x00110000, &Spvr::EnableAccelerometer, "EnableAccelerometer"}, | ||||
|         {0x00120000, &Spvr::DisableAccelerometer, "DisableAccelerometer"}, | ||||
|         {0x00130000, &Spvr::EnableGyroscopeLow, "EnableGyroscopeLow"}, | ||||
|         {0x00140000, &Spvr::DisableGyroscopeLow, "DisableGyroscopeLow"}, | ||||
|         {0x00150000, &Spvr::GetGyroscopeLowRawToDpsCoefficient, | ||||
|          "GetGyroscopeLowRawToDpsCoefficient"}, | ||||
|         {0x00160000, &Spvr::GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"}, | ||||
|         {0x00170000, &Spvr::GetSoundVolume, "GetSoundVolume"}, | ||||
|     }; | ||||
|     RegisterHandlers(functions); | ||||
| } | ||||
| 
 | ||||
| } // namespace HID
 | ||||
|  |  | |||
|  | @ -4,19 +4,15 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/hle/service/hid/hid.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace HID { | ||||
| 
 | ||||
| class HID_SPVR_Interface : public Service::Interface { | ||||
| class Spvr final : public Module::Interface { | ||||
| public: | ||||
|     HID_SPVR_Interface(); | ||||
| 
 | ||||
|     std::string GetPortName() const override { | ||||
|         return "hid:SPVR"; | ||||
|     } | ||||
|     explicit Spvr(std::shared_ptr<Module> hid); | ||||
| }; | ||||
| 
 | ||||
| } // namespace HID
 | ||||
| } // namespace Service
 | ||||
| } // namespace Service
 | ||||
|  |  | |||
|  | @ -2,27 +2,26 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/hle/service/hid/hid.h" | ||||
| #include "core/hle/service/hid/hid_user.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace HID { | ||||
| 
 | ||||
| const Interface::FunctionInfo FunctionTable[] = { | ||||
|     {0x000A0000, GetIPCHandles, "GetIPCHandles"}, | ||||
|     {0x000B0000, nullptr, "StartAnalogStickCalibration"}, | ||||
|     {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, | ||||
|     {0x00110000, EnableAccelerometer, "EnableAccelerometer"}, | ||||
|     {0x00120000, DisableAccelerometer, "DisableAccelerometer"}, | ||||
|     {0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"}, | ||||
|     {0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"}, | ||||
|     {0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"}, | ||||
|     {0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"}, | ||||
|     {0x00170000, GetSoundVolume, "GetSoundVolume"}, | ||||
| }; | ||||
| 
 | ||||
| HID_U_Interface::HID_U_Interface() { | ||||
|     Register(FunctionTable); | ||||
| User::User(std::shared_ptr<Module> hid) : Module::Interface(std::move(hid), "hid:USER", 6) { | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0x000A0000, &User::GetIPCHandles, "GetIPCHandles"}, | ||||
|         {0x000B0000, nullptr, "StartAnalogStickCalibration"}, | ||||
|         {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, | ||||
|         {0x00110000, &User::EnableAccelerometer, "EnableAccelerometer"}, | ||||
|         {0x00120000, &User::DisableAccelerometer, "DisableAccelerometer"}, | ||||
|         {0x00130000, &User::EnableGyroscopeLow, "EnableGyroscopeLow"}, | ||||
|         {0x00140000, &User::DisableGyroscopeLow, "DisableGyroscopeLow"}, | ||||
|         {0x00150000, &User::GetGyroscopeLowRawToDpsCoefficient, | ||||
|          "GetGyroscopeLowRawToDpsCoefficient"}, | ||||
|         {0x00160000, &User::GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"}, | ||||
|         {0x00170000, &User::GetSoundVolume, "GetSoundVolume"}, | ||||
|     }; | ||||
|     RegisterHandlers(functions); | ||||
| } | ||||
| 
 | ||||
| } // namespace HID
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/hle/service/hid/hid.h" | ||||
| 
 | ||||
| // This service is used for interfacing to physical user controls.
 | ||||
| // Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad.
 | ||||
|  | @ -12,17 +12,10 @@ | |||
| namespace Service { | ||||
| namespace HID { | ||||
| 
 | ||||
| /**
 | ||||
|  * HID service interface. | ||||
|  */ | ||||
| class HID_U_Interface : public Service::Interface { | ||||
| class User final : public Module::Interface { | ||||
| public: | ||||
|     HID_U_Interface(); | ||||
| 
 | ||||
|     std::string GetPortName() const override { | ||||
|         return "hid:USER"; | ||||
|     } | ||||
|     explicit User(std::shared_ptr<Module> hid); | ||||
| }; | ||||
| 
 | ||||
| } // namespace HID
 | ||||
| } // namespace Service
 | ||||
| } // namespace Service
 | ||||
|  |  | |||
|  | @ -277,7 +277,7 @@ void Init() { | |||
|     DLP::Init(); | ||||
|     FRD::Init(); | ||||
|     GSP::InstallInterfaces(*SM::g_service_manager); | ||||
|     HID::Init(); | ||||
|     HID::InstallInterfaces(*SM::g_service_manager); | ||||
|     IR::InstallInterfaces(*SM::g_service_manager); | ||||
|     MVD::Init(); | ||||
|     NDM::Init(); | ||||
|  | @ -307,7 +307,6 @@ void Shutdown() { | |||
|     NIM::Shutdown(); | ||||
|     NEWS::Shutdown(); | ||||
|     NDM::Shutdown(); | ||||
|     HID::Shutdown(); | ||||
|     FRD::Shutdown(); | ||||
|     DLP::Shutdown(); | ||||
|     CFG::Shutdown(); | ||||
|  |  | |||
|  | @ -84,86 +84,86 @@ ResultCode ConversionConfiguration::SetStandardCoefficient( | |||
| } | ||||
| 
 | ||||
| static void SetInputFormat(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1, 1, 0); | ||||
| 
 | ||||
|     conversion.input_format = static_cast<InputFormat>(cmd_buff[1]); | ||||
|     conversion.input_format = rp.PopEnum<InputFormat>(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast<u8>(conversion.input_format)); | ||||
| } | ||||
| 
 | ||||
| static void GetInputFormat(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = static_cast<u32>(conversion.input_format); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushEnum(conversion.input_format); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast<u8>(conversion.input_format)); | ||||
| } | ||||
| 
 | ||||
| static void SetOutputFormat(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); | ||||
| 
 | ||||
|     conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]); | ||||
|     conversion.output_format = rp.PopEnum<OutputFormat>(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast<u8>(conversion.output_format)); | ||||
| } | ||||
| 
 | ||||
| static void GetOutputFormat(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = static_cast<u32>(conversion.output_format); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushEnum(conversion.output_format); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast<u8>(conversion.output_format)); | ||||
| } | ||||
| 
 | ||||
| static void SetRotation(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 1, 0); | ||||
| 
 | ||||
|     conversion.rotation = static_cast<Rotation>(cmd_buff[1]); | ||||
|     conversion.rotation = rp.PopEnum<Rotation>(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast<u8>(conversion.rotation)); | ||||
| } | ||||
| 
 | ||||
| static void GetRotation(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = static_cast<u32>(conversion.rotation); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushEnum(conversion.rotation); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast<u8>(conversion.rotation)); | ||||
| } | ||||
| 
 | ||||
| static void SetBlockAlignment(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x7, 1, 0); | ||||
| 
 | ||||
|     conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); | ||||
|     conversion.block_alignment = rp.PopEnum<BlockAlignment>(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", | ||||
|               static_cast<u8>(conversion.block_alignment)); | ||||
| } | ||||
| 
 | ||||
| static void GetBlockAlignment(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x8, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = static_cast<u32>(conversion.block_alignment); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushEnum(conversion.block_alignment); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", | ||||
|               static_cast<u8>(conversion.block_alignment)); | ||||
|  | @ -177,11 +177,12 @@ static void GetBlockAlignment(Interface* self) { | |||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void SetSpacialDithering(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     spacial_dithering_enabled = cmd_buff[1] & 0xF; | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     spacial_dithering_enabled = rp.Pop<u8>() & 0xF; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -193,7 +194,9 @@ static void SetSpacialDithering(Interface* self) { | |||
|  *      2 : u8, 0 = Disabled, 1 = Enabled | ||||
|  */ | ||||
| static void GetSpacialDithering(Interface* self) { | ||||
|     IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0xA, 2, 0); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xA, 0, 0); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(spacial_dithering_enabled != 0); | ||||
| 
 | ||||
|  | @ -208,11 +211,11 @@ static void GetSpacialDithering(Interface* self) { | |||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void SetTemporalDithering(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     temporal_dithering_enabled = cmd_buff[1] & 0xF; | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 1, 0); | ||||
|     temporal_dithering_enabled = rp.Pop<u8>() & 0xF; | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -224,11 +227,11 @@ static void SetTemporalDithering(Interface* self) { | |||
|  *      2 : u8, 0 = Disabled, 1 = Enabled | ||||
|  */ | ||||
| static void GetTemporalDithering(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = temporal_dithering_enabled; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(temporal_dithering_enabled); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -241,11 +244,11 @@ static void GetTemporalDithering(Interface* self) { | |||
|  *      1 : Result of function, 0 on success, otherwise error code | ||||
|  */ | ||||
| static void SetTransferEndInterrupt(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     transfer_end_interrupt_enabled = cmd_buff[1] & 0xf; | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 1, 0); | ||||
|     transfer_end_interrupt_enabled = rp.Pop<u8>() & 0xF; | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -257,11 +260,11 @@ static void SetTransferEndInterrupt(Interface* self) { | |||
|  *      2 : u8, 0 = Disabled, 1 = Enabled | ||||
|  */ | ||||
| static void GetTransferEndInterrupt(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = transfer_end_interrupt_enabled; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(transfer_end_interrupt_enabled); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -273,18 +276,18 @@ static void GetTransferEndInterrupt(Interface* self) { | |||
|  *      3 : The handle of the completion event | ||||
|  */ | ||||
| static void GetTransferEndEvent(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap(); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushCopyHandles(Kernel::g_handle_table.Create(completion_event).Unwrap()); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called"); | ||||
| } | ||||
| 
 | ||||
| static void SetSendingY(Interface* self) { | ||||
|     // The helper should be passed by argument to the function
 | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00100102); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x10, 4, 2); | ||||
|     conversion.src_Y.address = rp.Pop<u32>(); | ||||
|     conversion.src_Y.image_size = rp.Pop<u32>(); | ||||
|     conversion.src_Y.transfer_unit = rp.Pop<u32>(); | ||||
|  | @ -302,7 +305,7 @@ static void SetSendingY(Interface* self) { | |||
| 
 | ||||
| static void SetSendingU(Interface* self) { | ||||
|     // The helper should be passed by argument to the function
 | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00110102); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x11, 4, 2); | ||||
|     conversion.src_U.address = rp.Pop<u32>(); | ||||
|     conversion.src_U.image_size = rp.Pop<u32>(); | ||||
|     conversion.src_U.transfer_unit = rp.Pop<u32>(); | ||||
|  | @ -319,37 +322,41 @@ static void SetSendingU(Interface* self) { | |||
| } | ||||
| 
 | ||||
| static void SetSendingV(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     // The helper should be passed by argument to the function
 | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 4, 2); | ||||
| 
 | ||||
|     conversion.src_V.address = cmd_buff[1]; | ||||
|     conversion.src_V.image_size = cmd_buff[2]; | ||||
|     conversion.src_V.transfer_unit = cmd_buff[3]; | ||||
|     conversion.src_V.gap = cmd_buff[4]; | ||||
|     conversion.src_V.address = rp.Pop<u32>(); | ||||
|     conversion.src_V.image_size = rp.Pop<u32>(); | ||||
|     conversion.src_V.transfer_unit = rp.Pop<u32>(); | ||||
|     conversion.src_V.gap = rp.Pop<u32>(); | ||||
|     Kernel::Handle src_process_handle = rp.PopHandle(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||||
|                            "src_process_handle=0x%08X", | ||||
|               conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap, | ||||
|               cmd_buff[6]); | ||||
|               static_cast<u32>(src_process_handle)); | ||||
| } | ||||
| 
 | ||||
| static void SetSendingYUYV(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     // The helper should be passed by argument to the function
 | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x13, 4, 2); | ||||
| 
 | ||||
|     conversion.src_YUYV.address = cmd_buff[1]; | ||||
|     conversion.src_YUYV.image_size = cmd_buff[2]; | ||||
|     conversion.src_YUYV.transfer_unit = cmd_buff[3]; | ||||
|     conversion.src_YUYV.gap = cmd_buff[4]; | ||||
|     conversion.src_YUYV.address = rp.Pop<u32>(); | ||||
|     conversion.src_YUYV.image_size = rp.Pop<u32>(); | ||||
|     conversion.src_YUYV.transfer_unit = rp.Pop<u32>(); | ||||
|     conversion.src_YUYV.gap = rp.Pop<u32>(); | ||||
|     Kernel::Handle src_process_handle = rp.PopHandle(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||||
|                            "src_process_handle=0x%08X", | ||||
|               conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit, | ||||
|               conversion.src_YUYV.gap, cmd_buff[6]); | ||||
|               conversion.src_YUYV.gap, static_cast<u32>(src_process_handle)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -359,11 +366,11 @@ static void SetSendingYUYV(Interface* self) { | |||
|  *       2 : u8, 0 = Not Finished, 1 = Finished | ||||
|  */ | ||||
| static void IsFinishedSendingYuv(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = 1; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(1); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -375,11 +382,11 @@ static void IsFinishedSendingYuv(Interface* self) { | |||
|  *       2 : u8, 0 = Not Finished, 1 = Finished | ||||
|  */ | ||||
| static void IsFinishedSendingY(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x15, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = 1; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(1); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -391,11 +398,11 @@ static void IsFinishedSendingY(Interface* self) { | |||
|  *       2 : u8, 0 = Not Finished, 1 = Finished | ||||
|  */ | ||||
| static void IsFinishedSendingU(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = 1; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(1); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -407,30 +414,31 @@ static void IsFinishedSendingU(Interface* self) { | |||
|  *       2 : u8, 0 = Not Finished, 1 = Finished | ||||
|  */ | ||||
| static void IsFinishedSendingV(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = 1; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(1); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| static void SetReceiving(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 4, 2); | ||||
| 
 | ||||
|     conversion.dst.address = cmd_buff[1]; | ||||
|     conversion.dst.image_size = cmd_buff[2]; | ||||
|     conversion.dst.transfer_unit = cmd_buff[3]; | ||||
|     conversion.dst.gap = cmd_buff[4]; | ||||
|     conversion.dst.address = rp.Pop<u32>(); | ||||
|     conversion.dst.image_size = rp.Pop<u32>(); | ||||
|     conversion.dst.transfer_unit = rp.Pop<u32>(); | ||||
|     conversion.dst.gap = rp.Pop<u32>(); | ||||
|     Kernel::Handle dst_process_handle = rp.PopHandle(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||||
|                            "dst_process_handle=0x%08X", | ||||
|               conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap, | ||||
|               cmd_buff[6]); | ||||
|               static_cast<u32>(dst_process_handle)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -440,65 +448,67 @@ static void SetReceiving(Interface* self) { | |||
|  *       2 : u8, 0 = Not Finished, 1 = Finished | ||||
|  */ | ||||
| static void IsFinishedReceiving(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x19, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = 1; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(1); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| static void SetInputLineWidth(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 1, 0); | ||||
|     u32 input_line_width = rp.Pop<u32>(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0); | ||||
|     cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(conversion.SetInputLineWidth(input_line_width)); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); | ||||
|     LOG_DEBUG(Service_Y2R, "called input_line_width=%u", input_line_width); | ||||
| } | ||||
| 
 | ||||
| static void GetInputLineWidth(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = conversion.input_line_width; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(conversion.input_line_width); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width); | ||||
| } | ||||
| 
 | ||||
| static void SetInputLines(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1C, 1, 0); | ||||
|     u32 input_lines = rp.Pop<u32>(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0); | ||||
|     cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(conversion.SetInputLines(input_lines)); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); | ||||
|     LOG_DEBUG(Service_Y2R, "called input_lines=%u", input_lines); | ||||
| } | ||||
| 
 | ||||
| static void GetInputLines(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = static_cast<u32>(conversion.input_lines); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(static_cast<u32>(conversion.input_lines)); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines); | ||||
| } | ||||
| 
 | ||||
| static void SetCoefficient(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 4, 0); | ||||
| 
 | ||||
|     const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]); | ||||
|     std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); | ||||
|     rp.PopRaw<CoefficientSet>(conversion.coefficients); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", | ||||
|               coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4], | ||||
|               coefficients[5], coefficients[6], coefficients[7]); | ||||
|               conversion.coefficients[0], conversion.coefficients[1], conversion.coefficients[2], | ||||
|               conversion.coefficients[3], conversion.coefficients[4], conversion.coefficients[5], | ||||
|               conversion.coefficients[6], conversion.coefficients[7]); | ||||
| } | ||||
| 
 | ||||
| static void GetCoefficient(Interface* self) { | ||||
|  | @ -512,12 +522,11 @@ static void GetCoefficient(Interface* self) { | |||
| } | ||||
| 
 | ||||
| static void SetStandardCoefficient(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x20, 1, 0); | ||||
|     u32 index = rp.Pop<u32>(); | ||||
| 
 | ||||
|     u32 index = cmd_buff[1]; | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); | ||||
|     cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(conversion.SetStandardCoefficient(static_cast<StandardCoefficient>(index))); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index); | ||||
| } | ||||
|  | @ -544,22 +553,21 @@ static void GetStandardCoefficient(Interface* self) { | |||
| } | ||||
| 
 | ||||
| static void SetAlpha(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x22, 1, 0); | ||||
|     conversion.alpha = rp.Pop<u32>(); | ||||
| 
 | ||||
|     conversion.alpha = cmd_buff[1]; | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); | ||||
| } | ||||
| 
 | ||||
| static void GetAlpha(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x23, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = conversion.alpha; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push(conversion.alpha); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); | ||||
| } | ||||
|  | @ -584,7 +592,7 @@ static void GetDitheringWeightParams(Interface* self) { | |||
| } | ||||
| 
 | ||||
| static void StartConversion(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x26, 0, 0); | ||||
| 
 | ||||
|     // dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
 | ||||
|     u32 total_output_size = | ||||
|  | @ -596,17 +604,17 @@ static void StartConversion(Interface* self) { | |||
| 
 | ||||
|     completion_event->Signal(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called"); | ||||
| } | ||||
| 
 | ||||
| static void StopConversion(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x27, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called"); | ||||
| } | ||||
|  | @ -618,11 +626,11 @@ static void StopConversion(Interface* self) { | |||
|  *      2 : 1 if there's a conversion running, otherwise 0. | ||||
|  */ | ||||
| static void IsBusyConversion(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x28, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = 0; // StartConversion always finishes immediately
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(0); // StartConversion always finishes immediately
 | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called"); | ||||
| } | ||||
|  | @ -631,59 +639,60 @@ static void IsBusyConversion(Interface* self) { | |||
|  * Y2R_U::SetPackageParameter service function | ||||
|  */ | ||||
| static void SetPackageParameter(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x29, 7, 0); | ||||
|     auto params = rp.PopRaw<ConversionParameters>(); | ||||
| 
 | ||||
|     auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); | ||||
|     conversion.input_format = params.input_format; | ||||
|     conversion.output_format = params.output_format; | ||||
|     conversion.rotation = params.rotation; | ||||
|     conversion.block_alignment = params.block_alignment; | ||||
| 
 | ||||
|     conversion.input_format = params->input_format; | ||||
|     conversion.output_format = params->output_format; | ||||
|     conversion.rotation = params->rotation; | ||||
|     conversion.block_alignment = params->block_alignment; | ||||
| 
 | ||||
|     ResultCode result = conversion.SetInputLineWidth(params->input_line_width); | ||||
|     ResultCode result = conversion.SetInputLineWidth(params.input_line_width); | ||||
| 
 | ||||
|     if (result.IsError()) | ||||
|         goto cleanup; | ||||
| 
 | ||||
|     result = conversion.SetInputLines(params->input_lines); | ||||
|     result = conversion.SetInputLines(params.input_lines); | ||||
| 
 | ||||
|     if (result.IsError()) | ||||
|         goto cleanup; | ||||
| 
 | ||||
|     result = conversion.SetStandardCoefficient(params->standard_coefficient); | ||||
|     result = conversion.SetStandardCoefficient(params.standard_coefficient); | ||||
| 
 | ||||
|     if (result.IsError()) | ||||
|         goto cleanup; | ||||
| 
 | ||||
|     conversion.padding = params->padding; | ||||
|     conversion.alpha = params->alpha; | ||||
|     conversion.padding = params.padding; | ||||
|     conversion.alpha = params.alpha; | ||||
| 
 | ||||
| cleanup: | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); | ||||
|     cmd_buff[1] = result.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(result); | ||||
| 
 | ||||
|     LOG_DEBUG( | ||||
|         Service_Y2R, | ||||
|         "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " | ||||
|         "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", | ||||
|         static_cast<u8>(params->input_format), static_cast<u8>(params->output_format), | ||||
|         static_cast<u8>(params->rotation), static_cast<u8>(params->block_alignment), | ||||
|         params->input_line_width, params->input_lines, | ||||
|         static_cast<u8>(params->standard_coefficient), params->padding, params->alpha); | ||||
|         static_cast<u8>(params.input_format), static_cast<u8>(params.output_format), | ||||
|         static_cast<u8>(params.rotation), static_cast<u8>(params.block_alignment), | ||||
|         params.input_line_width, params.input_lines, static_cast<u8>(params.standard_coefficient), | ||||
|         params.padding, params.alpha); | ||||
| } | ||||
| 
 | ||||
| static void PingProcess(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2A, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     cmd_buff[2] = 0; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(0); | ||||
| 
 | ||||
|     LOG_WARNING(Service_Y2R, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| static void DriverInitialize(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2B, 0, 0); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
| 
 | ||||
|     conversion.input_format = InputFormat::YUV422_Indiv8; | ||||
|     conversion.output_format = OutputFormat::RGBA8; | ||||
|  | @ -702,17 +711,16 @@ static void DriverInitialize(Interface* self) { | |||
| 
 | ||||
|     completion_event->Clear(); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called"); | ||||
| } | ||||
| 
 | ||||
| static void DriverFinalize(Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2C, 0, 0); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_Y2R, "called"); | ||||
| } | ||||
|  | @ -787,4 +795,4 @@ Y2R_U::~Y2R_U() { | |||
| } | ||||
| 
 | ||||
| } // namespace Y2R
 | ||||
| } // namespace Service
 | ||||
| } // namespace Service
 | ||||
|  |  | |||
|  | @ -8,7 +8,9 @@ | |||
| #include "common/assert.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/scm_rev.h" | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
| #include "common/x64/cpu_detect.h" | ||||
| #endif | ||||
| #include "core/core.h" | ||||
| #include "core/settings.h" | ||||
| #include "core/telemetry_session.h" | ||||
|  | @ -20,6 +22,7 @@ | |||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
| static const char* CpuVendorToStr(Common::CPUVendor vendor) { | ||||
|     switch (vendor) { | ||||
|     case Common::CPUVendor::INTEL: | ||||
|  | @ -31,6 +34,7 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) { | |||
|     } | ||||
|     UNREACHABLE(); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static u64 GenerateTelemetryId() { | ||||
|     u64 telemetry_id{}; | ||||
|  | @ -121,7 +125,8 @@ TelemetrySession::TelemetrySession() { | |||
|     AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date); | ||||
|     AddField(Telemetry::FieldType::App, "BuildName", Common::g_build_name); | ||||
| 
 | ||||
|     // Log user system information
 | ||||
| // Log user system information
 | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
|     AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); | ||||
|     AddField(Telemetry::FieldType::UserSystem, "CPU_BrandString", | ||||
|              Common::GetCPUCaps().brand_string); | ||||
|  | @ -143,6 +148,9 @@ TelemetrySession::TelemetrySession() { | |||
|              Common::GetCPUCaps().sse4_1); | ||||
|     AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42", | ||||
|              Common::GetCPUCaps().sse4_2); | ||||
| #else | ||||
|     AddField(Telemetry::FieldType::UserSystem, "CPU_Model", "Other"); | ||||
| #endif | ||||
| #ifdef __APPLE__ | ||||
|     AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Apple"); | ||||
| #elif defined(_WIN32) | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ add_library(input_common STATIC | |||
|     motion_emu.cpp | ||||
|     motion_emu.h | ||||
| 
 | ||||
|     $<$<BOOL:SDL2_FOUND>:sdl/sdl.cpp sdl/sdl.h> | ||||
|     $<$<BOOL:${SDL2_FOUND}>:sdl/sdl.cpp sdl/sdl.h> | ||||
| ) | ||||
| 
 | ||||
| create_target_directory_groups(input_common) | ||||
|  |  | |||
|  | @ -17,11 +17,12 @@ namespace InputCommon { | |||
| // Implementation class of the motion emulation device
 | ||||
| class MotionEmuDevice { | ||||
| public: | ||||
|     MotionEmuDevice(int update_millisecond, float sensitivity) | ||||
|     MotionEmuDevice(int update_millisecond, float sensitivity, float tilt_clamp) | ||||
|         : update_millisecond(update_millisecond), | ||||
|           update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>( | ||||
|               std::chrono::milliseconds(update_millisecond))), | ||||
|           sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {} | ||||
|           sensitivity(sensitivity), tilt_clamp(tilt_clamp), | ||||
|           motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {} | ||||
| 
 | ||||
|     ~MotionEmuDevice() { | ||||
|         if (motion_emu_thread.joinable()) { | ||||
|  | @ -44,7 +45,7 @@ public: | |||
|             } else { | ||||
|                 tilt_direction = mouse_move.Cast<float>(); | ||||
|                 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * sensitivity, 0.0f, | ||||
|                                              MathUtil::PI * 0.5f); | ||||
|                                              MathUtil::PI * this->tilt_clamp / 180.0f); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -70,6 +71,7 @@ private: | |||
|     std::mutex tilt_mutex; | ||||
|     Math::Vec2<float> tilt_direction; | ||||
|     float tilt_angle = 0; | ||||
|     float tilt_clamp = 90; | ||||
| 
 | ||||
|     bool is_tilting = false; | ||||
| 
 | ||||
|  | @ -126,8 +128,8 @@ private: | |||
| // can forward all the inputs to the implementation only when it is valid.
 | ||||
| class MotionEmuDeviceWrapper : public Input::MotionDevice { | ||||
| public: | ||||
|     MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) { | ||||
|         device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity); | ||||
|     MotionEmuDeviceWrapper(int update_millisecond, float sensitivity, float tilt_clamp) { | ||||
|         device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity, tilt_clamp); | ||||
|     } | ||||
| 
 | ||||
|     std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const { | ||||
|  | @ -140,7 +142,9 @@ public: | |||
| std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) { | ||||
|     int update_period = params.Get("update_period", 100); | ||||
|     float sensitivity = params.Get("sensitivity", 0.01f); | ||||
|     auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity); | ||||
|     float tilt_clamp = params.Get("tilt_clamp", 90.0f); | ||||
|     auto device_wrapper = | ||||
|         std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity, tilt_clamp); | ||||
|     // Previously created device is disconnected here. Having two motion devices for 3DS is not
 | ||||
|     // expected.
 | ||||
|     current_device = device_wrapper->device; | ||||
|  |  | |||
|  | @ -221,6 +221,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
|                     MICROPROFILE_SCOPE(GPU_Drawing); | ||||
|                     immediate_attribute_id = 0; | ||||
| 
 | ||||
|                     Shader::OutputVertex::ValidateSemantics(regs.rasterizer); | ||||
| 
 | ||||
|                     auto* shader_engine = Shader::GetEngine(); | ||||
|                     shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset); | ||||
| 
 | ||||
|  | @ -289,6 +291,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
|         // Later, these can be compiled and cached.
 | ||||
|         const u32 base_address = regs.pipeline.vertex_attributes.GetPhysicalBaseAddress(); | ||||
|         VertexLoader loader(regs.pipeline); | ||||
|         Shader::OutputVertex::ValidateSemantics(regs.rasterizer); | ||||
| 
 | ||||
|         // Load vertices
 | ||||
|         bool is_indexed = (id == PICA_REG_INDEX(pipeline.trigger_draw_indexed)); | ||||
|  |  | |||
|  | @ -87,6 +87,8 @@ struct RasterizerRegs { | |||
|         BitField<8, 5, Semantic> map_y; | ||||
|         BitField<16, 5, Semantic> map_z; | ||||
|         BitField<24, 5, Semantic> map_w; | ||||
| 
 | ||||
|         u32 raw; | ||||
|     } vs_output_attributes[7]; | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0xe); | ||||
|  |  | |||
|  | @ -65,6 +65,7 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { | |||
|     PicaShaderConfig res; | ||||
| 
 | ||||
|     auto& state = res.state; | ||||
|     // Memset structure to zero padding bits, so that they will be deterministic when hashing
 | ||||
|     std::memset(&state, 0, sizeof(PicaShaderConfig::State)); | ||||
| 
 | ||||
|     state.scissor_test_mode = regs.rasterizer.scissor_test.mode; | ||||
|  |  | |||
|  | @ -131,10 +131,6 @@ union PicaShaderConfig { | |||
| 
 | ||||
|     } state; | ||||
| }; | ||||
| #if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) | ||||
| static_assert(std::is_trivially_copyable<PicaShaderConfig::State>::value, | ||||
|               "PicaShaderConfig::State must be trivially copyable"); | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * Generates the GLSL vertex shader program source code for the current Pica state | ||||
|  | @ -156,7 +152,7 @@ namespace std { | |||
| template <> | ||||
| struct hash<GLShader::PicaShaderConfig> { | ||||
|     size_t operator()(const GLShader::PicaShaderConfig& k) const { | ||||
|         return Common::ComputeHash64(&k.state, sizeof(GLShader::PicaShaderConfig::State)); | ||||
|         return Common::ComputeStructHash64(k.state); | ||||
|     } | ||||
| }; | ||||
| } // namespace std
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <cinttypes> | ||||
| #include <cmath> | ||||
| #include <cstring> | ||||
| #include "common/bit_set.h" | ||||
|  | @ -21,32 +22,41 @@ namespace Pica { | |||
| 
 | ||||
| namespace Shader { | ||||
| 
 | ||||
| void OutputVertex::ValidateSemantics(const RasterizerRegs& regs) { | ||||
|     unsigned int num_attributes = regs.vs_output_total; | ||||
|     ASSERT(num_attributes <= 7); | ||||
|     for (size_t attrib = 0; attrib < num_attributes; ++attrib) { | ||||
|         u32 output_register_map = regs.vs_output_attributes[attrib].raw; | ||||
|         for (size_t comp = 0; comp < 4; ++comp) { | ||||
|             u32 semantic = (output_register_map >> (8 * comp)) & 0x1F; | ||||
|             ASSERT_MSG(semantic < 24 || semantic == RasterizerRegs::VSOutputAttributes::INVALID, | ||||
|                        "Invalid/unknown semantic id: %" PRIu32, semantic); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs, | ||||
|                                                const AttributeBuffer& input) { | ||||
|     // Setup output data
 | ||||
|     union { | ||||
|         OutputVertex ret{}; | ||||
|         std::array<float24, 24> vertex_slots; | ||||
|         // Allow us to overflow OutputVertex to avoid branches, since
 | ||||
|         // RasterizerRegs::VSOutputAttributes::INVALID would write to slot 31, which
 | ||||
|         // would be out of bounds otherwise.
 | ||||
|         std::array<float24, 32> vertex_slots_overflow; | ||||
|     }; | ||||
|     static_assert(sizeof(vertex_slots) == sizeof(ret), "Struct and array have different sizes."); | ||||
| 
 | ||||
|     unsigned int num_attributes = regs.vs_output_total; | ||||
|     ASSERT(num_attributes <= 7); | ||||
|     for (unsigned int i = 0; i < num_attributes; ++i) { | ||||
|         const auto& output_register_map = regs.vs_output_attributes[i]; | ||||
|     // Assert that OutputVertex has enough space for 24 semantic registers
 | ||||
|     static_assert(sizeof(std::array<float24, 24>) == sizeof(ret), | ||||
|                   "Struct and array have different sizes."); | ||||
| 
 | ||||
|         RasterizerRegs::VSOutputAttributes::Semantic semantics[4] = { | ||||
|             output_register_map.map_x, output_register_map.map_y, output_register_map.map_z, | ||||
|             output_register_map.map_w}; | ||||
| 
 | ||||
|         for (unsigned comp = 0; comp < 4; ++comp) { | ||||
|             RasterizerRegs::VSOutputAttributes::Semantic semantic = semantics[comp]; | ||||
|             if (semantic < vertex_slots.size()) { | ||||
|                 vertex_slots[semantic] = input.attr[i][comp]; | ||||
|             } else if (semantic != RasterizerRegs::VSOutputAttributes::INVALID) { | ||||
|                 LOG_ERROR(HW_GPU, "Invalid/unknown semantic id: %u", (unsigned int)semantic); | ||||
|             } | ||||
|         } | ||||
|     unsigned int num_attributes = regs.vs_output_total & 7; | ||||
|     for (size_t attrib = 0; attrib < num_attributes; ++attrib) { | ||||
|         const auto output_register_map = regs.vs_output_attributes[attrib]; | ||||
|         vertex_slots_overflow[output_register_map.map_x] = input.attr[attrib][0]; | ||||
|         vertex_slots_overflow[output_register_map.map_y] = input.attr[attrib][1]; | ||||
|         vertex_slots_overflow[output_register_map.map_z] = input.attr[attrib][2]; | ||||
|         vertex_slots_overflow[output_register_map.map_w] = input.attr[attrib][3]; | ||||
|     } | ||||
| 
 | ||||
|     // The hardware takes the absolute and saturates vertex colors like this, *before* doing
 | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ struct OutputVertex { | |||
|     INSERT_PADDING_WORDS(1); | ||||
|     Math::Vec2<float24> tc2; | ||||
| 
 | ||||
|     static void ValidateSemantics(const RasterizerRegs& regs); | ||||
|     static OutputVertex FromAttributeBuffer(const RasterizerRegs& regs, | ||||
|                                             const AttributeBuffer& output); | ||||
| }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue