From 455a0198d9961b148053fb5cc73be069f091c90b Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Fri, 3 Mar 2023 05:04:31 -0800 Subject: [PATCH 1/7] ci: Bump macOS target to 11 (Big Sur) (#6325) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96f999f8d..a0e58c8a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,7 +96,7 @@ jobs: - name: Build run: ./.ci/macos/build.sh env: - MACOSX_DEPLOYMENT_TARGET: "10.13" + MACOSX_DEPLOYMENT_TARGET: "11" ENABLE_COMPATIBILITY_REPORTING: "ON" TARGET_ARCH: ${{ matrix.arch }} - name: Pack From 07e02a1acf8f48c3379dfa17aaea46cef2c9136f Mon Sep 17 00:00:00 2001 From: Tobias Date: Wed, 8 Mar 2023 00:51:46 +0100 Subject: [PATCH 2/7] Port multiplayer related PRs from yuzu (yuzu-emu/yuzu#9661 and yuzu-emu/yuzu#9713) (#6319) Co-authored-by: SoRadGaming Co-authored-by: Luke Sawczak --- src/citra_qt/multiplayer/direct_connect.cpp | 21 ++++++----------- src/citra_qt/multiplayer/direct_connect.ui | 23 +++++++++---------- src/citra_qt/multiplayer/lobby.cpp | 20 +++++++++++++++-- src/citra_qt/multiplayer/lobby.h | 2 ++ src/citra_qt/multiplayer/lobby.ui | 7 ++++++ src/citra_qt/multiplayer/validation.h | 25 +++++++++++++++++---- 6 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src/citra_qt/multiplayer/direct_connect.cpp b/src/citra_qt/multiplayer/direct_connect.cpp index feaea69c9..6cef55845 100644 --- a/src/citra_qt/multiplayer/direct_connect.cpp +++ b/src/citra_qt/multiplayer/direct_connect.cpp @@ -70,20 +70,13 @@ void DirectConnectWindow::Connect() { } } } - switch (static_cast(ui->connection_type->currentIndex())) { - case ConnectionType::TraversalServer: - break; - case ConnectionType::IP: - if (!ui->ip->hasAcceptableInput()) { - NetworkMessage::ErrorManager::ShowError( - NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID); - return; - } - if (!ui->port->hasAcceptableInput()) { - NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID); - return; - } - break; + if (!ui->ip->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID); + return; + } + if (!ui->port->hasAcceptableInput()) { + NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PORT_NOT_VALID); + return; } // Store settings diff --git a/src/citra_qt/multiplayer/direct_connect.ui b/src/citra_qt/multiplayer/direct_connect.ui index 681b6bf69..4a328189a 100644 --- a/src/citra_qt/multiplayer/direct_connect.ui +++ b/src/citra_qt/multiplayer/direct_connect.ui @@ -26,20 +26,11 @@ 0 - - - - - IP Address - - - - - 5 + 0 0 @@ -53,17 +44,17 @@ - IP + Server Address - <html><head/><body><p>IPv4 address of the host</p></body></html> + <html><head/><body><p>Server address of the host</p></body></html> - 16 + 253 @@ -85,6 +76,12 @@ 24872 + + + 65 + 50 + + diff --git a/src/citra_qt/multiplayer/lobby.cpp b/src/citra_qt/multiplayer/lobby.cpp index d74bbf3ab..72f11b9db 100644 --- a/src/citra_qt/multiplayer/lobby.cpp +++ b/src/citra_qt/multiplayer/lobby.cpp @@ -66,6 +66,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, // UI Buttons connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); + connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty); connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); @@ -282,12 +283,22 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s return true; } + // filter by empty rooms + if (filter_empty) { + QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent); + const int player_count = + sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size(); + if (player_count == 0) { + return false; + } + } + // filter by filled rooms if (filter_full) { QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent); - int player_count = + const int player_count = sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size(); - int max_players = + const int max_players = sourceModel()->data(member_list, LobbyItemMemberList::MaxPlayerRole).toInt(); if (player_count >= max_players) { return false; @@ -352,6 +363,11 @@ void LobbyFilterProxyModel::SetFilterOwned(bool filter) { invalidate(); } +void LobbyFilterProxyModel::SetFilterEmpty(bool filter) { + filter_empty = filter; + invalidate(); +} + void LobbyFilterProxyModel::SetFilterFull(bool filter) { filter_full = filter; invalidate(); diff --git a/src/citra_qt/multiplayer/lobby.h b/src/citra_qt/multiplayer/lobby.h index 985b82ba7..129b25497 100644 --- a/src/citra_qt/multiplayer/lobby.h +++ b/src/citra_qt/multiplayer/lobby.h @@ -116,12 +116,14 @@ public: public slots: void SetFilterOwned(bool); + void SetFilterEmpty(bool); void SetFilterFull(bool); void SetFilterSearch(const QString&); private: QStandardItemModel* game_list; bool filter_owned = false; + bool filter_empty = false; bool filter_full = false; QString filter_search; }; diff --git a/src/citra_qt/multiplayer/lobby.ui b/src/citra_qt/multiplayer/lobby.ui index 4c9901c9a..0ef0ef762 100644 --- a/src/citra_qt/multiplayer/lobby.ui +++ b/src/citra_qt/multiplayer/lobby.ui @@ -77,6 +77,13 @@ + + + + Hide Empty Rooms + + + diff --git a/src/citra_qt/multiplayer/validation.h b/src/citra_qt/multiplayer/validation.h index 1c215a190..ecf3069d9 100644 --- a/src/citra_qt/multiplayer/validation.h +++ b/src/citra_qt/multiplayer/validation.h @@ -37,11 +37,28 @@ private: QRegExp nickname_regex = QRegExp(QStringLiteral("^[a-zA-Z0-9._- ]{4,20}$")); QRegExpValidator nickname; - /// ipv4 address only - // TODO remove this when we support hostnames in direct connect + /// ipv4 / ipv6 / hostnames QRegExp ip_regex = QRegExp(QStringLiteral( - "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|" - "2[0-4][0-9]|25[0-5])")); + // IPv4 regex + "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|" + // IPv6 regex + "^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|" + "(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-" + "5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|" + "(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)" + "(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|" + "(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]" + "\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|" + "(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[" + "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|" + "(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[" + "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|" + "(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[" + "0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|" + "(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?" + "\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$|" + // Hostname regex + "^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\\.)+[a-zA-Z]{2,}$")); QRegExpValidator ip; /// port must be between 0 and 65535 From 976995ba08e6441689c2ff9aa3db286a07c083b6 Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Wed, 8 Mar 2023 15:05:00 -0800 Subject: [PATCH 3/7] cfg: Set system setup complete flag when formatting save. (#6331) --- src/core/hle/service/cfg/cfg.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 68ba0b64f..86ba6b45a 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -639,7 +639,8 @@ ResultCode Module::FormatConfig() { // 0x00110000 - The low u16 indicates whether the system setup is required, such as when the // system is booted for the first time or after doing a System Format: 0 = setup required, // non-zero = no setup required - res = CreateConfigInfoBlk(SystemSetupRequiredBlockID, 0x4, 0xC, zero_buffer); + u32 system_setup_flag = 1; + res = CreateConfigInfoBlk(SystemSetupRequiredBlockID, 0x4, 0xC, &system_setup_flag); if (!res.IsSuccess()) return res; From 1cca713e3ba18c9bc8d6f354baf17081f9059b55 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 9 Mar 2023 11:24:32 -0500 Subject: [PATCH 4/7] citra_android: New settings fragment animations (#6332) --- .../settings/ui/SettingsActivity.java | 8 +++--- .../anim_pop_settings_fragment_out.xml | 16 +++++++++++ .../anim-ldrtl/anim_settings_fragment_in.xml | 16 +++++++++++ .../anim/anim_pop_settings_fragment_out.xml | 16 +++++++++++ .../res/anim/anim_settings_fragment_in.xml | 16 +++++++++++ .../res/anim/anim_settings_fragment_out.xml | 10 +++++++ .../src/main/res/animator/settings_enter.xml | 28 ------------------- .../src/main/res/animator/settings_exit.xml | 28 ------------------- .../main/res/animator/settings_pop_enter.xml | 28 ------------------- .../main/res/animator/setttings_pop_exit.xml | 27 ------------------ 10 files changed, 78 insertions(+), 115 deletions(-) create mode 100644 src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml create mode 100644 src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml create mode 100644 src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml create mode 100644 src/android/app/src/main/res/anim/anim_settings_fragment_in.xml create mode 100644 src/android/app/src/main/res/anim/anim_settings_fragment_out.xml delete mode 100644 src/android/app/src/main/res/animator/settings_enter.xml delete mode 100644 src/android/app/src/main/res/animator/settings_exit.xml delete mode 100644 src/android/app/src/main/res/animator/settings_pop_enter.xml delete mode 100644 src/android/app/src/main/res/animator/setttings_pop_exit.xml diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java index 23c3c4c9e..91adf7dae 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java @@ -107,10 +107,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting if (addToStack) { if (areSystemAnimationsEnabled()) { transaction.setCustomAnimations( - R.animator.settings_enter, - R.animator.settings_exit, - R.animator.settings_pop_enter, - R.animator.setttings_pop_exit); + R.anim.anim_settings_fragment_in, + R.anim.anim_settings_fragment_out, + 0, + R.anim.anim_pop_settings_fragment_out); } transaction.addToBackStack(null); diff --git a/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml b/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml new file mode 100644 index 000000000..9f49c133a --- /dev/null +++ b/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml new file mode 100644 index 000000000..82fd719db --- /dev/null +++ b/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml new file mode 100644 index 000000000..5892128f1 --- /dev/null +++ b/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml new file mode 100644 index 000000000..98e0cf8bd --- /dev/null +++ b/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml new file mode 100644 index 000000000..77a40a4d1 --- /dev/null +++ b/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/android/app/src/main/res/animator/settings_enter.xml b/src/android/app/src/main/res/animator/settings_enter.xml deleted file mode 100644 index 3c216a054..000000000 --- a/src/android/app/src/main/res/animator/settings_enter.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/android/app/src/main/res/animator/settings_exit.xml b/src/android/app/src/main/res/animator/settings_exit.xml deleted file mode 100644 index a233b6757..000000000 --- a/src/android/app/src/main/res/animator/settings_exit.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/android/app/src/main/res/animator/settings_pop_enter.xml b/src/android/app/src/main/res/animator/settings_pop_enter.xml deleted file mode 100644 index 080bc27c4..000000000 --- a/src/android/app/src/main/res/animator/settings_pop_enter.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/android/app/src/main/res/animator/setttings_pop_exit.xml b/src/android/app/src/main/res/animator/setttings_pop_exit.xml deleted file mode 100644 index 4fccbcca2..000000000 --- a/src/android/app/src/main/res/animator/setttings_pop_exit.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - \ No newline at end of file From d2caf2d386575555160c088ef6c0467de680f5e8 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Thu, 9 Mar 2023 17:22:11 -0500 Subject: [PATCH 5/7] citra_android: Start material 3 migration (#6335) --- src/android/app/src/main/AndroidManifest.xml | 8 +- .../org/citra/citra_emu/NativeLibrary.java | 12 +- .../activities/EmulationActivity.java | 112 +++++++----------- .../citra/citra_emu/adapters/GameAdapter.java | 28 +---- .../citra/citra_emu/applets/MiiSelector.java | 6 +- .../citra_emu/applets/SoftwareKeyboard.java | 6 +- .../DiskShaderCacheProgress.java | 20 ++-- .../cheats/ui/CheatDetailsFragment.java | 14 ++- .../features/cheats/ui/CheatsActivity.java | 7 ++ .../settings/ui/SettingsActivity.java | 7 ++ .../ui/SettingsActivityPresenter.java | 4 +- .../features/settings/ui/SettingsAdapter.java | 105 +++++++--------- .../citra/citra_emu/ui/main/MainActivity.java | 2 +- .../ui/platform/PlatformGamesFragment.java | 11 +- .../citra/citra_emu/utils/StartupHandler.java | 4 +- .../org/citra/citra_emu/utils/ThemeUtil.java | 14 ++- .../main/res/drawable-hdpi/ic_cia_install.png | Bin 514 -> 0 bytes .../src/main/res/drawable-hdpi/ic_folder.png | Bin 275 -> 0 bytes .../src/main/res/drawable-hdpi/ic_premium.png | Bin 961 -> 0 bytes .../res/drawable-hdpi/ic_settings_core.png | Bin 793 -> 0 bytes .../main/res/drawable-mdpi/ic_cia_install.png | Bin 364 -> 0 bytes .../src/main/res/drawable-mdpi/ic_folder.png | Bin 214 -> 0 bytes .../src/main/res/drawable-mdpi/ic_premium.png | Bin 605 -> 0 bytes .../drawable-night-hdpi/ic_cia_install.png | Bin 556 -> 0 bytes .../res/drawable-night-hdpi/ic_folder.png | Bin 289 -> 0 bytes .../res/drawable-night-hdpi/ic_premium.png | Bin 955 -> 0 bytes .../drawable-night-hdpi/ic_settings_core.png | Bin 1152 -> 0 bytes .../drawable-night-mdpi/ic_cia_install.png | Bin 405 -> 0 bytes .../res/drawable-night-mdpi/ic_folder.png | Bin 227 -> 0 bytes .../res/drawable-night-mdpi/ic_premium.png | Bin 595 -> 0 bytes .../drawable-night-xhdpi/ic_cia_install.png | Bin 729 -> 0 bytes .../res/drawable-night-xhdpi/ic_folder.png | Bin 347 -> 0 bytes .../res/drawable-night-xhdpi/ic_premium.png | Bin 1281 -> 0 bytes .../drawable-night-xhdpi/ic_settings_core.png | Bin 1431 -> 0 bytes .../drawable-night-xxhdpi/ic_cia_install.png | Bin 1168 -> 0 bytes .../res/drawable-night-xxhdpi/ic_folder.png | Bin 555 -> 0 bytes .../res/drawable-night-xxhdpi/ic_premium.png | Bin 2049 -> 0 bytes .../ic_settings_core.png | Bin 2125 -> 0 bytes .../drawable-night-xxxhdpi/ic_cia_install.png | Bin 1433 -> 0 bytes .../res/drawable-night-xxxhdpi/ic_folder.png | Bin 657 -> 0 bytes .../res/drawable-night-xxxhdpi/ic_premium.png | Bin 2614 -> 0 bytes .../ic_settings_core.png | Bin 2587 -> 0 bytes .../res/drawable-xhdpi/ic_cia_install.png | Bin 656 -> 0 bytes .../src/main/res/drawable-xhdpi/ic_folder.png | Bin 325 -> 0 bytes .../main/res/drawable-xhdpi/ic_premium.png | Bin 1334 -> 0 bytes .../res/drawable-xhdpi/ic_settings_core.png | Bin 1029 -> 0 bytes .../res/drawable-xxhdpi/ic_cia_install.png | Bin 967 -> 0 bytes .../main/res/drawable-xxhdpi/ic_folder.png | Bin 487 -> 0 bytes .../main/res/drawable-xxhdpi/ic_premium.png | Bin 2096 -> 0 bytes .../res/drawable-xxhdpi/ic_settings_core.png | Bin 1647 -> 0 bytes .../res/drawable-xxxhdpi/ic_cia_install.png | Bin 1244 -> 0 bytes .../main/res/drawable-xxxhdpi/ic_folder.png | Bin 591 -> 0 bytes .../main/res/drawable-xxxhdpi/ic_premium.png | Bin 2654 -> 0 bytes .../res/drawable-xxxhdpi/ic_settings_core.png | Bin 2093 -> 0 bytes .../main/res/drawable/gamelist_divider.xml | 2 +- .../app/src/main/res/drawable/ic_add.xml | 2 +- .../app/src/main/res/drawable/ic_back.xml | 10 ++ .../app/src/main/res/drawable/ic_folder.xml | 9 ++ .../app/src/main/res/drawable/ic_heart.xml | 9 ++ .../app/src/main/res/drawable/ic_install.xml | 9 ++ .../app/src/main/res/drawable/ic_settings.xml | 9 ++ .../main/res/drawable/popup_background.xml | 4 + .../main/res/layout-ldrtl/list_item_cheat.xml | 1 - .../src/main/res/layout/activity_cheats.xml | 66 ++++++++--- .../app/src/main/res/layout/activity_main.xml | 31 ++--- .../src/main/res/layout/activity_settings.xml | 30 ++++- .../app/src/main/res/layout/card_game.xml | 11 +- .../{dialog_seekbar.xml => dialog_slider.xml} | 6 +- .../main/res/layout/fragment_emulation.xml | 1 - .../app/src/main/res/layout/fragment_grid.xml | 8 +- .../src/main/res/layout/fragment_settings.xml | 11 +- .../src/main/res/layout/list_item_cheat.xml | 1 - .../src/main/res/layout/list_item_setting.xml | 4 +- .../res/layout/list_item_setting_checkbox.xml | 2 - .../res/layout/list_item_settings_header.xml | 4 +- .../main/res/layout/premium_item_setting.xml | 5 +- .../app/src/main/res/menu/menu_game_grid.xml | 6 +- .../main/res/values-night/citra_colors.xml | 33 ++++++ .../app/src/main/res/values-night/colors.xml | 17 --- .../app/src/main/res/values/citra_colors.xml | 35 ++++++ .../app/src/main/res/values/colors.xml | 17 --- .../app/src/main/res/values/styles.xml | 88 +++++--------- .../app/src/main/res/values/themes.xml | 55 +++++++++ 83 files changed, 494 insertions(+), 352 deletions(-) delete mode 100644 src/android/app/src/main/res/drawable-hdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-hdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-hdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-hdpi/ic_settings_core.png delete mode 100644 src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-mdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-mdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-night-hdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-night-hdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-night-hdpi/ic_settings_core.png delete mode 100644 src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-night-mdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-night-mdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-night-xhdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-night-xhdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-night-xhdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-night-xhdpi/ic_settings_core.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxhdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxhdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxhdpi/ic_settings_core.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxxhdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxxhdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-night-xxxhdpi/ic_settings_core.png delete mode 100644 src/android/app/src/main/res/drawable-xhdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-xhdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-xhdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-xhdpi/ic_settings_core.png delete mode 100644 src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-xxhdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-xxhdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-xxhdpi/ic_settings_core.png delete mode 100644 src/android/app/src/main/res/drawable-xxxhdpi/ic_cia_install.png delete mode 100644 src/android/app/src/main/res/drawable-xxxhdpi/ic_folder.png delete mode 100644 src/android/app/src/main/res/drawable-xxxhdpi/ic_premium.png delete mode 100644 src/android/app/src/main/res/drawable-xxxhdpi/ic_settings_core.png create mode 100644 src/android/app/src/main/res/drawable/ic_back.xml create mode 100644 src/android/app/src/main/res/drawable/ic_folder.xml create mode 100644 src/android/app/src/main/res/drawable/ic_heart.xml create mode 100644 src/android/app/src/main/res/drawable/ic_install.xml create mode 100644 src/android/app/src/main/res/drawable/ic_settings.xml create mode 100644 src/android/app/src/main/res/drawable/popup_background.xml rename src/android/app/src/main/res/layout/{dialog_seekbar.xml => dialog_slider.xml} (93%) create mode 100644 src/android/app/src/main/res/values-night/citra_colors.xml delete mode 100644 src/android/app/src/main/res/values-night/colors.xml create mode 100644 src/android/app/src/main/res/values/citra_colors.xml delete mode 100644 src/android/app/src/main/res/values/colors.xml create mode 100644 src/android/app/src/main/res/values/themes.xml diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index cfc9a7474..dcae891e4 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -34,7 +34,7 @@ @@ -48,13 +48,13 @@ @@ -72,7 +72,7 @@ diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java index 90d774f6c..5899d62a6 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.java @@ -38,6 +38,8 @@ import java.util.Objects; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.RECORD_AUDIO; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + /** * Class which contains methods that interact * with the native side of the Citra code. @@ -245,7 +247,7 @@ public final class NativeLibrary { final String title = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("title")); final String message = Objects.requireNonNull(Objects.requireNonNull(getArguments()).getString("message")); - return new AlertDialog.Builder(emulationActivity) + return new MaterialAlertDialogBuilder(emulationActivity) .setTitle(title) .setMessage(message) .setPositiveButton(R.string.continue_button, (dialog, which) -> { @@ -345,7 +347,7 @@ public final class NativeLibrary { } else { // Create object used for waiting. final Object lock = new Object(); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(caption) .setMessage(text); @@ -427,7 +429,7 @@ public final class NativeLibrary { return alertPromptResult; } - public static AlertDialog.Builder displayAlertPromptImpl(String caption, String text, int buttonConfig) { + public static MaterialAlertDialogBuilder displayAlertPromptImpl(String caption, String text, int buttonConfig) { final EmulationActivity emulationActivity = sEmulationActivity.get(); alertPromptResult = ""; alertPromptButton = 0; @@ -444,7 +446,7 @@ public final class NativeLibrary { FrameLayout container = new FrameLayout(emulationActivity); container.addView(alertPromptEditText); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(caption) .setView(container) .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> @@ -506,7 +508,7 @@ public final class NativeLibrary { captionId = R.string.loader_error_encrypted; } - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(captionId) .setMessage(Html.fromHtml("Please follow the guides to redump your game cartidges or installed titles.", Html.FROM_HTML_MODE_LEGACY)) .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> emulationActivity.finish()) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java index b5e547266..28659589e 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.java @@ -18,13 +18,11 @@ import android.view.MotionEvent; import android.view.SubMenu; import android.view.View; import android.widget.CheckBox; -import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.IntDef; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.PopupMenu; import androidx.core.app.NotificationManagerCompat; @@ -45,6 +43,7 @@ import org.citra.citra_emu.utils.EmulationMenuSettings; import org.citra.citra_emu.utils.FileBrowserHelper; import org.citra.citra_emu.utils.FileUtil; import org.citra.citra_emu.utils.ForegroundService; +import org.citra.citra_emu.utils.ThemeUtil; import java.io.File; import java.io.IOException; @@ -56,6 +55,9 @@ import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.RECORD_AUDIO; import static java.lang.annotation.RetentionPolicy.SOURCE; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.google.android.material.slider.Slider; + public final class EmulationActivity extends AppCompatActivity { public static final String EXTRA_SELECTED_GAME = "SelectedGame"; public static final String EXTRA_SELECTED_TITLE = "SelectedTitle"; @@ -151,6 +153,8 @@ public final class EmulationActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { + ThemeUtil.applyTheme(this); + super.onCreate(savedInstanceState); if (savedInstanceState == null) { @@ -179,8 +183,6 @@ public final class EmulationActivity extends AppCompatActivity { // Set these options now so that the SurfaceView the game renders into is the right size. enableFullscreenImmersive(); - setTheme(R.style.CitraEmulationBase); - setContentView(R.layout.activity_emulation); // Find or create the EmulationFragment @@ -243,7 +245,7 @@ public final class EmulationActivity extends AppCompatActivity { case NativeLibrary.REQUEST_CODE_NATIVE_CAMERA: if (grantResults[0] != PackageManager.PERMISSION_GRANTED && shouldShowRequestPermissionRationale(CAMERA)) { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle(R.string.camera) .setMessage(R.string.camera_permission_needed) .setPositiveButton(android.R.string.ok, null) @@ -254,7 +256,7 @@ public final class EmulationActivity extends AppCompatActivity { case NativeLibrary.REQUEST_CODE_NATIVE_MIC: if (grantResults[0] != PackageManager.PERMISSION_GRANTED && shouldShowRequestPermissionRationale(RECORD_AUDIO)) { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle(R.string.microphone) .setMessage(R.string.microphone_permission_needed) .setPositiveButton(android.R.string.ok, null) @@ -324,7 +326,7 @@ public final class EmulationActivity extends AppCompatActivity { View view = inflater.inflate(R.layout.dialog_checkbox, null); CheckBox checkBox = view.findViewById(R.id.checkBox); - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle(R.string.savestate_warning_title) .setMessage(R.string.savestate_warning_message) .setView(view) @@ -464,8 +466,8 @@ public final class EmulationActivity extends AppCompatActivity { case MENU_ACTION_LOAD_AMIIBO: FileBrowserHelper.openFilePicker(this, REQUEST_SELECT_AMIIBO, - R.string.select_amiibo, - Collections.singletonList("bin"), false); + R.string.select_amiibo, + Collections.singletonList("bin"), false); break; case MENU_ACTION_REMOVE_AMIIBO: @@ -490,7 +492,7 @@ public final class EmulationActivity extends AppCompatActivity { case MENU_ACTION_CLOSE_GAME: NativeLibrary.PauseEmulation(); - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle(R.string.emulation_close_game) .setMessage(R.string.emulation_close_game_message) .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> @@ -498,11 +500,8 @@ public final class EmulationActivity extends AppCompatActivity { mEmulationFragment.stopEmulation(); finish(); }) - .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> - NativeLibrary.UnPauseEmulation()) - .setOnCancelListener(dialogInterface -> - NativeLibrary.UnPauseEmulation()) - .create() + .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> NativeLibrary.UnPauseEmulation()) + .setOnCancelListener(dialogInterface -> NativeLibrary.UnPauseEmulation()) .show(); break; } @@ -589,11 +588,10 @@ public final class EmulationActivity extends AppCompatActivity { } if (!success) { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle(R.string.amiibo_load_error) .setMessage(R.string.amiibo_load_error_message) .setPositiveButton(android.R.string.ok, null) - .create() .show(); } } @@ -605,8 +603,6 @@ public final class EmulationActivity extends AppCompatActivity { private void toggleControls() { final SharedPreferences.Editor editor = mPreferences.edit(); boolean[] enabledButtons = new boolean[14]; - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.emulation_toggle_controls); for (int i = 0; i < enabledButtons.length; i++) { // Buttons that are disabled by default @@ -621,63 +617,47 @@ public final class EmulationActivity extends AppCompatActivity { enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, defaultValue); } - builder.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons, - (dialog, indexSelected, isChecked) -> editor - .putBoolean("buttonToggle" + indexSelected, isChecked)); - builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> - { - editor.apply(); - mEmulationFragment.refreshInputOverlay(); - }); - - AlertDialog alertDialog = builder.create(); - alertDialog.show(); + new MaterialAlertDialogBuilder(this) + .setTitle(R.string.emulation_toggle_controls) + .setMultiChoiceItems(R.array.n3dsButtons, enabledButtons, + (dialog, indexSelected, isChecked) -> editor + .putBoolean("buttonToggle" + indexSelected, isChecked)) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> + { + editor.apply(); + mEmulationFragment.refreshInputOverlay(); + }) + .show(); } private void adjustScale() { LayoutInflater inflater = LayoutInflater.from(this); - View view = inflater.inflate(R.layout.dialog_seekbar, null); + View view = inflater.inflate(R.layout.dialog_slider, null); - final SeekBar seekbar = view.findViewById(R.id.seekbar); - final TextView value = view.findViewById(R.id.text_value); + final Slider slider = view.findViewById(R.id.slider); + final TextView textValue = view.findViewById(R.id.text_value); final TextView units = view.findViewById(R.id.text_units); - seekbar.setMax(150); - seekbar.setProgress(mPreferences.getInt("controlScale", 50)); - seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - public void onStartTrackingTouch(SeekBar seekBar) { - } - - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - value.setText(String.valueOf(progress + 50)); - } - - public void onStopTrackingTouch(SeekBar seekBar) { - setControlScale(seekbar.getProgress()); - } + slider.setValueTo(150); + slider.setValue(mPreferences.getInt("controlScale", 50)); + slider.addOnChangeListener((slider1, progress, fromUser) -> { + textValue.setText(String.valueOf((int) progress + 50)); + setControlScale((int) slider1.getValue()); }); - value.setText(String.valueOf(seekbar.getProgress() + 50)); + textValue.setText(String.valueOf((int) slider.getValue() + 50)); units.setText("%"); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.emulation_control_scale); - builder.setView(view); - final int previousProgress = seekbar.getProgress(); - builder.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> { - setControlScale(previousProgress); - }); - builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> - { - setControlScale(seekbar.getProgress()); - }); - builder.setNeutralButton(R.string.slider_default, (dialogInterface, i) -> { - setControlScale(50); - }); + final int previousProgress = (int) slider.getValue(); - AlertDialog alertDialog = builder.create(); - alertDialog.show(); + new MaterialAlertDialogBuilder(this) + .setTitle(R.string.emulation_control_scale) + .setView(view) + .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> setControlScale(previousProgress)) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> setControlScale((int) slider.getValue())) + .setNeutralButton(R.string.slider_default, (dialogInterface, i) -> setControlScale(50)) + .show(); } private void setControlScale(int scale) { @@ -688,12 +668,10 @@ public final class EmulationActivity extends AppCompatActivity { } private void resetOverlay() { - new AlertDialog.Builder(this) + new MaterialAlertDialogBuilder(this) .setTitle(getString(R.string.emulation_touch_overlay_reset)) .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mEmulationFragment.resetInputOverlay()) - .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> { - }) - .create() + .setNegativeButton(android.R.string.cancel, null) .show(); } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java index bc791638a..ca66c3b9b 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/adapters/GameAdapter.java @@ -2,8 +2,6 @@ package org.citra.citra_emu.adapters; import android.database.Cursor; import android.database.DataSetObserver; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.os.Build; import android.os.SystemClock; import android.view.LayoutInflater; @@ -12,14 +10,14 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.color.MaterialColors; + import org.citra.citra_emu.R; import org.citra.citra_emu.activities.EmulationActivity; import org.citra.citra_emu.model.GameDatabase; -import org.citra.citra_emu.ui.DividerItemDecoration; import org.citra.citra_emu.utils.Log; import org.citra.citra_emu.utils.PicassoUtils; import org.citra.citra_emu.viewholders.GameViewHolder; @@ -99,9 +97,9 @@ public final class GameAdapter extends RecyclerView.Adapter impl holder.regions = mCursor.getString(GameDatabase.GAME_COLUMN_REGIONS); holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY); - final int backgroundColorId = isValidGame(holder.path) ? R.color.card_view_background : R.color.card_view_disabled; + final int backgroundColorId = isValidGame(holder.path) ? R.attr.colorSurface : R.attr.colorErrorContainer; View itemView = holder.getItemView(); - itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), backgroundColorId)); + itemView.setBackgroundColor(MaterialColors.getColor(itemView, backgroundColorId)); } else { Log.error("[GameAdapter] Can't bind view; Cursor is not valid."); } @@ -204,24 +202,6 @@ public final class GameAdapter extends RecyclerView.Adapter impl EmulationActivity.launch((FragmentActivity) view.getContext(), holder.path, holder.title); } - public static class SpacesItemDecoration extends DividerItemDecoration { - private int space; - - public SpacesItemDecoration(Drawable divider, int space) { - super(divider); - this.space = space; - } - - @Override - public void getItemOffsets(Rect outRect, @NonNull View view, @NonNull RecyclerView parent, - @NonNull RecyclerView.State state) { - outRect.left = 0; - outRect.right = 0; - outRect.bottom = space; - outRect.top = 0; - } - } - private boolean isValidGame(String path) { return Stream.of( ".rar", ".zip", ".7z", ".torrent", ".tar", ".gz").noneMatch(suffix -> path.toLowerCase().endsWith(suffix)); diff --git a/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java b/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java index 85b55b00d..d6fce6a30 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/applets/MiiSelector.java @@ -22,6 +22,8 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + public final class MiiSelector { public static class MiiSelectorConfig implements java.io.Serializable { public boolean enable_cancel_button; @@ -69,8 +71,8 @@ public final class MiiSelector { ? (int) config.initially_selected_mii_index : 0; data.index = initialIndex; - AlertDialog.Builder builder = - new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = + new MaterialAlertDialogBuilder(emulationActivity) .setTitle(config.title.isEmpty() ? emulationActivity.getString(R.string.mii_selector) : config.title) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java b/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java index 7be5f6d97..800ecaf86 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/applets/SoftwareKeyboard.java @@ -19,6 +19,8 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.citra.citra_emu.CitraApplication; import org.citra.citra_emu.NativeLibrary; import org.citra.citra_emu.R; @@ -124,7 +126,7 @@ public final class SoftwareKeyboard { FrameLayout container = new FrameLayout(emulationActivity); container.addView(editText); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity) .setTitle(R.string.software_keyboard) .setView(container); setCancelable(false); @@ -227,7 +229,7 @@ public final class SoftwareKeyboard { break; } - new AlertDialog.Builder(emulationActivity) + new MaterialAlertDialogBuilder(emulationActivity) .setTitle(R.string.software_keyboard) .setMessage(message) .setPositiveButton(android.R.string.ok, null) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java b/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java index e71c2cfc1..1e3111739 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress.java @@ -8,7 +8,6 @@ import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; -import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.widget.ProgressBar; @@ -18,6 +17,8 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.citra.citra_emu.NativeLibrary; import org.citra.citra_emu.R; import org.citra.citra_emu.activities.EmulationActivity; @@ -70,22 +71,15 @@ public class DiskShaderCacheProgress { setCancelable(false); setRetainInstance(true); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity); - builder.setTitle(title); - builder.setMessage(message); - builder.setView(view); - builder.setNegativeButton(android.R.string.cancel, null); - - dialog = builder.create(); - dialog.create(); - - dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener((v) -> emulationActivity.onBackPressed()); - synchronized (finishLock) { finishLock.notifyAll(); } - return dialog; + return new MaterialAlertDialogBuilder(emulationActivity) + .setTitle(title) + .setMessage(message) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> emulationActivity.onBackPressed()) + .create(); } private void onUpdateProgress(String msg, int progress, int max) { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java index 762cdb80e..f4833c0c5 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatDetailsFragment.java @@ -15,6 +15,8 @@ import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import org.citra.citra_emu.R; import org.citra.citra_emu.features.cheats.model.Cheat; import org.citra.citra_emu.features.cheats.model.CheatsViewModel; @@ -80,12 +82,12 @@ public class CheatDetailsFragment extends Fragment { private void onDeleteClicked(View view) { String name = mEditName.getText().toString(); - AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); - builder.setMessage(getString(R.string.cheats_delete_confirmation, name)); - builder.setPositiveButton(android.R.string.yes, - (dialog, i) -> mViewModel.deleteSelectedCheat()); - builder.setNegativeButton(android.R.string.no, null); - builder.show(); + new MaterialAlertDialogBuilder(requireContext()) + .setMessage(getString(R.string.cheats_delete_confirmation, name)) + .setPositiveButton(android.R.string.yes, + (dialog, i) -> mViewModel.deleteSelectedCheat()) + .setNegativeButton(android.R.string.no, null) + .show(); } private void onEditClicked(View view) { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java index a36bf427c..a6ab89429 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/cheats/ui/CheatsActivity.java @@ -14,10 +14,13 @@ import androidx.core.view.ViewCompat; import androidx.lifecycle.ViewModelProvider; import androidx.slidingpanelayout.widget.SlidingPaneLayout; +import com.google.android.material.appbar.MaterialToolbar; + import org.citra.citra_emu.R; import org.citra.citra_emu.features.cheats.model.Cheat; import org.citra.citra_emu.features.cheats.model.CheatsViewModel; import org.citra.citra_emu.ui.TwoPaneOnBackPressedCallback; +import org.citra.citra_emu.utils.ThemeUtil; public class CheatsActivity extends AppCompatActivity implements SlidingPaneLayout.PanelSlideListener { @@ -37,6 +40,8 @@ public class CheatsActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { + ThemeUtil.applyTheme(this); + super.onCreate(savedInstanceState); mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class); @@ -63,6 +68,8 @@ public class CheatsActivity extends AppCompatActivity mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView); // Show "Up" button in the action bar for navigation + MaterialToolbar toolbar = findViewById(R.id.toolbar_cheats); + setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java index 91adf7dae..cfbcf5099 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivity.java @@ -15,11 +15,14 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentTransaction; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.google.android.material.appbar.MaterialToolbar; + import org.citra.citra_emu.NativeLibrary; import org.citra.citra_emu.R; import org.citra.citra_emu.utils.DirectoryInitialization; import org.citra.citra_emu.utils.DirectoryStateReceiver; import org.citra.citra_emu.utils.EmulationMenuSettings; +import org.citra.citra_emu.utils.ThemeUtil; public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView { private static final String ARG_MENU_TAG = "menu_tag"; @@ -38,6 +41,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting @Override protected void onCreate(Bundle savedInstanceState) { + ThemeUtil.applyTheme(this); + super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); @@ -49,6 +54,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting mPresenter.onCreate(savedInstanceState, menuTag, gameID); // Show "Back" button in the action bar for navigation + MaterialToolbar toolbar = findViewById(R.id.toolbar_settings); + setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java index 0d63873bb..e288bf934 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.java @@ -4,6 +4,8 @@ import android.content.IntentFilter; import android.os.Bundle; import android.text.TextUtils; +import androidx.appcompat.app.AppCompatActivity; + import org.citra.citra_emu.NativeLibrary; import org.citra.citra_emu.features.settings.model.Settings; import org.citra.citra_emu.features.settings.utils.SettingsFile; @@ -109,8 +111,6 @@ public final class SettingsActivityPresenter { mSettings.saveSettings(mView); } - ThemeUtil.applyTheme(); - NativeLibrary.ReloadSettings(); } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java index bfd7c71a9..59c37394e 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.java @@ -6,13 +6,16 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.DatePicker; -import android.widget.SeekBar; import android.widget.TextView; import android.widget.TimePicker; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.google.android.material.slider.Slider; + import org.citra.citra_emu.R; import org.citra.citra_emu.dialogs.MotionAlertDialog; import org.citra.citra_emu.features.settings.model.FloatSetting; @@ -41,15 +44,14 @@ import org.citra.citra_emu.utils.Log; import java.util.ArrayList; -public final class SettingsAdapter extends RecyclerView.Adapter - implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener { +public final class SettingsAdapter extends RecyclerView.Adapter implements DialogInterface.OnClickListener, Slider.OnChangeListener { private SettingsFragmentView mView; private Context mContext; private ArrayList mSettings; private SettingsItem mClickedItem; private int mClickedPosition; - private int mSeekbarProgress; + private int mSliderProgress; private AlertDialog mDialog; private TextView mTextSliderValue; @@ -149,11 +151,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter { - seekbar.setProgress(item.getDefaultValue()); - onClick(dialog, which); - }); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(mView.getActivity()) + .setTitle(item.getNameId()) + .setView(view) + .setPositiveButton(android.R.string.ok, this) + .setNegativeButton(android.R.string.cancel, defaultCancelListener) + .setNeutralButton(R.string.slider_default, (DialogInterface dialog, int which) -> { + slider.setValue(item.getDefaultValue()); + onClick(dialog, which); + }); mDialog = builder.show(); mTextSliderValue = view.findViewById(R.id.text_value); - mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); + mTextSliderValue.setText(String.valueOf(mSliderProgress)); TextView units = view.findViewById(R.id.text_units); units.setText(item.getUnits()); - seekbar.setMin(item.getMin()); - seekbar.setMax(item.getMax()); - seekbar.setProgress(mSeekbarProgress); + slider.setValueFrom(item.getMin()); + slider.setValueTo(item.getMax()); + slider.setValue(mSliderProgress); - seekbar.setOnSeekBarChangeListener(this); + slider.addOnChangeListener(this); } public void onSubmenuClick(SubmenuSetting item) { @@ -375,19 +370,19 @@ public final class SettingsAdapter extends RecyclerView.AdapterKqsZu0-DxFb3>J z3FBznrHHcRS+rk0Y>Gr~D^)2%Ul~(=pdyEHN8D`z|dyY|oeh9X8q*^|J~6*6GOh zj7eM0Q~DI2b1?&=5Fl|VGCi*cP)SV_C|_HaqJCr}VSG8I@AP=kXO=2d(68YWa;Pf$ z0t`i5yjo?HW!XOhqx7LxXbxD@kDu}8w!6F;F7(5^Y5P>ih!9Z~}l-{jVW?$(3~YeGaJjVPoMg*2j& zMikPBLK-H8G6No-RD>EfT;4QAXm3EobCljU5QBUTe5f0`Y~OKVPhS25IbZIu1jRY9 Zd;z`*c?FdiugL%a002ovPDHLkV1g@hY}5b% diff --git a/src/android/app/src/main/res/drawable-hdpi/ic_premium.png b/src/android/app/src/main/res/drawable-hdpi/ic_premium.png deleted file mode 100644 index 7dd45a4055928d18ea4b3f8f47537f69aafce256..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 961 zcmV;y13vtTP)mRl$ONvM-ArT1)k!)GAW#1WL z7$)o3Gh^vY&(+)O-fzD7X7JS4z2Ezu_r1?O_nhaPw_N#(l?XvsUz8Ld}7tz>rvrfW5#x;F>cw9heQ&jIr)D zumD&U524P$5g@e~G6B8=J%Q7}eqd0X>kJ!#j97#k0QZ5=?-v94zzu!kl42P!Gm6l0 zz3sn-eU-EBGm|v>TF_a6`D?>7t(sO{1-gpgV=6i3v3h*iTlcZl+VLUKZWResP+a;=RfOo)c zV4WiqpWJ7ghpqx`%7D-)DY0Pyuo7tH%-jDT63_sqUsFW*H=!-Sm{O9-l1dvU$pbzb z0pEZZ-uMTgm&#I+TMv|y%oB%9{}3|Ifx!y&JYb;i15{O=tBX98D#NM?5}F9Cm&)7Q zS9j(tK3a-^725(yfF;obpt56Yr23eLz5t68BxCO0qWj4n8ALQk8q`&(o+GbidA7ZQ zERGymvYy%4QnNuPMW#+_tjSoBC@Nr6lXP5;napu421anA- zhd{H&tI>C1`yY>|^)QX<1MG{K@22!d>bt?2s{kAX_Gq6}#gI=XQ+@p(=d$8pk}x{N z#%fkuDY0Q8FxRsgsX*W5jG1~*a|xD|5b6jghm05M(*-ftPR-hoki%oPVrjMCj)dt@ zahRev^F2`Db)Q#s7_@@h0cfghybhe#{U1ogLjlZ7!I9XyPU~~5ikAw>$tj6AsR;I# j7CTln?ObPGD;z_hwA$Jo1U((Y8s^! zp$6OpO28cO1^5hHs|&LZGQEH@*r8C|6yam9#dk0i)JjN14v5G)gL{=Fbd3RTy@>RQ zNP{ub@Hv`+XL|d~z}bj@DKKvNWFLT*NX!6G(f(IJ*69oZO+c~6A|t>);2bb(Ayc(a zz(rseP^jUbjFSF`kbd*$4vQ_8jRVVVgpMRgrfNrRS@weD$4l#>22LlqJLjE*5g1B< z%$T!M18W^Y^T1)COIts1+cCD=htTC1uu))>rL1)PrDG<$;;KU}S`1A_#vL@9-c^Z-3TH?RRPm5U0HQ%aRw(nmxxS|FDgrc42^wQ?7gQjY-!Jr3g4fUe+@ z2Yw#lr$@l<07Bb5@aI#&cSiv$1JeP*_YU~wIJcAn791xp=!57xDjso?0y2)1n}D_e zLT$h@$GO>*ZVg;VZvdhF9&_{6wC@LYXhG|=?8KZ>YK0FQ5%~zLG~wx#p8|%JQiWPs zu{tIb?s;U`ofxck*V0jl0lEb=ScnL4JqF{EPc7?m(2TdhNnj(8*46=>0NyynZ}lN` z-?No?ugxcB$hEkhO0YpY6W8SDVk-UruufY~y;+}yB*z|OPwnsnxY#xgw7N8ClQn{$ z0>|~!b-@uGGi*qNR@W@cMt15##J?Swu@ET%J0mfJk-yfQ*L4ig2pj{p82k^sMemPh z^#RSMb<>thh%#{3au{bVz8M4N1TYbN9TPL2SbU?wsC8cH-2lD;U-gyV58xq?4#oTp XV;1BxGI*AY00000NkvXXu0mjf6ZTW7 diff --git a/src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-mdpi/ic_cia_install.png deleted file mode 100644 index c6dc232b44e3d1f2aee1efaaea1ca15fc431fc33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364 zcmV-y0h9iTP)!UKp& z7gW?l7G_~s9yt5*()h!`%w+y=c8j+mLaV?t@B*yG7_DjmGl^rXX(?jCr^-JTv0$q5 zpG7Q4rT@9$5O@IU5u6?19yqcL&s6NIeogfcRKFT{{?ties3m|J(%TMZPpwyO2F5Z_ zQuQXV9?Z0?aIZ6Eo&W>jTfT=OFjBv_W8q%s!6~Of?V_6&?$t_w`smLK_qs@cUs}xt zV8bxiQSd9nykoAo2Gb32YnZDG^U)PwQVa}6t9t&n?x}bc(!3HF|Hk`EvQgc0B978O6-(I!lVlot9yRf~x zzISTIQ%0N4@DECHhiBi2%6e#+!xAX6{fg7Z0H$b8rx;JgHwVI5+A38VS=b^Tb}94a z@0{{_V%OFK#!9Sh_k|?Sy>S1QZ{W<{%WrGBDScyGLQJ>!ygKR{rf*u{ z&%2^g>W8p`w}m~ogR$ZTt^<0C8~BeKACt+wb1MA9bS~q=^&byBVQqZsYc^MSWgyU@ N44$rjF6*2UngF(pO?ChP diff --git a/src/android/app/src/main/res/drawable-mdpi/ic_premium.png b/src/android/app/src/main/res/drawable-mdpi/ic_premium.png deleted file mode 100644 index 4dfb625961f6f7c7b125454449d7be2e456c9789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 605 zcmV-j0;2tiP)tEYHhcmu9owL_odwqMY^{sun)i-pPVHFl&48G$IPT)Z1GZ1Sr z9YgRES8)jKN+<|=;w&bV9ReK@uozZ8w12`-+)wtpMAF6J z$FVSer-m*@k5JtU>_uDQoH9ta8DC}ao1vtBQJ%qJ-xLHpV|@j)@GkYB2`yC;q~P!I z82!Vx16y%1D$)`?ot^rV2+}ZZtRi@OG;>=VPB@BPn1>PAgFcuT7wD`BeQ&9P;9z{O z%WOB$6q(HpCDms8EAyu!)pXKd2u9;BzGb%O5%?NbM}|3G*JO*C#c))>*FTth<1%K1 zdghldP@5Bcz!bc#Krk|t@+GrfiIObBdORwA$Wb4kk74VGvLw8T2|gJzeT4z#3<}(f;d@-NCs}^NoLDzU){UX2 zYgzEA_*{t~;c1ZE&TQ$==rGP;ERKae_2PUqcwZ>)=ebHHDwiew8BL3Inuq1tetI_2 rUQ$`|`%NU(5EM8agH_E?{sMjgYyxm5Sn*(S00000NkvXXu0mjftwt2> diff --git a/src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-hdpi/ic_cia_install.png deleted file mode 100644 index cc986c8ac9865062c63f5c1d78c195e8246daff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 556 zcmV+{0@MA8P)9dv~5!)izFSAv$pwS;>SV_0P+AbGQ}f+ zVE_fwlyMsx0386@m3|k1v`s==AP2yk)-Tl(sN$*vl~Wz4oc|>4DyL90 zV+s@%DiVw-ke_|JvL?ArfkfL5fDz|RyxKs1_8G&tK>Z}wNG_9Xv9xd3V0=o}77Trm z_Y+sEhDUZz^4f4_)|L9%B!FJ!NuobzO<4N~@&`!m{T- u0weSyW@rY$iu`J!UV_z>_|DV>VBr%0;-TvT!dD{z00005t(aoJW|Pg% z=F999`4 zs1>|t3XI%Sm%gaeMx?+7xIA}65Of5F0z->s|qK zffex(Y7ZO&(uyDx;2Y2tI0@_p`p3D>upY>cMW`-t2PmJwriORGRej<_u>_bFMd+yB z_Fu!kN{;)?6b=3lGKTs7Kq@fuFF*7GkgM!62h>*bGzNO={+3Iq?Oz~~2dq{$UXQTp z3{2JYe-pA8dIHocK{A%oGl7rZc)Bw4y*FMF_!Rs}(XXU18ki_DDGG13u~l z`M@)8`~%QUW$Bk&_Z5@OLx)W75He4J0SffzKtJ6FsH8eq6L~02hLw^eG!|GZmAALA z;>=lmG!+3Wwgr*^OQO3#1;^G%^)L^aR4+)9jJbQ0?#Fv%5YaqoP)DVDp1iimv+WLa z2r_J`=f-$(STAH46|yGKpbXW$T-D7wz>^UB?0}Hz!;mBh1?`7DqsDI);sTXPLZs z9HdnM&E_)e>9%f!!)V~uqR@^lhPZh?>c9$EU+KgrG3&ALq3_w>g)eFmlXw*gyA7J zRGpXa%|k1|%Oc%$UKz!04SB5>XQ2>tmIipR1RclAn~S zSCLx)lxJYDv9BmdOwLX%QAkQn&&;z`dcS+Wl0s&Rtx~wDuYqrYb81GWM^#a3aFt(3 za#eP+Wr~u$9hXgo70`g()RIJnirk#MVyg;UC9t_xKsHENUr7P1q$Jx`$q{6sLWFOC zf^&XRs)DJWnQpS7iIIZ2rJkXgfu)g&j)IYap}D?+xxS&Xu7RPIfti)Dr2-Tv*>Ne@ z6s4qD1-ZCEEd%mwl`=|73as??%gf94%8m8%i_-NCEiEne4UF`SjC6r2bc-wVN)jt{ z^NN*0MnKGPNi9w;$}A|!%+FH*nVFcBUs__Tq@)kBL?3Dk(0lrD{{cDh5K7Gh21^mp z|MO%L-ZL;TeeiT~42fvnJHy{2)KTPE{rjbo5*;1_UV@xc|2AhO&ivbIu&8BI?v@}|n*cKa4(qIa23{w!kty92n5oWM>iPKuwJQ!#RK0kvZK&_&2-C)f>mQ zb*`LnC13JM>yo>Z&Ur|E?>L$0bji~5wn}N_A7`JXR=84{d$9ZB;d@kEN>6%5-#48K-KcB%Zp1aX(la_1y zLhiqljN}YGUw{5^#%q;Xa*_8o?QOjWwC>FBmRuk$QO0#h#VwiJu{_FAow>97pk{H+ zX1P_YZjGA`%njkWCRm%$Snqw~tEw3D2IZd#TveVtB z%gqgp)sJSVi``dWWPS6x<=SmN;U#B%0jqWTjmsOQ<^JzDZvHGo{>YwoNsVX!FDUn~-yRTj seXox0I)PBGuC+21d@@Q}XPQ1rJ4x-znjevE3@SK0UHx3vIVCg!0Ft@Vr2qf` diff --git a/src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-mdpi/ic_cia_install.png deleted file mode 100644 index f61d84961525c9e2ebe234ab1af54b2f401b153c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 405 zcmV;G0c!qLZ84!O9c@HNwCx|t(}lU5EMm05DbW|m6h07X=CFf_yA(; z#J`v$D;pSZe(oeDjRSMb!1}!Gz$`EZi~w_9P&)$e zCVuOMK-I)wy%6Zg{-?k?a0}$y;8_H2fI{6#=!1k?TQz4nv%hBcmlBv?e2psDF!33X zi}8oquf+EPaBlXCo(h6*CT=w)Y1FJIC5~6lJ^Yxu_Hfl4us%7f>s=$ z3rUxbynN)m3yaUVC^?1{5OPj1BSc#;N*lD8(zVHeQi@^WL6ag~VITN9-`hm$r$Bc{Iy5B3>dwo0T{C!BfL?E%AmvI)Ch`F~BDl zZGpC#C9uJ$5U{EX6<;ze4IE1Y?!X=RCqPxOze=E65a=baE(m<*Z#uyT9&pGRbYp2g ddCrRyroS`LbonQ*OvL~I002ovPDHLkV1lmpSy=!8 diff --git a/src/android/app/src/main/res/drawable-night-mdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-mdpi/ic_premium.png deleted file mode 100644 index 87bac27dfa30bf09c36b3bb6d80cdaf8105103d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 595 zcmV-Z0<8UsP)i7#9>}G5q>%EU7!rw;BHlbm@}Q7gDJ7(MkXtD)Vls>g zNfIJS?$^n^%{sN6e0*R2b=qgIz4rR=wf0(Tcly-vQHCX$h2i*y8#snNnND9U$7Bq| z3tYy2oKr$U&=qGfrmPTX3xiEKfyN4YTd_J96a?)USz%Lwr|9($orJa6loK>#cNH6J zK?>vJq~93}|HIHloWw0OLWy+&8RAkUyCsGj_?c!`I;cu67v>J!`tM+sTv4o#CQ|k<9~A){vPSR z9%FGHqwBD(O5>@Bo?v5U{4n-n0p?-@w#QmJbaP$g_qr+w_Qkj&lU+ss2-}RP`uc2r zao#FYOvn9&U>I)WYbJXZhFh@|J4$_0h|Q;9jNV5kec}mN4qUkYj3a=X@n_4sD6VXwLAmLGv zT+d|bF*<-V7=a@pPj`_DD_!ue$he>TDut*Vmh>1+ig~Js)G-UBwvpCSmL5P)^GyCWEX51Xy2Lrcn?!EVYyF0^svq%axPD^?#>4T)f zPBzxp#*$$*ychc3MF9PVXKBn6iUEqxfSfl1*MJeAFOx@xitj+AuB=*HlFz848l6n>*&*pm#OI)oYIGoS zy##n9Cc6$G~0>0=j_-V1;9@oaUTGz~vabc@j^6JsD!e z!K|lA$DL2h{RdeDi~*-&=E_#Y?i}Q|+B?50X8sazJ2grJ?c0E3Vb<_n($2d7-b-1; zn8zw~`@aW{`R8_SgQV$%I9EeA_2(0kf0DFT(iz9xkfbh2uM_-l zHp(aOX~19tiIEtCWE#b-ygZr_V8%rcMJx01DHM~TCj?|ii%nyz|{HcJPFvBr76jXvOTu1Z+w=Q9mn4D9*@GS9%KCienxDw z?Q@}`;Sx}?qh$q{HSn$gE@_x?eY%RB`Cq`-C7{!u7PJI9TIqdv z!3gMDe6%uSwGr^DMaa4^Won`Izzv|seofcnh)z`&^F>Eaj?(fW4g-Yh0Z5x4X2 zqL!SJj(EZ&__1sR{m+<$uf$aS66 z6Hm>Kf4IMAimCuJKda6g28n4Kn_U(J2E0{&uy4UF`xEXp46j)2l9^>1Rh)T$UXEcf zP2fG#AR3pzAMj{an^LB{Ts5YW|H| diff --git a/src/android/app/src/main/res/drawable-night-xhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-xhdpi/ic_premium.png deleted file mode 100644 index 23a5cec5135b940f18327604b3d6a1edf43fb001..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1281 zcmV+c1^)VpP)dcLCgU& z^k$$9Xeu+tdSC%C2lyePb15(rI5dY%F9UOdH*)9(9heL}01VGFfWz;=wZNlJHV(KS zIIhC64D*0l<&@G6Tn*e;#bh3~60?C5fY*V>8pc|z4;qDc;B4T98jVlzFfaw!y`Zt$ zV~vReHc=n2a{&|91CxN~L*pjPJLi9Fyx~D$hfqVKQswl)x2YoR7269X#eOXttqA<6 z-v-%a_2B?uh#T8J^3x#)j{lF0eic}z=Sra8wIz{Zb>nnkB5)$GZ@SjlCSDF4Gl+&; zrn?h(r-D9BT7kEKUf*C2tdeK-#v0%%;JL^=yU5T1t;Sz@>?sXW{C}u0Sdo%X5H~sr z7!B+v`M1DVq7@cqU&aQu73fjPuNOW5T7k`O{8n+TQ&MyGXa$aRGAmS10=EIHLJnBD zKDSQBTiA>N);gKR|8)`l?G-u$fwtdNkr#QPh0puN+Wap}Od0=dhVh6I49RG-{?oj$ zQ888z*s{Ot#=TekD1=vlGk|rX=ci`yGGn`^C4Ukw^eJ$7flh1%4sbOT>LTQmdF?7|@AJ zp$h^jt`haQFqxXySo`r@Ok$VETsY(Ziy7%=t`xv2nTbErHawCC-+T5!% z`fbc&qMciD8rOqdr$!?Q`iggcaqe^cAWRm0+yX{XTsFarjPmP@c+I z2Kouu19(KTr5PWgJI`6*;@Fi&1kJkpOz>hv&zze9T?qt*$!TnA2B%5m68FDP_fBb8&hM1Wa{c3y zm2|nAtjvv(IqV)bdOMwkyLHoC4(-n2l5%`YppT7pPh>2UUlWnftkwaPp>8On(=V#9 zQU6Elc)52#ce&VThc@!?;Zqle1Gx6p~WYGxKbf-tXS8q>!0ns}yePYv5bpoSKp8QB{;0T;&&% zT$P<{nWAKG$7NGt1vDTxwIorYA~z?m*s8)-32d$vkPQ;nS5g2gDap1~as*kZ5aAo3 z;GAESs$i;TrkiYNVx(Yhsb^?rU}!BPbD z|2&z5_Y4dy?4B-;AsLNlXT^4ehKd}o|6b%SyY*x98J#&FjYXz*yiwHZ@XC-gaeJXy ztLeqgVjs}4Xu%6tAUdwzT_w+Ioo$sB&X*p^#6K_2 z*)EdVBRD^4{}Mk>c9p-v3j5^bJ*qq|o#xwp)@<&!+eIUN^`0SNx0{Rn_Xo$Y$^a^Z^Oo93=R86j4&uHUOF z_L7$8Wvc@)^HA-2Q|I&^MgWks*Pizfeb)>$2e}7E4+V%L}FIM4F5wnkmZuhiX zb)ey4#}lQTDk|Le-O7RX)dL?i#KLZ46fu-_8&_G5ht#SwF;Ah+lTP zHnYdN;kH5R(r1nZA`wzAZH_Yhn;t7}sD593hTc~5^&5O9nS@_*JfHL_hS@b|_wxR@ zndf%so(^=Wo3Fl8#a2qO&X?i8mURE=ll+W+Ys$?nlY<%F2MA6-eep13pUO*P&;Ao7 zTO!xZT5@%g9q*GjT9;mUUHQ!{{OMo{5A*#G_jR4vpEb;oJ~2J(#YQ{bj&~am%*|4p zH^p`N+og44%T8u>-q@Jt;9&i1@?ZI!&Rs`KA9&+$*-dghyL6PvX(f>CDO z{1d{O4=&9(!FjxNZG9ZW;sb5ZvN?~1Jvjb4E%&Bw@Qkhkhh?G`R}Z`_vU*r?Ix#&r zJf=0LyY<6QtJy0j&6{*ZUg3|o=iN!mu70$6{7~D1L8x#sXXO4R)w*SCC;j`vlhD`t zSZ}7{`ZcFZ`#Wb Y@y85OmXvM@N&uCop00i_>zopr07r*qhyVZp diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_cia_install.png deleted file mode 100644 index fc3c434b0802be5af58f6ad39ae560ef6077ffa7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1168 zcmV;B1aJF^P)YfJs*n>Mb>JDO`6$hScR z+5x-`dH;c&W5?h985xLLP<~9+T zEwJ@U$g_rsGg9$DhJy(O8WWM33VVBepEo4L9=6;!P5bEtVYM50%aY@M(${E+K+7D; zyc}61vw!xwLx+5w?=y!8^tod>BhtYD`@W0;?>LTDW79_p?`R z{9I9{DS3#@i1JF6m=h79J(P}&Jv4^i#nmS>&N>7fhY{GZ}XMsn7FX}ejVtYgeN;nXDt+{?-n;adAj+PWm;#GfL#hFe!R3ciZu*F5?Kx*pgB{G-XGnF#gUCX+G4$}D&8 zQXRh26z%`3`NJaeXWxqWgoxZ-<4V8k+8`nm1*`JEL}W`OAMO{C&-yY3CL^#3 zlC*(IT`Z5};K~K55+ly$NOhZraIQpyr-pQApa|19)R{-F;#Wx78 zzhS0u#reL~{8#w*Fv&gL@J`Qw@r?NQi~s#~FN7xW?U?%bmh}OP4HF*Ea-X);e#R+F zza0#FSo5Cmc{lNhSc!auPC?uH1I!6~4@gFJ>ApxjXw0)D`Qp~5=NVc|vHAyP1Yi1E z1y1oYzYspDF>jgh(d-227nTc8cZYquELHgOqwo)=H75Ja`W)vsO3TVT+46(0h9z#9 z{;TzC4&T3&S;Q*Al-BtCYvY>*8J~D&sJu;@f9YR7`_Zfa7HnT|t*TL-@5;Rn=B?%r z9GaN7ZhsFXIr&s9CV;pUFa#R4md@LdFmwB&ZM!yo5w6L87d|cT(S~PcN*Ah+XTEg4 z(cRses}up?!NTdH$kf6dwxFCKL$UBU^mWxf-b;Bz~JfX=d#Wzp$Pzm Ct^54| diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_premium.png deleted file mode 100644 index 2a0f1568fa4085becbe422c2a9c3a1b94750cc63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2049 zcmV+c2>$npP)% zcZ?QA7>6Ga6%Pap#tI^KV>z)v>xe(cE(0RHe?wQX|+a8jAaya^ZxH0AA>B%rnR zMRqI8^cLqc;6&gZ=Ssb{#Of6q^KD=+;IBNLd;&71`z)||S*N!+&C+LY0Q&(A!15Iy za{|y;3O$I{u|{oyQB@F*u`F;0a4c|R6@+7S2k!BF??mD&=vL{6|2sp0v+@ea%<02R zfp8*hTR)T4$X#lKDn6y!;zC*KR3ZWO*Zy3Z*abKwR6u=~GT}t@&uU6z(>I6!0(QbOuuvl>VP%mx@-gN zm9xw}#Qllr}?R?Ywg!8^zwOB7JIB}6#3LT&)=RBELt zGZgJVEdh1cYMbY~pZP)WYw6W07Lp7Bt}PO5mP(r%*FWC?M+5I=w6hhkCeTHCC-QHa zl*F@y+Mh)x&?aeV)c}@g>z9&|TtWJyw-jx?ES8UNC1ktjD-wJVw#CLuv)2-j$%#wS zHOB*I0NWOrv*n>41+G@wB^C1(zOFIhSgPgl1mT$V`2e^#ML6S>yWUn=%s3q+VAH+V zWMvUr=UHx5!2FuyfVrNb1}ec;pD~Te5>jKv3bE~YfpSEfCN*lUN!0=~^<-yw=6E-7 zW#AlLJyaLaelsQQ_D&LzN%^-L5YV6mk9L^Sv!VRtwEHozPbWE^J>wUmd{9G3WLrTR zHM#iKx=be_S^OqJ4hKGtS>_G3ZBGq2u9_x;H8n1vQ_|e+DsUt)CN|7IAqg$u7hn(I z!;E$t$i#QfX!oL$`wNXkliMFccMCy>?TC4~Vl zyQldj4zs0ykZF%n#`B#v(dXSIJd^?nQ75K{+Q!d^Zj+L#7Vmq;=@mG3vtzB6&nU#O zijf zfb8k^_k4+R@lVX+P(VP&0VGg>zu03?y1HK+KwkU+iG)Paz4iB=uEB1%Pc-l zSE{54P0AijiuJuDz#!ZntIDlcvg=X(y}j~Hk^8WUtXSG>71MQ;Z7JHa2?)LDGCXIn z88K4*<2()=n9=usJ)o=#`VF))vT898(<#qF$|)d}FmFi6$)sVv%bq%QaOtF+a#n_M ziq6{KlphtXnAK#x4$?|XDsG4vEGzvV_}y})~o(&+_S z(Z>b$vs0Aw;{PmFN&9$C!C~?Tq=X|etFdS#Hw)WLY4Jxt4TJq`RJ~s*ue?cXX(K# zkYe@;X}V&|p;={pl@<_k^BV%JQJlC5cWI@!5s%P2CS3#W*DQr^=VA|qzDrvT0U^^r zJi(MF@szTKS+r9r-gYXA@1Rg(u2#o%)lrWP3`rKDS^|o=QMv9cf?rpvG?h})Nt_cP zpo?lnIJCeDis-MZG51(zt_r2@Z;1&d(NY3J^*`oNt}I=#Z{REARi)Zn%*7I87WdeZ fv00*;6Ar+?6yC-joZ~%a00000NkvXXu0mjfZu9Dg diff --git a/src/android/app/src/main/res/drawable-night-xxhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-night-xxhdpi/ic_settings_core.png deleted file mode 100644 index 23706188b6beaafec4fd6c2f604cdfe653995ef1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2125 zcmX|DdpHy98{TH-5Q>x>TQi3oHbc%s5vGQmLddWfGm9;AD8pCC;c{Mb_*RY2AzC?9 z3@gc5k}?!>N+G3EWc8cx*Y&;E^*rzMyzhP8_jNyiyw{uV;$$x=t{@Ho037zu;^3!#LgVMliapQwB zqD)Oqk$U<_eSKX4LN_{=M8VN?Nztl16#wV2#YYE45y%umBni61i3^ORQqV9Maz_OD zM@3){`JX>Q^WTKv=wdKf zeKV>ijjdA+&v^p5uDAEHor9Pn-$mU>dh^iD-emQwdougjw`C*Pwr>4mqw0!{?{1^{ z-*Ih>vE{b!D{ap#*Zik>s!#Gw(AIu=oCqiV_{j^^@OpEpikNrO}6~mi$dz z(g97vEy7Dwk*zMOTCpQ|Nn(ZtXv?-<>uHC(rUR(J4}7v;(23G24Gq|e+_T$!1O z0LHD4=aYXL=PR_iY z`^|_X*B3Ol4M=hhS4Ye9+wVPL5q;uiM?G&IbQA` zkGX|+PQ0ITNv0J(m}lteda=CBKuwoS3~E$bo206vm1qY`ezoe$d+k06-l8S`ByEE& zcCA3l!Py(b%95Px@Oc^L4XzHeEcLU^WktGipBP-YO7Yc~pBBY7>1A9~UWr!$D|Yxz z%kp!XAl9B2#oVF3?4n~H8jaliOVk#dl ztxR@#&RMD_fF z*;PqsLYf?T?sB_f;3}p-t?GI38i(eDr zfVZE&?%A?KnNi5n^`os7 zOX+ohK(`W)gJ2x~!VSu^sy7phzR~+74sope8N2kuVn|smH@Y*si*WKUJaD_VWqT>B zZZkp4+1E?Lw_L~w(8*rUJMe843@y6}jMzbG|Z*r+zRPKrspgd~dhL}s@Owc{=zewFOi-YlBs63M6 zMM(26pk07X0?ZP)C4NA5$-U{WM9G3iIr6-6SNY9vV3C#AdV^+uJ$CEKm5~<9x??SYlVfTa@nJoYVwj^}YY}Pf5&$j?4;;r#LY_o_B-KO8#`tQ(nuk`D<5TUuyGrores;poOv;I~~b$2Ji6Iv(N4jct*-Ocl>csU|z#p;)Rx~>#wH>~{XWeqC1|;z{IET4~ ty!RjMq*gfLC714l%!fWvPne5-I%qQ6lix-gFW&hDV2(T4)*lN<{vWu>#l-*s diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_cia_install.png deleted file mode 100644 index b4d1b92b754f4874ae4a465328da0893c0e8104b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1433 zcmbW1X*k;l0LK53Af&V*)LPL;kW^`DT4&~%#MPE!i5|{6>%JzX6@rq4HUx90st2V9 z-CRwTIEJ~+MrpKKXBbK}O}1e6eV_I`zxRDVyV{yiQCZ3U{xQ>US~)^SaUAvnGwF8H{Rs)Iux7s^>%>*YvTydPmF|K`4SLxXxY+!} zbq1&c@wABUD%)TYlv|}5XrVZ(D~HpDv>f5aY4EE;QJpe?EZ{kXo>2svyH(MeT(Hh> z&YSX!p1S2JKUP!T`EB5BsGyv)L#-t>&{a@UpV4&Dy|*?x5YwZ%y$nONOA~?uSRRLq zrQ3fgG|=RRrHXlxh49oZfBsybk4iLq(jQiIh-*9C#%+xPtEtp$7R2)weUx+ew5c;M zWgILrIZV-OZtM6tqe>Ih)=c)o8({a2k7nmTK8$ zjg6W`GTROgwp+2F83Md8M`eilM8JwRon}M_6P(_TjG=8!OmLMftG-W+{8-CbQ|b^4 zb-Lt4AnYCGxv|7wU@GF1C2&UUR9PzQBvf+eZAui}c{>9q+*U9gna(sv8 zLbH0iaxsXIrt7B1`d8DKF>#N)lrtO4a}KBmA?FsC8{EWQlH#X!ih6TFBKVi3tO4X0 z*%EuT)U?YWjKnbP$0Jwf$w8h5IgPXn@V~Hm*#g$bPrSNU{>~)jp4eDRtQV4k;CcbW zP@fy+n53UjlM8(N7PJGyaM&L|#S-jNwRorZloHttPj*hJI**e|dob|gDt?&d<3dAX zePoR7uh=JdbRHS)ilP;5a*EFNbX)M3N=GB7Ohv#(jsCOUG7mF4-QL4$XX)_!niw#A z%{u_ocNYEyw*G*zW9l*7FoitBV!bJ&FWyp;ms7^9jixeQEOWXj}ydW&WDmQ;zyTVcrzQSFeJ35Me~bbbX@tsP;}Q zqH74SoRfTY)K@wDD0_0?MR?q=2D~bcKx2VZ{kRNB${TY^9%iqt43-}hPHt#OJzLR_ z8I5PA!1BiPozKkk>tKQJ!J*Nau}7hXLzmfy$}((lkJHxn{cW?j z0fi-zUa7F=q~kZi>pShYjHM@JkNZpg%eWz&$iFTUPS`2A8z(4KRdzK;lDZcQXs&AK zw_MVjv(^o*NW=R+2=zW{-WhEDHKsQ_|8qz*rmr=Dk01jnAi{Lhy#Pvl$i8v`M_XsY J3mf0Ge*i6Kna}_L diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_folder.png deleted file mode 100644 index 166bd052df4b1a63528452577279b1a565dbb003..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 657 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 YA4=f~i5K>1qW}N^07*qoM6N<$f>n3_dH?_b diff --git a/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-night-xxxhdpi/ic_settings_core.png deleted file mode 100644 index 32eb4faff6531285cac44352ce1a6203a4f74772..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2587 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 N!PC{xWt~$(69D}tg?9h| diff --git a/src/android/app/src/main/res/drawable-xhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-xhdpi/ic_premium.png deleted file mode 100644 index ac4b19ff4221bfb6e731b04453457fcf8e898f2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1334 zcmV-61hP=Qz!HIbBAAQkk$GP4PC znkcO)(ZC+Y4COSvpHyo0P}2!T3mYd^{3mwXz0W=O&Yj60F6W$m*4}%qv)5khU;CEz z8!(U*j0DaHjsdEH(ZFutC*TXD<^n%PWUdBo29C<0)9b(kz^V+gK?9}(j{zgH4B+5*;1=L%r)>i8 zC~!i7V;SZFv+@a5ROE8*^Nqm53|~D2`x55?gMgL5$-rv4N-?Mfjs})wHNYl+w#dm` z)B$6Fdy4!JE^0K^t1*GZh4uqB=ap%>;t7AhNUHTqt! z%oqZE34G#r$jSH1iyylr_*uX0(#i6}p+KcG_zd9rkO3$CPfD)=ycwdD~Vd( zI188xoB|x==Nj9@ZNPDTsQAmkg9@=(F<2|`4$$EnuwIt+HZ}q`053-786riywHkkA z(I?kO{{NxeV6E3aS={JU#ruQR{vGhOXob1iSFwR@1=^MJdkbrn-2QOJuM^ig-J7#r zD=^7v+X*ZN?giF|3}^-}=q2UNZN>qcoHq0STO;!QveqyctUhyAGHmXE7hg+ul5bFYPh#tmUahzGSgU#1xBa{A;l^q(n8T`T#Gy1!iaX()>kIVk(s}l@!xlyVTC+ZL_h8<~4bWWZP6vIfT{hJ7{iD9SCqdr$y-Qa}N5Z30S z($^_R6oT=N><@kc;^2GW5s%7P1o{OS4m_o{Whoz_HA^jUMeIl;f-0ToBzPqvXU5$G zT;{ZOE573-zwIdoG-oYlruj5B4=!4hCOky<*Y&sKu3I*U60QrSE*x? zHF{p93H<}(6U8jwmaA=%%dFJ<`M_0y@#iNioA3XD7%W2Fo=2urRAGmn$H<@q0VaY~OGlDvX!0_9mH5rLNT1|%Ft%x%w37e6FVuxxtX8>rc8bhc z@tYNT?o_2VT>tv`*s49|kGWa@JrfrMz^koG zY5?NyucdIGkO2uSLLDoAA13$MB<1%w%=J+!0}yX%5sz^(JHuWn8l^MG)rRGFg$LK`p56T-#O=ZE`Cp)R_i(6=lh)B|2%I;RF#k-BI|($ zU^wu@{JS6U7HCq{BSGzjbU;L=0c|mAGgS3fD4Q9=%2ErR`j7@gJ5UoyC#(Y^k}o19 z7OG<{bSg!pFvi|E5h=6sKvnx(z(U{&&<&gcYWmRq2&ly>$+3Sta2EIlbOEbl`Nfc5 znS;Q_fAp$=CQI@P90n#t*!mQq+vw#$ALRe86Xxrj0)9J?&tB(&`M~ZNy#^--3^7gF zS{cRlzdRr!#lRb&kaWpUpiEUi#6_V&DI^b&2Mo4~LO@Fje?nJX?m)Q-WodGt+BFKT zN)h?(zIk9@0;BJNduDYez&W2JkySey>jn-2wLt$YoFZT@upjvBh&eNf1Fa5*T7a<@ zrAY;F&H?v|ZzisCFtR6(cm)0~2mDFa4gmSUFyN>qc(W_{EL$fnZ5%f_Kq51-h)guK z&s1PCPz(&PNG@H#2vzOz?BMc%cg9|vd;vOu2f%&cqN-j4s55EWCqnG>J^ie&uq8o+ z+63@80l>Hb4vb7t&3??75a%N=RrOiY0!LNfm_ziD?-`)SbMvEsg_ex8n|Eyx-|j$4VE@qO=UdTrI?%Ta5Bq0$k#dG z*ZP*zPaKRiIFMiOfZLv=A6Vd++Ajl(f&2)pJab_$Ibts9i(_8QEuZ5U73P`sT14&u zFS6qN6mz#+We(8nJlq9Vq^PN1<2qU_HR(Mq4*Uj2MRCoQg?E`ml@<}xA;$lNOedDM zp;pe`IO+>1@Rf8*kL_%9c?wUz(RHlS5CeG1lmc%Pka}k@j`@}=afz%k2jI3Woq@nw z;D!V7o4`6?NEXiKI9tvR7~r*LWT=MZtQ>kpz&A?=%5z{$H$~(xa}OKi_*uQwi%11f zs;akR^qyOEY8|Rt9{0=&bgOEI&8vsaTp59aUg(DwcSTpQ9dBBo+l00000NkvXXu0mjf1w6>+ diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_cia_install.png deleted file mode 100644 index e6812f0d48724cae80aa63a67070b5ee5f5f14b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 967 zcmV;&133JNP)w_2cdY?oAlzvgLtVY&r-4K z$?c#X)Z&VQbP<;d)>?^X;PKv@WHK`^$$QNQey0hUU%r!Xl1XOpgvG!$;63mOxCylS zlALRn0Uv;ok{ATGjBaWEbE|?lQ9Sk`L6^)1-YEq70jzeJ;26gOjRP7q*C$%crwrEu z{RV~=*N0^pXSf!q;Q0PpTnpqqK_Cr5APqqv4M89cK_Cr5APqqv4M89cK_Cr5APqqv z4M89cSp_P{JsTh2$_sL@DN}A)1lk950^Pt>U{#hxa;62i4BVCn0LSEmv_^Z(7U>DY z1m?(-jJPe=pTIi9I(GtpD%5qwmN;7#4H9wMpsu_H+6xRR>N}jGy~bS92qfZU!bK6{B$L%*2NKvS3UQ=8uj-FW(iC%CY&s@o5onizINw~H^)~}A4MaLyp^mfy z>1M0q=mR!c(4!&?fj1d?QW}Bw8}yt3pv{#y5sM7%Ry=T<3bfnMRx8~|QjV$J&d3_X zp!rcz?ia9LmNl`}fJkZZQ;e>UG*=nPol9bbEXt%5(dgEH(M9^Wnm`odAffasB5zkWK zMHR(yx(LjRM#IHq(h#;B(7;adzvgCtJml)^0UD zTbW{Nx4O1f97*lg=gXPfH2G54X7P|sx0tkPtFiJyYzJnRUKG21pNkT^XK{#9l`91;bdDY|&Or6iGd86=Ge#e zAJtDD>T2D)$U~Fc%&BYd+D}Y-*y9?uiYrJ5ojMezxin%zZ$#9lg-`5d=dSLV`8y!( z*@K_g*3Udz^8B8EdfK}rKiT=xjLRAv7qC_^#d+;{x4SjjnkU zooAi!$o%0JX7O{|4P)v*Rs1`ZZ9uV8WafhK-a9xn?@$VUZ_7dp_>Q;WY@=9U7TD7hrH(`^+ ziwhR=YIh{gOM9G;Ik2+Zp;q+In*Z0{HLg2UAAVvb=M2+;x@5Lf>n}SfF+csDqcE}2 zg-uA(BZV?XNXP$@(p0Sq(WhbZ1?z7kAE@G zmb9Y12U-K~slN%A){6`KdthGSy|8luZ9z5sBGayz7zlr#(Dr`Car>I}A`Y(}e-2Jy gjp*IIk+Fi+=}@5zOG=tHFajAoUHx3vIVCg!02Hyv+5i9m diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_premium.png deleted file mode 100644 index 63f162e529bf138d38907d4b3b4d0ef6bdbdcd2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2096 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 diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_folder.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_folder.png deleted file mode 100644 index c85074c60a1d59bf44477e7b6306b2259241ea0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 591 zcmeAS@N?(olHy`uVBq!ia0vp^2|(=2!3HEFS0!FxU|juPNP#^{??VahNfErGCA}?_-vt z2lxJ2U+_QtfCtO>OJ~b|iTq&xbK;%8&B3`-{$6=&efR5tsCS5^%(m^&=# zMVQJsYU}*ZpDV6#jfYV$doR=4c*O&*bIldLnX*`{`pUJ1?{o5+eW}O8B1;c_j9L4K z^Tky0_?=%LNoKzPD}PRV_T5dlqT~GTr~hBrA7ydo?RN32(_e~Ak1d<_L}LSw4#W8d t!}`SPEotA}{!N;DdGpMA7-NDT?0%g44$rjF6*2UngDBi13Lf! diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_premium.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_premium.png deleted file mode 100644 index 6f1550a1087834d2920e9821fc1aa330bc46c9f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2654 zcmV-k3ZeChP)J000UlNkl z36LH|703VC-6R}x#VuCW1p|s35yD|ENCFCy5GcG7O9>ztp{ODXNRWi0C{$8nL_v|l zGjbWviiMzYYdOLd2oW%f5z1U>Vj@8jL?I+(7wcL7v6^;zru$9LeDiJiRc-Bl$8`7f zo9=#B_YN97bQeV8-2lb{*bl&g0J;F|4d68Zn*lt-pT7Zk2*6)sRqTPZYb1cT0C)$0 zw*uG$z^eeZ09ehotpjjBfPYlgu(pENzaPMK04K5ILs$ji*8qM3U}^DNc^!iRe2{H> zA0Gr?`T+b1z?}eYVmnJiGXh!wTnJz`fR0EF`wM`10KQeQ92s?20yqOetfBodfH?pj zj8xw12o=D`0el<4US%`}zzYB_0&q*9>{6B>=wW_)HqUh;w|^$eMLLfUQOA z%@e>m0KV6N8#K8Mz$dKt4g#=*T1}(aK<#RMk@~X)FayB+2Hv2_T>vKW^+?XgLu=43 zQLje>SfeeV#qs{}HR^ofL@u(TyZ@>toi7Xn&{IU?N*e*)09I7EsQDsB)5|4lU`4ot zc1JL+t!@8ji;1@Lwi@1KYed~XHuBKEs4*z*DQFge@wL3g`ZJF%m>|p%&<@}iHJV?W z4H=>P0JuG*-R1}o3v|z&x(kaV>hj?sr6eO@HvqTNq<@2AyhbOZ7s&{i24LSeIz9?h zLzIO?0H@aXewq$>kWcq*bQ^u=JGb2|TLSKB2!OZK5qJU}mb)3BkLT>tpI4Ar!ZQKfu#&0 z;BEj<12|SwK(4M@b3~<_z-U<(b7zMpn7|RDv31Ot_#nmg3jlm2?@KMr zecYg^pwWm$+ndpbQ<=}HO+X&x_&bEptRG^3k3x`t(3qz|-4*TYXjhK1&TJElG2gWEX`>I11~0<71?n6+3K(p1V5$m0ETW?(1mPvP5vtL2J%&!^S?U+m1dYeMg!LY~c-CQnlC6IfuqzSc-k zFg2f7_S?`RZpD9BDrSz; zlZaVbdb(yPSAlDjoJzLPJFMj&Q9wA6!*qsp4Xnm5wxDaXDi!qv`YY>U;A2R!Lf1eR-YK+d$;If z_|nAJj%a1fco+WGru2`<5&#%A@_j1G#1*ycDp=<^*6T+Yp6v@f-ayOfJOO~a<6ZM_ zRXp#gr)Wm^^c2Jnmg;K0|#4AbcLc44GgN-YIx{2-_^;& z9a8|H5Jr2XiffSck#{++yOI7IUkIin%$DvorQ^%?`4(q zh+tAwOGcXbVhY{0VA~8~O8OXfQr0mB!x)^pol1c(TNz8(WPiWxDadAFk`EJsG1R(T z^zzu4P`1$5b(&HBp&VVE%I!+yUDkHlQ;^Nnh*{X*$@zSe^33afm^Nr;$+h#JK7@9D zKP_yhtD|ZGfbQGe&1)VU$)ohMIGM-OXfWN;eTwTh(BRATRW(8YpeCbDW->XCPPHs9 z;ItRI&&%xe(Bm8y@XU$Td0(tz5q2S`pRdy%`D<_-^>jPW-{fhII??0b<5_M8-QZK$ z#)CbsSYT@F7A8N-WbBQU@M({ zYiLw1ryWu_Ev8Na0O#nXnnSSpFQ?Li=2`~P3D;j{oTE`%kc&+;dP4JUz8typ!9{dO z$w8*t2>^^GNMPs3p5MQS3t&^yNHsK%h0OgNoAJddDLWmTiaHAbbS3nsY(sj5&aDR; z)UIgMT>xMwZ#4GrK1WTZs-&z^X$AoZl6)mrvxGV&VkJpo%@2V803%V}4aPgu2LJ#7 M07*qoM6N<$f|B(9=l}o! diff --git a/src/android/app/src/main/res/drawable-xxxhdpi/ic_settings_core.png b/src/android/app/src/main/res/drawable-xxxhdpi/ic_settings_core.png deleted file mode 100644 index 2827a1777face21e079f802a8b76c4ce1bd21bb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2093 zcmV+|2-5e7P)pS zTm*-!>StB8v997H^a=wRx5g2WulLsj-vL7m#O*EN>h7m^22RJECLD_Mrp{x@k z0ogI$P}ZnOKz8hBC~Myxf&dZeBO=4x0$LP9E!Ptf*|nf;gG8iBM2@H&0U|O$L}mdi zTw5+84~xi{iV)Bn7@O0!Q6lmfupD>?Xcdt~B65_8W~y4Kx$wYR7aHiSJpeRhl(`$Q zr5l-f+SDAj-PnW7$%Xw_(D{b}8&ep+3^*l&-1Ci{ufXjY{(l(ox-Vi)2mp>q>HOMu zXE*Zp0Hy;k)&$!u+ZJFha3wIP8$G508#2m1t5gDjgMm#s9q|sZUyZCIftfjsF4F!0 z9sfV0~fgUK?AoZfm4AG3}l^=)t{MSL2g&m3hYcoS2$f& zUosEIiAaAJ9BYg|?hzwZ^&9JRz$4N5sqvWU2oRBd-2p2i!|7X9S6GdJ!O@?YG0<`f zTI%-E5$W-*83C%g4wxN%ni}&|b&d5dXb`YE`Zg8Ds%oq0E=W~>1a6ML?Gba+IzQ70 zs2K^1T__@Ad$f}z0BlP8gFN>E6QiHI#CchqpNjxhT@73meZ3u?Rn=#*{wQ}|kcd1D zOo@I6V3n%oN}(x2fRLmO9u~mhWxyJe`v`!(zyOkq{Q8o0`3;zmJN>cXjo2xa6Egmh zBo;Ld7~YLO`?yj0An-#;x?L0UN^*{8<621W4R*<)TQ6WLu+X!PuZFskJl>;G8-R<8 zUU=8&dM@y@M|~%UAYg$fV-^5=SvkK(-$vkNPda~6_MPxBPlhyGJH8X$XL{0oOqm4S z?ZJTAzMb!xgh%t}l4~0(BCCNxhFiIXs(O5xdq)wO1DtLs@^@A3>+KvMA_GbC^~V7Z z8!EmY7_6%8WgjL)WF@e-p~%bJ8n#MRH|8clL=FIsCdtts@AlV&@skaaDT~0EfF_bW zC2Raj@>+8_@ILT0@VTlk?(!h!PLsC*pHp5=&YNY;``xSu&y;BIH^5WC$=fW#uD7>Q zep1wt6(C?FVG_dbR#gP(@8T+D$5582>OobAq=`riwl8whVrm_5Z2;#(l80Fp%nbCu zGwOf|f&P4Mg$Q_s?Mok&fxYV7;u|7<-c}XwtLg@~K$eY$P>=MMR`60~ae)8aTt}FM zt0fgAAV@ulP$S@n3KFnxMPTKY zmW@#&(yL+w91v<78Y1I)*FZh4Q@HT^fB=8JqK+hoCIrr}Q13q{(Er}HH6(iv<%eT` z1A2uHM?3p}4q*RH+g!?%WSzZ1{ul6h+Vl!Ie_Dz5{sqhjCUto!?_R<*kz7=ab32xO zNNPD)V*@ZiRW}Em|J$5>*4P59BDv^#5BLapOI5dx0;KFEeMriF9SbxY_lj8od;cBA zw#;#B+tDQTj_Q4-rC1-^OTU;GkqN-t#$tC;)xXOKMYI?z{(^{%F46fz-CS&mFS0tn z2m#jDiS@uD5gAc}^Y;c8xwD8RX5|Y3=9@bOKlOdq1LHl;`D1}!Jm}pH^v`m=#;?u^jlgh{ zLV{!5TtBcRy;^}LRsFNh?{J}U%8t+Z6V2RTv0h!mlcegQb5VY zZi@zRBL1sxbzAKUIMwMg0{FOqtQ*QM*mPD7B_E=bTp5x_()W?nhV$+8H6))-InJ<@ z&Gn&f#HOZHa2;@N%A2gS@pF^4h@^B`W6BY}%4g3vA;2j+xd#PbA!7+LV6+DUCT7&3 zAMivEGOr8zA-5W3j{&~wpxjcDQoecQ^~F9(_~4wnGy%^8|8!W#PP6cx=@+#{WD>9^ z(59*j3(EQuyQfTRfjzZ=u`Zi#DDbDMern~j2Fnews=jI{uFa5ul;wDr<)!pw&>;ibz0qyk#h>B@&PwOMst@WW65=$iC?P13YaY>WefVREq?3j{6K` z+*@S>D@}l^eoc~?n->dIwYkbRnjXZwDJgOSFoWdlO^r1t(vGNiC+L+VA5*)f>bCt0 XBkJ97^-|{S00000NkvXXu0mjf-5}Lk diff --git a/src/android/app/src/main/res/drawable/gamelist_divider.xml b/src/android/app/src/main/res/drawable/gamelist_divider.xml index 7da9dccce..a1cec8e41 100644 --- a/src/android/app/src/main/res/drawable/gamelist_divider.xml +++ b/src/android/app/src/main/res/drawable/gamelist_divider.xml @@ -6,6 +6,6 @@ android:width="1dp" android:height="1dp" /> - + diff --git a/src/android/app/src/main/res/drawable/ic_add.xml b/src/android/app/src/main/res/drawable/ic_add.xml index bdd99f48d..64360e8d9 100644 --- a/src/android/app/src/main/res/drawable/ic_add.xml +++ b/src/android/app/src/main/res/drawable/ic_add.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/src/android/app/src/main/res/drawable/ic_back.xml b/src/android/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 000000000..f99fea719 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_folder.xml b/src/android/app/src/main/res/drawable/ic_folder.xml new file mode 100644 index 000000000..17d6c7927 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_folder.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_heart.xml b/src/android/app/src/main/res/drawable/ic_heart.xml new file mode 100644 index 000000000..6845a40e7 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_heart.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_install.xml b/src/android/app/src/main/res/drawable/ic_install.xml new file mode 100644 index 000000000..fe58acf84 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_install.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_settings.xml b/src/android/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 000000000..12ce6e231 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/android/app/src/main/res/drawable/popup_background.xml b/src/android/app/src/main/res/drawable/popup_background.xml new file mode 100644 index 000000000..057f81c83 --- /dev/null +++ b/src/android/app/src/main/res/drawable/popup_background.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml b/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml index 9bcf883e1..ec9942cc5 100644 --- a/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml +++ b/src/android/app/src/main/res/layout-ldrtl/list_item_cheat.xml @@ -13,7 +13,6 @@ android:id="@+id/text_name" android:layout_width="0dp" android:layout_height="wrap_content" - android:textColor="@color/header_text" android:textSize="16sp" android:layout_margin="@dimen/spacing_large" style="@style/TextAppearance.AppCompat.Headline" diff --git a/src/android/app/src/main/res/layout/activity_cheats.xml b/src/android/app/src/main/res/layout/activity_cheats.xml index b9414ab6d..0b6204b6d 100644 --- a/src/android/app/src/main/res/layout/activity_cheats.xml +++ b/src/android/app/src/main/res/layout/activity_cheats.xml @@ -1,22 +1,58 @@ - - + - + - + + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/activity_main.xml b/src/android/app/src/main/res/layout/activity_main.xml index cea0922a7..1f737c321 100644 --- a/src/android/app/src/main/res/layout/activity_main.xml +++ b/src/android/app/src/main/res/layout/activity_main.xml @@ -1,27 +1,30 @@ - + + + + + + - - - - - - diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml index 11b91c45f..9da84faea 100644 --- a/src/android/app/src/main/res/layout/activity_settings.xml +++ b/src/android/app/src/main/res/layout/activity_settings.xml @@ -1,5 +1,27 @@ - - + android:layout_height="match_parent"> + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/card_game.xml b/src/android/app/src/main/res/layout/card_game.xml index 6e87490f9..84ca54abb 100644 --- a/src/android/app/src/main/res/layout/card_game.xml +++ b/src/android/app/src/main/res/layout/card_game.xml @@ -36,15 +36,13 @@ android:layout_marginStart="8dp" android:baselineAligned="false" android:ellipsize="end" - android:gravity="center_vertical" android:lines="1" android:maxLines="1" android:textAlignment="viewStart" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/image_game_screen" app:layout_constraintTop_toTopOf="parent" - tools:text="The Legend of Zelda\nOcarina of Time 3D" - android:textColor="@color/header_text" /> + tools:text="The Legend of Zelda\nOcarina of Time 3D" /> + tools:text="Nintendo" /> + tools:text="Pilotwings_Resort.cxi" /> - diff --git a/src/android/app/src/main/res/layout/dialog_seekbar.xml b/src/android/app/src/main/res/layout/dialog_slider.xml similarity index 93% rename from src/android/app/src/main/res/layout/dialog_seekbar.xml rename to src/android/app/src/main/res/layout/dialog_slider.xml index 35abecfcb..59719e013 100644 --- a/src/android/app/src/main/res/layout/dialog_seekbar.xml +++ b/src/android/app/src/main/res/layout/dialog_slider.xml @@ -5,8 +5,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - \ No newline at end of file + diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index d6e47e1e4..bd64d5d16 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -40,7 +40,6 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:padding="@dimen/spacing_small" - android:background="@color/citra_orange" android:text="@string/emulation_done" android:visibility="gone" /> diff --git a/src/android/app/src/main/res/layout/fragment_grid.xml b/src/android/app/src/main/res/layout/fragment_grid.xml index f5b6c2e19..5978bf998 100644 --- a/src/android/app/src/main/res/layout/fragment_grid.xml +++ b/src/android/app/src/main/res/layout/fragment_grid.xml @@ -17,17 +17,19 @@ android:id="@+id/gamelist_empty_text" android:layout_width="match_parent" android:layout_height="match_parent" + android:gravity="center" android:text="@string/empty_gamelist" - android:visibility="gone" android:textSize="18sp" - android:gravity="center" /> + android:visibility="gone" /> + - \ No newline at end of file + + diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml index 4c5d597c1..f8724552c 100644 --- a/src/android/app/src/main/res/layout/fragment_settings.xml +++ b/src/android/app/src/main/res/layout/fragment_settings.xml @@ -1,12 +1,13 @@ - + android:layout_height="match_parent" + android:background="?attr/colorSurface"> + android:layout_height="match_parent" /> - \ No newline at end of file + diff --git a/src/android/app/src/main/res/layout/list_item_cheat.xml b/src/android/app/src/main/res/layout/list_item_cheat.xml index c0b5f982f..d31ae63f9 100644 --- a/src/android/app/src/main/res/layout/list_item_cheat.xml +++ b/src/android/app/src/main/res/layout/list_item_cheat.xml @@ -13,7 +13,6 @@ android:id="@+id/text_name" android:layout_width="0dp" android:layout_height="wrap_content" - android:textColor="@color/header_text" android:textSize="16sp" android:layout_margin="@dimen/spacing_large" style="@style/TextAppearance.AppCompat.Headline" diff --git a/src/android/app/src/main/res/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml index df83684f7..a2a07b381 100644 --- a/src/android/app/src/main/res/layout/list_item_setting.xml +++ b/src/android/app/src/main/res/layout/list_item_setting.xml @@ -21,7 +21,6 @@ android:layout_alignParentEnd="true" android:layout_marginStart="@dimen/spacing_large" android:layout_marginEnd="@dimen/spacing_large" - android:textColor="@color/header_text" android:textSize="16sp" tools:text="Setting Name" /> @@ -37,7 +36,6 @@ android:layout_marginTop="@dimen/spacing_small" android:layout_marginEnd="@dimen/spacing_large" android:visibility="visible" - tools:text="@string/app_disclaimer" - android:textColor="@color/header_subtext" /> + tools:text="@string/app_disclaimer" /> \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml b/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml index 86ba83f11..332b2e08f 100644 --- a/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml +++ b/src/android/app/src/main/res/layout/list_item_setting_checkbox.xml @@ -19,7 +19,6 @@ android:layout_marginStart="@dimen/spacing_large" android:layout_marginTop="@dimen/spacing_large" android:layout_toStartOf="@+id/checkbox" - android:textColor="@color/header_text" android:textSize="16sp" tools:text="@string/frame_limit_enable" /> @@ -36,7 +35,6 @@ android:layout_marginTop="@dimen/spacing_small" android:layout_toStartOf="@+id/checkbox" android:textAlignment="textStart" - android:textColor="@color/header_subtext" tools:text="@string/frame_limit_enable_description" /> - \ No newline at end of file + diff --git a/src/android/app/src/main/res/layout/premium_item_setting.xml b/src/android/app/src/main/res/layout/premium_item_setting.xml index 17d5a13b2..1a606a458 100644 --- a/src/android/app/src/main/res/layout/premium_item_setting.xml +++ b/src/android/app/src/main/res/layout/premium_item_setting.xml @@ -21,7 +21,7 @@ android:layout_alignParentEnd="true" android:layout_marginStart="@dimen/spacing_large" android:layout_marginEnd="@dimen/spacing_large" - android:textColor="?android:colorAccent" + android:textColor="?attr/colorPrimary" android:textStyle="bold" tools:text="Setting Name" /> @@ -37,7 +37,6 @@ android:layout_marginTop="@dimen/spacing_small" android:layout_marginEnd="@dimen/spacing_large" android:visibility="visible" - tools:text="@string/app_disclaimer" - android:textColor="@color/header_subtext" /> + tools:text="@string/app_disclaimer" /> \ No newline at end of file diff --git a/src/android/app/src/main/res/menu/menu_game_grid.xml b/src/android/app/src/main/res/menu/menu_game_grid.xml index 9cdcc7f08..1a4bee6df 100644 --- a/src/android/app/src/main/res/menu/menu_game_grid.xml +++ b/src/android/app/src/main/res/menu/menu_game_grid.xml @@ -4,7 +4,7 @@ diff --git a/src/android/app/src/main/res/values-night/citra_colors.xml b/src/android/app/src/main/res/values-night/citra_colors.xml new file mode 100644 index 000000000..54e6a913a --- /dev/null +++ b/src/android/app/src/main/res/values-night/citra_colors.xml @@ -0,0 +1,33 @@ + + + + #F8BE00 + #3F2E00 + #451C00 + #FFDF9A + #D7C4A0 + #3A2F15 + #52452A + #F4E0BB + #FFB2BC + #5F1126 + #7D293B + #FFD9DD + #FFB4AB + #93000A + #690005 + #FFDAD6 + #1E1B16 + #E9E1D9 + #1E1B16 + #E9E1D9 + #4D4639 + #D0C5B4 + #999080 + #1E1B16 + #E9E1D9 + #785A00 + #F8BE00 + #4D4639 + + diff --git a/src/android/app/src/main/res/values-night/colors.xml b/src/android/app/src/main/res/values-night/colors.xml deleted file mode 100644 index 43b948021..000000000 --- a/src/android/app/src/main/res/values-night/colors.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - #272727 - #121212 - #FEC303 - - #121212 - #3D3D3D - - #404040 - - #E0E0E0 - #A0A0A0 - - @color/citra_accent - diff --git a/src/android/app/src/main/res/values/citra_colors.xml b/src/android/app/src/main/res/values/citra_colors.xml new file mode 100644 index 000000000..f0cfd3780 --- /dev/null +++ b/src/android/app/src/main/res/values/citra_colors.xml @@ -0,0 +1,35 @@ + + + + #FFAB03 + #FFFFFF + #FFDF9A + #251A00 + #6B5D3F + #FFFFFF + #F4E0BB + #241A04 + #9B4052 + #FFFFFF + #FFD9DD + #400013 + #BA1A1A + #FFDAD6 + #FFFFFF + #410002 + #FFFBFF + #1E1B16 + #FFFBFF + #1E1B16 + #EDE1CF + #4D4639 + #7F7667 + #F7F0E7 + #33302A + #F8BE00 + #000000 + #783E00 + #D0C5B4 + #000000 + + diff --git a/src/android/app/src/main/res/values/colors.xml b/src/android/app/src/main/res/values/colors.xml deleted file mode 100644 index 6668288a7..000000000 --- a/src/android/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - #FFC303 - #FF8D03 - #CC7102 - - #ffffff - #D5D5D5 - - #ffffff - - #1C1424 - #5C5661 - - @color/header_text - diff --git a/src/android/app/src/main/res/values/styles.xml b/src/android/app/src/main/res/values/styles.xml index 47fe6f6ea..b1d36498e 100644 --- a/src/android/app/src/main/res/values/styles.xml +++ b/src/android/app/src/main/res/values/styles.xml @@ -1,65 +1,37 @@ - - - - - - - - - - - - + + + + + + + + + + diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..edff173fe --- /dev/null +++ b/src/android/app/src/main/res/values/themes.xml @@ -0,0 +1,55 @@ + + + + + + + + + From c96f54f022eec799be26f77ab335d0e1b70692bc Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:44:26 -0800 Subject: [PATCH 6/7] Implement app management support (suspend, resume, close, etc) (#6322) --- src/audio_core/hle/hle.cpp | 3 +- .../configuration/configure_input.cpp | 5 - src/citra_qt/main.cpp | 25 + src/citra_qt/main.h | 2 + src/citra_qt/main.ui | 6 + src/common/settings.cpp | 5 + src/core/hle/applets/applet.cpp | 2 +- src/core/hle/kernel/kernel.h | 6 + src/core/hle/kernel/process.cpp | 9 +- src/core/hle/kernel/svc.cpp | 3 + src/core/hle/service/am/am.cpp | 1 + src/core/hle/service/apt/applet_manager.cpp | 480 +++++++++++++++++- src/core/hle/service/apt/applet_manager.h | 127 ++++- src/core/hle/service/apt/apt.cpp | 204 ++++++-- src/core/hle/service/apt/apt.h | 137 ++++- src/core/hle/service/apt/apt_a.cpp | 14 +- src/core/hle/service/apt/apt_s.cpp | 14 +- src/core/hle/service/apt/apt_u.cpp | 14 +- src/core/hle/service/apt/ns_s.cpp | 6 +- src/core/hle/service/gsp/gsp_gpu.cpp | 52 +- src/core/hle/service/gsp/gsp_gpu.h | 38 ++ 21 files changed, 1043 insertions(+), 110 deletions(-) diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index cba17c5ca..652e06bf3 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -272,6 +272,7 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector& buffer) case StateChange::Sleep: LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware"); UNIMPLEMENTED(); + AudioPipeWriteStructAddresses(); dsp_state = DspState::Sleeping; break; default: @@ -438,7 +439,7 @@ bool DspHle::Impl::Tick() { parent.OutputFrame(std::move(current_frame)); - return true; + return GetDspState() == DspState::On; } void DspHle::Impl::AudioTickCallback(s64 cycles_late) { diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index 62be40208..b13013c1a 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -423,11 +423,6 @@ void ConfigureInput::OnHotkeysChanged(QList new_key_list) { QList ConfigureInput::GetUsedKeyboardKeys() { QList list; for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { - // TODO(adityaruplaha): Add home button to list when we finally emulate it - if (button == Settings::NativeButton::Home) { - continue; - } - const auto& button_param = buttons_param[button]; if (button_param.Get("engine", "") == "keyboard") { list << QKeySequence(button_param.Get("code", 0)); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index e40883384..2905130e0 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -85,6 +85,7 @@ #include "core/frontend/applets/default_applets.h" #include "core/frontend/scope_acquire_context.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/nfc/nfc.h" #include "core/loader/loader.h" @@ -318,6 +319,8 @@ void GMainWindow::InitializeWidgets() { updater = new Updater(this); UISettings::values.updater_found = updater->HasUpdater(); + UpdateBootHomeMenuState(); + // Create status bar message_label = new QLabel(); // Configured separately for left alignment @@ -741,6 +744,7 @@ void GMainWindow::ConnectMenuEvents() { // File connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile); connect_menu(ui->action_Install_CIA, &GMainWindow::OnMenuInstallCIA); + connect_menu(ui->action_Boot_Home_Menu, &GMainWindow::OnMenuBootHomeMenu); connect_menu(ui->action_Exit, &QMainWindow::close); connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo); connect_menu(ui->action_Remove_Amiibo, &GMainWindow::OnRemoveAmiibo); @@ -1601,6 +1605,20 @@ void GMainWindow::OnMenuInstallCIA() { InstallCIA(filepaths); } +static std::string GetHomeMenuPath() { + static const std::array home_menu_tids = { + 0x0004003000008202, 0x0004003000008F02, 0x0004003000009802, 0x0004003000009802, + 0x000400300000A102, 0x000400300000A902, 0x000400300000B102}; + + Service::CFG::Module cfg{}; + return Service::AM::GetTitleContentPath(Service::FS::MediaType::NAND, + home_menu_tids[cfg.GetRegionValue()]); +} + +void GMainWindow::OnMenuBootHomeMenu() { + BootGame(QString::fromStdString(GetHomeMenuPath())); +} + void GMainWindow::InstallCIA(QStringList filepaths) { ui->action_Install_CIA->setEnabled(false); game_list->SetDirectoryWatcherEnabled(false); @@ -1951,6 +1969,7 @@ void GMainWindow::OnConfigure() { setMouseTracking(false); } UpdateSecondaryWindowVisibility(); + UpdateBootHomeMenuState(); } else { Settings::values.input_profiles = old_input_profiles; Settings::values.touch_from_button_maps = old_touch_from_button_maps; @@ -2244,6 +2263,12 @@ void GMainWindow::UpdateStatusBar() { emu_frametime_label->setVisible(true); } +void GMainWindow::UpdateBootHomeMenuState() { + const std::string home_menu_path = GetHomeMenuPath(); + ui->action_Boot_Home_Menu->setEnabled(!home_menu_path.empty() && + FileUtil::Exists(GetHomeMenuPath())); +} + void GMainWindow::HideMouseCursor() { if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) { mouse_hide_timer.stop(); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 6a230fd0b..85dd95587 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -192,6 +192,7 @@ private slots: void OnConfigurePerGame(); void OnMenuLoadFile(); void OnMenuInstallCIA(); + void OnMenuBootHomeMenu(); void OnUpdateProgress(std::size_t written, std::size_t total); void OnCIAInstallReport(Service::AM::InstallStatus status, QString filepath); void OnCIAInstallFinished(); @@ -238,6 +239,7 @@ private slots: private: Q_INVOKABLE void OnMoviePlaybackCompleted(); void UpdateStatusBar(); + void UpdateBootHomeMenuState(); void LoadTranslation(); void UpdateWindowTitle(); void UpdateUISettings(); diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 4c88252eb..38fff8b71 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -66,6 +66,7 @@ + @@ -209,6 +210,11 @@ Install CIA... + + + Boot Home Menu + + E&xit diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 8e3143dbe..e2934b3f3 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -56,6 +56,11 @@ void Apply() { hid->ReloadInputDevices(); } + auto apt = Service::APT::GetModule(system); + if (apt) { + apt->GetAppletManager()->ReloadInputDevices(); + } + auto sm = system.ServiceManager(); auto ir_user = sm.GetService("ir:USER"); if (ir_user) diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index ab96cc2e2..8be1a0e7e 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -70,7 +70,7 @@ ResultCode Applet::Create(Service::APT::AppletId id, Service::APT::AppletId pare } Service::APT::AppletAttributes attributes; - attributes.applet_pos.Assign(static_cast(Service::APT::AppletPos::AutoLibrary)); + attributes.applet_pos.Assign(Service::APT::AppletPos::AutoLibrary); attributes.is_home_menu.Assign(false); const auto lock_handle_data = manager->GetLockHandle(attributes); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index ca47d591f..b807bc261 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -133,6 +133,12 @@ public: std::shared_ptr CreateProcess(std::shared_ptr code_set); + /** + * Removes a process from the kernel process list + * @param process Process to remove + */ + void RemoveProcess(std::shared_ptr process); + /** * Creates and returns a new thread. The new thread is immediately scheduled * @param name The friendly name desired for the thread diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 6e8e25bea..89b3636c5 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -79,6 +79,10 @@ std::shared_ptr KernelSystem::CreateProcess(std::shared_ptr co return process; } +void KernelSystem::RemoveProcess(std::shared_ptr process) { + std::erase(process_list, process); +} + void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { for (std::size_t i = 0; i < len; ++i) { u32 descriptor = kernel_caps[i]; @@ -208,9 +212,6 @@ void Process::Exit() { if (plgldr) { plgldr->OnProcessExit(*this, kernel); } - - // Clear the process's open handles. - handle_table.Clear(); } VAddr Process::GetLinearHeapAreaAddress() const { @@ -474,6 +475,8 @@ Kernel::Process::Process(KernelSystem& kernel) kernel.memory.RegisterPageTable(vm_manager.page_table); } Kernel::Process::~Process() { + LOG_INFO(Kernel, "Cleaning up process {}", process_id); + // Release all objects this process owns first so that their potential destructor can do clean // up with this process before further destruction. // TODO(wwylele): explicitly destroy or invalidate objects this process owns (threads, shared diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 6af90edbd..666fdecd1 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -565,6 +565,9 @@ void SVC::ExitProcess() { // Kill the current thread kernel.GetCurrentThreadManager().GetCurrentThread()->Stop(); + // Remove kernel reference to process so it can be cleaned up. + kernel.RemoveProcess(current_process); + system.PrepareReschedule(); } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index af4532b75..dd00b71c4 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1140,6 +1140,7 @@ void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) { // TODO(shinyquagsire23): Read tickets for this instead? bool has_rights = + FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::NAND, tid, content_index)) || FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index)); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 0bcbd2ecf..b0755bfdb 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -4,17 +4,23 @@ #include "common/settings.h" #include "core/core.h" +#include "core/frontend/input.h" #include "core/hle/applets/applet.h" #include "core/hle/service/am/am.h" #include "core/hle/service/apt/applet_manager.h" #include "core/hle/service/apt/errors.h" #include "core/hle/service/apt/ns.h" #include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/gsp/gsp_gpu.h" +#include "video_core/utils.h" SERVICE_CONSTRUCT_IMPL(Service::APT::AppletManager) namespace Service::APT { +/// The interval at which the home button update callback will be called, 16.6ms +static constexpr u64 home_button_update_interval_us = 16666; + struct AppletTitleData { // There are two possible applet ids for each applet. std::array applet_ids; @@ -108,6 +114,14 @@ static u64 GetTitleIdForApplet(AppletId id, u32 region_value) { return itr->title_ids[region_value]; } +static bool IsSystemAppletId(AppletId applet_id) { + return (static_cast(applet_id) & static_cast(AppletId::AnySystemApplet)) != 0; +} + +static bool IsApplicationAppletId(AppletId applet_id) { + return (static_cast(applet_id) & static_cast(AppletId::Application)) != 0; +} + AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) { if (id == AppletId::Application) { if (GetAppletSlot(AppletSlot::Application)->applet_id != AppletId::None) @@ -133,7 +147,7 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) { if (slot_data->applet_id == AppletId::None) return AppletSlot::Error; - auto applet_pos = static_cast(slot_data->attributes.applet_pos.Value()); + auto applet_pos = slot_data->attributes.applet_pos.Value(); if ((id == AppletId::AnyLibraryApplet && applet_pos == AppletPos::Library) || (id == AppletId::AnySysLibraryApplet && applet_pos == AppletPos::SysLibrary)) return AppletSlot::LibraryApplet; @@ -163,11 +177,11 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromAttributes(AppletAttri AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet, AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet}; - auto applet_pos = attributes.applet_pos; - if (applet_pos >= applet_position_slots.size()) + auto applet_pos_value = static_cast(attributes.applet_pos.Value()); + if (applet_pos_value >= applet_position_slots.size()) return AppletSlot::Error; - auto slot = applet_position_slots[applet_pos]; + auto slot = applet_position_slots[applet_pos_value]; if (slot == AppletSlot::Error) return AppletSlot::Error; @@ -212,13 +226,35 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) { // Otherwise, send the parameter the LLE way. next_parameter = parameter; + if (parameter.signal == SignalType::RequestForSysApplet) { + // APT handles RequestForSysApplet messages itself. + LOG_DEBUG(Service_APT, "Replying to RequestForSysApplet from {:03X}", + parameter.sender_id); + + if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) { + SendCaptureBufferInfo(parameter.buffer); + CaptureFrameBuffers(); + } + + next_parameter->sender_id = parameter.destination_id; + next_parameter->destination_id = parameter.sender_id; + next_parameter->signal = SignalType::Response; + next_parameter->buffer.clear(); + next_parameter->object = nullptr; + } else if (IsSystemAppletId(parameter.sender_id) && + IsApplicationAppletId(parameter.destination_id) && parameter.object) { + // When a message is sent from a system applet to an application, APT + // replaces its object with the zero handle. + next_parameter->object = nullptr; + } + // Signal the event to let the receiver know that a new parameter is ready to be read - auto slot = GetAppletSlotFromId(parameter.destination_id); + auto slot = GetAppletSlotFromId(next_parameter->destination_id); if (slot != AppletSlot::Error) { GetAppletSlot(slot)->parameter_event->Signal(); } else { LOG_DEBUG(Service_APT, "No applet was registered with ID {:03X}", - parameter.destination_id); + next_parameter->destination_id); } } } @@ -261,6 +297,10 @@ ResultVal AppletManager::GlanceParameter(AppletId app_id) { ResultVal AppletManager::ReceiveParameter(AppletId app_id) { auto result = GlanceParameter(app_id); if (result.Succeeded()) { + LOG_DEBUG(Service_APT, + "Received parameter from {:03X} to {:03X} with signal {:08X} and size {:08X}", + result->sender_id, result->destination_id, result->signal, result->buffer.size()); + // Clear the parameter next_parameter = {}; } @@ -282,13 +322,13 @@ bool AppletManager::CancelParameter(bool check_sender, AppletId sender_appid, bo ResultVal AppletManager::GetLockHandle( AppletAttributes attributes) { auto corrected_attributes = attributes; - if (attributes.applet_pos == static_cast(AppletPos::Library) || - attributes.applet_pos == static_cast(AppletPos::SysLibrary) || - attributes.applet_pos == static_cast(AppletPos::AutoLibrary)) { + if (attributes.applet_pos == AppletPos::Library || + attributes.applet_pos == AppletPos::SysLibrary || + attributes.applet_pos == AppletPos::AutoLibrary) { auto corrected_pos = last_library_launcher_slot == AppletSlot::Application ? AppletPos::Library : AppletPos::SysLibrary; - corrected_attributes.applet_pos.Assign(static_cast(corrected_pos)); + corrected_attributes.applet_pos.Assign(corrected_pos); LOG_DEBUG(Service_APT, "Corrected applet attributes from {:08X} to {:08X}", attributes.raw, corrected_attributes.raw); } @@ -350,6 +390,13 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) { auto slot_data = GetAppletSlot(slot); slot_data->registered = true; + if (slot_data->attributes.applet_pos == AppletPos::System && + slot_data->attributes.is_home_menu) { + slot_data->attributes.raw |= attributes.raw; + LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.", + slot_data->attributes.raw); + } + // Send any outstanding parameters to the newly-registered application if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) { // TODO: Real APT would loop trying to send the parameter until it succeeds, @@ -366,6 +413,35 @@ bool AppletManager::IsRegistered(AppletId app_id) { return slot != AppletSlot::Error && GetAppletSlot(slot)->registered; } +ResultVal AppletManager::InquireNotification(AppletId app_id) { + auto slot = GetAppletSlotFromId(app_id); + if (slot != AppletSlot::Error) { + auto slot_data = GetAppletSlot(slot); + if (slot_data->registered) { + auto notification = slot_data->notification; + slot_data->notification = Notification::None; + return MakeResult(notification); + } + } + + return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status); +} + +ResultCode AppletManager::SendNotification(Notification notification) { + if (active_slot != AppletSlot::Error) { + const auto slot_data = GetAppletSlot(active_slot); + if (slot_data->registered) { + slot_data->notification = notification; + slot_data->notification_event->Signal(); + return RESULT_SUCCESS; + } + } + + return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status}; +} + ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { // The real APT service returns an error if there's a pending APT parameter when this function // is called. @@ -589,18 +665,281 @@ ResultCode AppletManager::CloseSystemApplet(std::shared_ptr obje "Attempting to close a system applet from a non-system applet."); auto slot = GetAppletSlot(active_slot); + auto closed_applet_id = slot->applet_id; active_slot = last_system_launcher_slot; - - // TODO: Send a parameter to the application only if the application ordered the applet to - // close. - - // TODO: Terminate the running applet title slot->Reset(); + if (ordered_to_close_sys_applet) { + ordered_to_close_sys_applet = false; + + active_slot = AppletSlot::Application; + CancelAndSendParameter({ + .sender_id = closed_applet_id, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByExit, + .object = std::move(object), + .buffer = buffer, + }); + } + + // TODO: Terminate the running applet title return RESULT_SUCCESS; } +ResultCode AppletManager::OrderToCloseSystemApplet() { + if (active_slot == AppletSlot::Error) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id == AppletId::None || + active_slot_data->attributes.applet_pos != AppletPos::Application) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto system_slot = GetAppletSlotFromPos(AppletPos::System); + if (system_slot == AppletSlot::Error) { + return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status}; + } + + auto system_slot_data = GetAppletSlot(system_slot); + if (!system_slot_data->registered) { + return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status}; + } + + ordered_to_close_sys_applet = true; + active_slot = system_slot; + + SendParameter({ + .sender_id = AppletId::Application, + .destination_id = system_slot_data->applet_id, + .signal = SignalType::WakeupByCancel, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::PrepareToJumpToHomeMenu() { + if (next_parameter) { + return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + last_jump_to_home_slot = active_slot; + if (last_jump_to_home_slot == AppletSlot::Application) { + EnsureHomeMenuLoaded(); + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::JumpToHomeMenu(std::shared_ptr object, + const std::vector& buffer) { + if (last_jump_to_home_slot != AppletSlot::Error) { + auto slot_data = GetAppletSlot(last_jump_to_home_slot); + if (slot_data->applet_id != AppletId::None) { + MessageParameter param; + param.object = std::move(object); + param.buffer = buffer; + + switch (slot_data->attributes.applet_pos) { + case AppletPos::Application: + active_slot = AppletSlot::HomeMenu; + + param.destination_id = AppletId::HomeMenu; + param.sender_id = AppletId::Application; + param.signal = SignalType::WakeupByPause; + SendParameter(param); + break; + case AppletPos::Library: + param.destination_id = slot_data->applet_id; + param.sender_id = slot_data->applet_id; + param.signal = SignalType::WakeupByCancel; + SendParameter(param); + break; + case AppletPos::System: + if (slot_data->attributes.is_home_menu) { + param.destination_id = slot_data->applet_id; + param.sender_id = slot_data->applet_id; + param.signal = SignalType::WakeupToJumpHome; + SendParameter(param); + } + break; + case AppletPos::SysLibrary: { + const auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet); + param.destination_id = slot_data->applet_id; + param.sender_id = slot_data->applet_id; + param.signal = system_slot_data->registered ? SignalType::WakeupByCancel + : SignalType::WakeupToJumpHome; + SendParameter(param); + break; + } + default: + break; + } + } + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::PrepareToLeaveHomeMenu() { + if (!GetAppletSlot(AppletSlot::Application)->registered) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + if (next_parameter) { + return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::LeaveHomeMenu(std::shared_ptr object, + const std::vector& buffer) { + active_slot = AppletSlot::Application; + + SendParameter({ + .sender_id = AppletId::HomeMenu, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByPause, + .object = std::move(object), + .buffer = buffer, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::OrderToCloseApplication() { + if (active_slot == AppletSlot::Error) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id == AppletId::None || + active_slot_data->attributes.applet_pos != AppletPos::System) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + ordered_to_close_application = true; + active_slot = AppletSlot::Application; + + SendParameter({ + .sender_id = AppletId::HomeMenu, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByCancel, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::PrepareToCloseApplication(bool return_to_sys) { + if (active_slot == AppletSlot::Error) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id == AppletId::None || + active_slot_data->attributes.applet_pos != AppletPos::Application) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet); + auto home_menu_slot_data = GetAppletSlot(AppletSlot::HomeMenu); + + if (!application_cancelled && return_to_sys) { + // TODO: Left side of the OR also includes "&& !power_button_clicked", but this isn't + // implemented yet. + if (!ordered_to_close_application || !system_slot_data->registered) { + application_close_target = AppletSlot::HomeMenu; + } else { + application_close_target = AppletSlot::SystemApplet; + } + } else { + application_close_target = AppletSlot::Error; + } + + if (application_close_target != AppletSlot::HomeMenu && !system_slot_data->registered && + !home_menu_slot_data->registered) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + if (next_parameter) { + return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + if (application_close_target == AppletSlot::HomeMenu) { + EnsureHomeMenuLoaded(); + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::CloseApplication(std::shared_ptr object, + const std::vector& buffer) { + ordered_to_close_application = false; + application_cancelled = false; + + GetAppletSlot(AppletSlot::Application)->Reset(); + + if (application_close_target != AppletSlot::Error) { + active_slot = application_close_target; + + CancelAndSendParameter({ + .sender_id = AppletId::Application, + .destination_id = GetAppletSlot(application_close_target)->applet_id, + .signal = SignalType::WakeupByExit, + .object = std::move(object), + .buffer = buffer, + }); + } + + // TODO: Terminate the application process. + return RESULT_SUCCESS; +} + +ResultVal AppletManager::GetAppletManInfo( + AppletPos requested_applet_pos) { + auto active_applet_pos = AppletPos::Invalid; + auto active_applet_id = AppletId::None; + if (active_slot != AppletSlot::Error) { + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id != AppletId::None) { + active_applet_pos = active_slot_data->attributes.applet_pos; + active_applet_id = active_slot_data->applet_id; + } + } + + auto requested_applet_id = AppletId::None; + auto requested_slot = GetAppletSlotFromPos(requested_applet_pos); + if (requested_slot != AppletSlot::Error) { + auto requested_slot_data = GetAppletSlot(requested_slot); + if (requested_slot_data->registered) { + requested_applet_id = requested_slot_data->applet_id; + } + } + + return MakeResult({ + .active_applet_pos = active_applet_pos, + .requested_applet_id = requested_applet_id, + .home_menu_applet_id = AppletId::HomeMenu, + .active_applet_id = active_applet_id, + }); +} + ResultVal AppletManager::GetAppletInfo(AppletId app_id) { auto slot = GetAppletSlotFromId(app_id); if (slot == AppletSlot::Error) { @@ -709,7 +1048,7 @@ ResultCode AppletManager::DoApplicationJump(const DeliverArg& arg) { ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) { if (active_slot == AppletSlot::Error || - GetAppletSlot(active_slot)->attributes.applet_pos != static_cast(AppletPos::System)) { + GetAppletSlot(active_slot)->attributes.applet_pos != AppletPos::System) { return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, ErrorLevel::Status}; } @@ -771,9 +1110,30 @@ ResultCode AppletManager::WakeupApplication() { // Send a Wakeup signal via the apt parameter to the application once it registers itself. // The real APT service does this by spin waiting on another thread until the application is // registered. - SendApplicationParameterAfterRegistration({.sender_id = AppletId::HomeMenu, - .destination_id = AppletId::Application, - .signal = SignalType::Wakeup}); + SendApplicationParameterAfterRegistration({ + .sender_id = AppletId::HomeMenu, + .destination_id = AppletId::Application, + .signal = SignalType::Wakeup, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::CancelApplication() { + auto application_slot_data = GetAppletSlot(AppletSlot::Application); + if (application_slot_data->applet_id == AppletId::None) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + application_cancelled = true; + + SendApplicationParameterAfterRegistration({ + .sender_id = active_slot != AppletSlot::Error ? GetAppletSlot(active_slot)->applet_id + : AppletId::None, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByCancel, + }); return RESULT_SUCCESS; } @@ -813,6 +1173,78 @@ void AppletManager::EnsureHomeMenuLoaded() { } } +static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height, + u32 format) { + static constexpr auto screen_capture_base_vaddr = static_cast(0x1F500000); + static constexpr auto screen_width = 240; + static constexpr auto screen_width_pow2 = 256; + const auto bpp = format < 2 ? 3 : 2; + + Memory::RasterizerFlushVirtualRegion(src, screen_width * height * bpp, + Memory::FlushMode::Flush); + + auto dst_vaddr = screen_capture_base_vaddr + capture_offset; + auto dst_ptr = system.Memory().GetPointer(dst_vaddr); + const auto src_ptr = system.Memory().GetPointer(src); + for (auto y = 0; y < height; y++) { + for (auto x = 0; x < screen_width; x++) { + auto dst_offset = + VideoCore::GetMortonOffset(x, y, bpp) + (y & ~7) * screen_width_pow2 * bpp; + auto src_offset = bpp * (screen_width * y + x); + std::memcpy(dst_ptr + dst_offset, src_ptr + src_offset, bpp); + } + } + + Memory::RasterizerFlushVirtualRegion(dst_vaddr, screen_width_pow2 * height * bpp, + Memory::FlushMode::Invalidate); +} + +void AppletManager::CaptureFrameBuffers() { + auto gsp = + Core::System::GetInstance().ServiceManager().GetService("gsp::Gpu"); + auto active_thread_id = gsp->GetActiveThreadId(); + auto top_screen = gsp->GetFrameBufferInfo(active_thread_id, 0); + auto bottom_screen = gsp->GetFrameBufferInfo(active_thread_id, 1); + + auto top_fb = top_screen->framebuffer_info[top_screen->index]; + auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index]; + + CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, bottom_fb.address_left, 320, + capture_info->bottom_screen_format); + CaptureFrameBuffer(system, capture_info->top_screen_left_offset, top_fb.address_left, 400, + capture_info->top_screen_format); + if (capture_info->is_3d) { + CaptureFrameBuffer(system, capture_info->top_screen_right_offset, top_fb.address_right, 400, + capture_info->top_screen_format); + } +} + +void AppletManager::LoadInputDevices() { + home_button = Input::CreateDevice( + Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]); +} + +void AppletManager::HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) { + if (is_device_reload_pending.exchange(false)) { + LoadInputDevices(); + } + + const bool state = home_button->GetStatus(); + // NOTE: We technically do support loading and jumping to home menu even if it isn't + // initially registered. However since the home menu suspend is not bug-free, we don't + // want normal users who didn't launch the home menu accidentally pressing the home + // button binding and freezing their game, so for now, gate it to only environments + // where the home menu was already loaded by the user (last condition). + if (state && !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) { + SendNotification(Notification::HomeButtonSingle); + } + last_home_button_state = state; + + // Reschedule recurrent event + Core::System::GetInstance().CoreTiming().ScheduleEvent( + usToCycles(home_button_update_interval_us) - cycles_late, home_button_update_event); +} + AppletManager::AppletManager(Core::System& system) : system(system) { lock = system.Kernel().CreateMutex(false, "APT_U:Lock"); for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) { @@ -828,10 +1260,20 @@ AppletManager::AppletManager(Core::System& system) : system(system) { system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter"); } HLE::Applets::Init(); + home_button_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent( + "Home Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) { + HomeButtonUpdateEvent(user_data, cycles_late); + }); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + usToCycles(home_button_update_interval_us), home_button_update_event); } AppletManager::~AppletManager() { HLE::Applets::Shutdown(); } +void AppletManager::ReloadInputDevices() { + is_device_reload_pending.store(true); +} + } // namespace Service::APT diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index b7a11500a..1f7b041de 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -13,6 +13,7 @@ #include #include #include +#include "core/frontend/input.h" #include "core/global.h" #include "core/hle/kernel/event.h" #include "core/hle/result.h" @@ -46,6 +47,21 @@ enum class SignalType : u32 { WakeupToLaunchApplication = 0x11, }; +enum class Notification : u32 { + None = 0, + HomeButtonSingle = 1, + HomeButtonDouble = 2, + SleepQuery = 3, + SleepCancelledByOpen = 4, + SleepAccepted = 5, + SleepAwake = 6, + Shutdown = 7, + PowerButtonClick = 8, + PowerButtonClear = 9, + TrySleep = 10, + OrderToClose = 11, +}; + /// App Id's used by APT functions enum class AppletId : u32 { None = 0, @@ -103,19 +119,20 @@ private: friend class boost::serialization::access; }; -enum class AppletPos { +enum class AppletPos : u32 { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4, - AutoLibrary = 5 + AutoLibrary = 5, + Invalid = 0xFF, }; union AppletAttributes { u32 raw; - BitField<0, 3, u32> applet_pos; + BitField<0, 3, AppletPos> applet_pos; BitField<29, 1, u32> is_home_menu; AppletAttributes() : raw(0) {} @@ -178,11 +195,41 @@ private: friend class boost::serialization::access; }; +/// Used by the application to pass information about the current framebuffer to applets. +struct CaptureBufferInfo { + u32_le size; + u8 is_3d; + INSERT_PADDING_BYTES(0x3); // Padding for alignment + u32_le top_screen_left_offset; + u32_le top_screen_right_offset; + u32_le top_screen_format; + u32_le bottom_screen_left_offset; + u32_le bottom_screen_right_offset; + u32_le bottom_screen_format; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& size; + ar& is_3d; + ar& top_screen_left_offset; + ar& top_screen_right_offset; + ar& top_screen_format; + ar& bottom_screen_left_offset; + ar& bottom_screen_right_offset; + ar& bottom_screen_format; + } + friend class boost::serialization::access; +}; +static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size"); + class AppletManager : public std::enable_shared_from_this { public: explicit AppletManager(Core::System& system); ~AppletManager(); + void ReloadInputDevices(); + /** * Clears any existing parameter and places a new one. This function is currently only used by * HLE Applets and should be likely removed in the future @@ -211,6 +258,9 @@ public: ResultCode Enable(AppletAttributes attributes); bool IsRegistered(AppletId app_id); + ResultVal InquireNotification(AppletId app_id); + ResultCode SendNotification(Notification notification); + ResultCode PrepareToStartLibraryApplet(AppletId applet_id); ResultCode PreloadLibraryApplet(AppletId applet_id); ResultCode FinishPreloadingLibraryApplet(AppletId applet_id); @@ -227,6 +277,18 @@ public: ResultCode PrepareToCloseSystemApplet(); ResultCode CloseSystemApplet(std::shared_ptr object, const std::vector& buffer); + ResultCode OrderToCloseSystemApplet(); + + ResultCode PrepareToJumpToHomeMenu(); + ResultCode JumpToHomeMenu(std::shared_ptr object, + const std::vector& buffer); + ResultCode PrepareToLeaveHomeMenu(); + ResultCode LeaveHomeMenu(std::shared_ptr object, const std::vector& buffer); + + ResultCode OrderToCloseApplication(); + ResultCode PrepareToCloseApplication(bool return_to_sys); + ResultCode CloseApplication(std::shared_ptr object, + const std::vector& buffer); ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, ApplicationJumpFlags flags); @@ -239,10 +301,40 @@ public: deliver_arg = std::move(arg); } + std::vector GetCaptureInfo() { + std::vector buffer; + if (capture_info) { + buffer.resize(sizeof(CaptureBufferInfo)); + std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo)); + } + return buffer; + } + std::vector ReceiveCaptureBufferInfo() { + std::vector buffer = GetCaptureInfo(); + capture_info.reset(); + return buffer; + } + void SendCaptureBufferInfo(std::vector buffer) { + ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); + + capture_info.emplace(); + std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); + } + ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type); ResultCode StartApplication(const std::vector& parameter, const std::vector& hmac, bool paused); ResultCode WakeupApplication(); + ResultCode CancelApplication(); + + struct AppletManInfo { + AppletPos active_applet_pos; + AppletId requested_applet_id; + AppletId home_menu_applet_id; + AppletId active_applet_id; + }; + + ResultVal GetAppletManInfo(AppletPos requested_applet_pos); struct AppletInfo { u64 title_id; @@ -273,6 +365,8 @@ private: boost::optional app_start_parameters{}; boost::optional deliver_arg{}; + boost::optional capture_info; + static constexpr std::size_t NumAppletSlot = 4; enum class AppletSlot : u8 { @@ -292,6 +386,7 @@ private: bool registered; bool loaded; AppletAttributes attributes; + Notification notification; std::shared_ptr notification_event; std::shared_ptr parameter_event; @@ -311,6 +406,7 @@ private: ar& registered; ar& loaded; ar& attributes.raw; + ar& notification; ar& notification_event; ar& parameter_event; } @@ -325,6 +421,16 @@ private: SignalType library_applet_closing_command = SignalType::None; AppletId last_prepared_library_applet = AppletId::None; AppletSlot last_system_launcher_slot = AppletSlot::Error; + AppletSlot last_jump_to_home_slot = AppletSlot::Error; + bool ordered_to_close_sys_applet = false; + bool ordered_to_close_application = false; + bool application_cancelled = false; + AppletSlot application_close_target = AppletSlot::Error; + + Core::TimingEventType* home_button_update_event; + std::atomic is_device_reload_pending{true}; + std::unique_ptr home_button; + bool last_home_button_state = false; Core::System& system; @@ -346,6 +452,11 @@ private: void EnsureHomeMenuLoaded(); + void CaptureFrameBuffers(); + + void LoadInputDevices(); + void HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late); + template void serialize(Archive& ar, const unsigned int file_version) { ar& next_parameter; @@ -358,10 +469,20 @@ private: ar& last_library_launcher_slot; ar& last_prepared_library_applet; ar& last_system_launcher_slot; + ar& last_jump_to_home_slot; + ar& ordered_to_close_sys_applet; + ar& ordered_to_close_application; + ar& application_cancelled; + ar& application_close_target; ar& lock; + ar& capture_info; } ar& applet_slots; ar& library_applet_closing_command; + + if (Archive::is_loading::value) { + LoadInputDevices(); + } } friend class boost::serialization::access; }; diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index bb0d19f7a..45051963e 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -41,7 +41,6 @@ void Module::serialize(Archive& ar, const unsigned int file_version) { ar& shared_font_relocated; ar& cpu_percent; ar& unknown_ns_state_field; - ar& screen_capture_buffer; ar& screen_capture_post_permission; ar& applet_manager; if (file_version > 0) { @@ -73,6 +72,47 @@ void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx) LOG_WARNING(Service_APT, "called size={}", size); } +void Module::NSInterface::ShutdownAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xE, 0, 0); // 0xE0000 + + LOG_INFO(Service_APT, "called"); + + apt->system.RequestShutdown(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::NSInterface::RebootSystem(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x10, 6, 0); // 0x100180 + const auto launch_title = rp.Pop() != 0; + const auto title_id = rp.Pop(); + const auto media_type = static_cast(rp.Pop()); + rp.Skip(1, false); // Skip padding + const auto mem_type = rp.Pop(); + + LOG_WARNING(Service_APT, + "called launch_title={}, title_id={:016X}, media_type={:02X}, mem_type={:02X}", + launch_title, title_id, media_type, mem_type); + + // TODO: Implement loading a specific title. + apt->system.RequestReset(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::NSInterface::RebootSystemClean(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x16, 0, 0); // 0x160000 + + LOG_INFO(Service_APT, "called"); + + apt->system.RequestReset(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + void Module::APTInterface::Initialize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x2, 2, 0); // 0x20080 const auto app_id = rp.PopEnum(); @@ -332,16 +372,22 @@ void Module::APTInterface::Enable(Kernel::HLERequestContext& ctx) { void Module::APTInterface::GetAppletManInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x5, 1, 0); // 0x50040 - const auto unk = rp.Pop(); + auto applet_pos = rp.PopEnum(); - IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); - rb.Push(RESULT_SUCCESS); // No error - rb.Push(0); - rb.Push(0); - rb.Push(static_cast(AppletId::HomeMenu)); // Home menu AppID - rb.Push(static_cast(AppletId::Application)); // TODO(purpasmart96): Do this correctly + LOG_DEBUG(Service_APT, "called, applet_pos={:08X}", applet_pos); - LOG_WARNING(Service_APT, "(STUBBED) called unk={:#010X}", unk); + auto info = apt->applet_manager->GetAppletManInfo(applet_pos); + if (info.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(info.Code()); + } else { + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + rb.Push(RESULT_SUCCESS); + rb.PushEnum(info->active_applet_pos); + rb.PushEnum(info->requested_applet_id); + rb.PushEnum(info->home_menu_applet_id); + rb.PushEnum(info->active_applet_id); + } } void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { @@ -357,13 +403,19 @@ void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { void Module::APTInterface::InquireNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0xB, 1, 0); // 0xB0040 - const auto app_id = rp.Pop(); + const auto app_id = rp.PopEnum(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - rb.Push(RESULT_SUCCESS); // No error - rb.Push(static_cast(SignalType::None)); // Signal type + LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id); - LOG_WARNING(Service_APT, "(STUBBED) called app_id={:#010X}", app_id); + auto notification = apt->applet_manager->InquireNotification(app_id); + if (notification.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(notification.Code()); + } else { + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast(notification.Unwrap())); + } } void Module::APTInterface::SendParameter(Kernel::HLERequestContext& ctx) { @@ -561,6 +613,15 @@ void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) { rb.Push(apt->applet_manager->WakeupApplication()); } +void Module::APTInterface::CancelApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1D, 0, 0); // 0x001D0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->CancelApplication()); +} + void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x4B, 3, 2); // 0x004B00C2 @@ -574,8 +635,18 @@ void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { "(STUBBED) called command={:#010X}, input_size={:#010X}, output_size={:#010X}", utility_command, input_size, output_size); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + std::vector out(output_size); + if (utility_command == 0x6 && output_size > 0) { + // Command 0x6 (TryLockTransition) expects a boolean return value indicating + // whether the attempt succeeded. Since we don't implement any of the transition + // locking stuff yet, fake a success result to avoid app crashes. + out[0] = true; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); // No error + rb.Push(RESULT_SUCCESS); // Utility function result + rb.PushStaticBuffer(out, 0); } void Module::APTInterface::SetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) { @@ -688,18 +759,35 @@ void Module::APTInterface::StartSystemApplet(Kernel::HLERequestContext& ctx) { rb.Push(apt->applet_manager->StartSystemApplet(applet_id, object, buffer)); } -void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x27, 1, 4); - [[maybe_unused]] const auto parameters_size = rp.Pop(); - [[maybe_unused]] const auto object = rp.PopGenericObject(); - [[maybe_unused]] const auto buffer = rp.PopStaticBuffer(); +void Module::APTInterface::OrderToCloseApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x21, 0, 0); LOG_DEBUG(Service_APT, "called"); - apt->system.RequestShutdown(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->OrderToCloseApplication()); +} + +void Module::APTInterface::PrepareToCloseApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x22, 1, 0); + const auto return_to_sys = rp.Pop() != 0; + + LOG_DEBUG(Service_APT, "called return_to_sys={}", return_to_sys); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); + rb.Push(apt->applet_manager->PrepareToCloseApplication(return_to_sys)); +} + +void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x27, 1, 4); + const auto parameter_size = rp.Pop(); + const auto object = rp.PopGenericObject(); + const auto buffer = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called size={}", parameter_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->CloseApplication(object, buffer)); } void Module::APTInterface::CancelLibraryApplet(Kernel::HLERequestContext& ctx) { @@ -758,6 +846,57 @@ void Module::APTInterface::CloseSystemApplet(Kernel::HLERequestContext& ctx) { rb.Push(apt->applet_manager->CloseSystemApplet(object, buffer)); } +void Module::APTInterface::OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2A, 0, 0); // 0x2A0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->OrderToCloseSystemApplet()); +} + +void Module::APTInterface::PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2B, 0, 0); // 0x2B0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->PrepareToJumpToHomeMenu()); +} + +void Module::APTInterface::JumpToHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2C, 1, 4); // 0x2C0044 + const auto parameter_size = rp.Pop(); + const auto object = rp.PopGenericObject(); + const auto buffer = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called size={}", parameter_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->JumpToHomeMenu(object, buffer)); +} + +void Module::APTInterface::PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2D, 0, 0); // 0x2D0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->PrepareToLeaveHomeMenu()); +} + +void Module::APTInterface::LeaveHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2E, 1, 4); // 0x2E0044 + const auto parameter_size = rp.Pop(); + const auto object = rp.PopGenericObject(); + const auto buffer = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called size={}", parameter_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->LeaveHomeMenu(object, buffer)); +} + void Module::APTInterface::LoadSysMenuArg(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x36, 1, 0); // 0x00360040 const auto size = std::min(std::size_t{rp.Pop()}, SysMenuArgSize); @@ -794,8 +933,7 @@ void Module::APTInterface::SendCaptureBufferInfo(Kernel::HLERequestContext& ctx) LOG_DEBUG(Service_APT, "called"); - ASSERT(size == 0x20); - apt->screen_capture_buffer = buffer; + apt->applet_manager->SendCaptureBufferInfo(buffer); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -807,12 +945,14 @@ void Module::APTInterface::ReceiveCaptureBufferInfo(Kernel::HLERequestContext& c LOG_DEBUG(Service_APT, "called"); - ASSERT(size == 0x20); + auto screen_capture_buffer = apt->applet_manager->ReceiveCaptureBufferInfo(); + auto real_size = std::min(static_cast(screen_capture_buffer.size()), size); + screen_capture_buffer.resize(size); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(static_cast(apt->screen_capture_buffer.size())); - rb.PushStaticBuffer(std::move(apt->screen_capture_buffer), 0); + rb.Push(real_size); + rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); } void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { @@ -820,13 +960,15 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { const auto size = rp.Pop(); LOG_DEBUG(Service_APT, "called"); - ASSERT(size == 0x20); + + auto screen_capture_buffer = apt->applet_manager->GetCaptureInfo(); + auto real_size = std::min(static_cast(screen_capture_buffer.size()), size); + screen_capture_buffer.resize(size); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(static_cast(apt->screen_capture_buffer.size())); - // This service function does not clear the capture buffer. - rb.PushStaticBuffer(apt->screen_capture_buffer, 0); + rb.Push(real_size); + rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); } void Module::APTInterface::SetScreenCapPostPermission(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index e831a7d77..90688ff85 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -31,20 +31,6 @@ class AppletManager; /// Each APT service can only have up to 2 sessions connected at the same time. static const u32 MaxAPTSessions = 2; -/// Used by the application to pass information about the current framebuffer to applets. -struct CaptureBufferInfo { - u32_le size; - u8 is_3d; - INSERT_PADDING_BYTES(0x3); // Padding for alignment - u32_le top_screen_left_offset; - u32_le top_screen_right_offset; - u32_le top_screen_format; - u32_le bottom_screen_left_offset; - u32_le bottom_screen_right_offset; - u32_le bottom_screen_format; -}; -static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size"); - constexpr std::size_t SysMenuArgSize = 0x40; enum class StartupArgumentType : u32 { @@ -87,6 +73,37 @@ public: * 0 : Result of function, 0 on success, otherwise error code */ void SetWirelessRebootInfo(Kernel::HLERequestContext& ctx); + + /** + * NS::ShutdownAsync service function. + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void ShutdownAsync(Kernel::HLERequestContext& ctx); + + /** + * NS::RebootSystem service function. + * Inputs: + * 1 : Boolean indicating whether to launch a title. + * 2-3 : Title ID + * 4 : Media Type + * 5 : Padding + * 6 : Launch memory type + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void RebootSystem(Kernel::HLERequestContext& ctx); + + /** + * NS::RebootSystemClean service function. + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void RebootSystemClean(Kernel::HLERequestContext& ctx); }; class APTInterface : public ServiceFramework { @@ -371,6 +388,16 @@ public: */ void WakeupApplication(Kernel::HLERequestContext& ctx); + /** + * APT::CancelApplication service function. + * Inputs: + * 0 : Command header [0x001D0000] + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code + */ + void CancelApplication(Kernel::HLERequestContext& ctx); + /** * APT::AppletUtility service function * Inputs: @@ -491,6 +518,27 @@ public: */ void StartSystemApplet(Kernel::HLERequestContext& ctx); + /** + * APT::OrderToCloseApplication service function + * Inputs: + * 0 : Command header [0x00210000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void OrderToCloseApplication(Kernel::HLERequestContext& ctx); + + /** + * APT::PrepareToCloseApplication service function + * Inputs: + * 0 : Command header [0x00220040] + * 1 : Boolean indicating whether to cancel applet preloads. + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void PrepareToCloseApplication(Kernel::HLERequestContext& ctx); + /** * APT::CloseApplication service function * Inputs: @@ -629,6 +677,66 @@ public: */ void CloseSystemApplet(Kernel::HLERequestContext& ctx); + /** + * APT::OrderToCloseSystemApplet service function + * Inputs: + * 0 : Command header [0x002A0000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx); + + /** + * APT::PrepareToJumpToHomeMenu service function + * Inputs: + * 0 : Command header [0x002B0000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx); + + /** + * APT::JumpToHomeMenu service function + * Inputs: + * 0 : Command header [0x002C0044] + * 1 : Buffer size + * 2 : 0x0 + * 3 : Object handle + * 4 : (Size << 14) | 2 + * 5 : Input buffer virtual address + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void JumpToHomeMenu(Kernel::HLERequestContext& ctx); + + /** + * APT::PrepareToLeaveHomeMenu service function + * Inputs: + * 0 : Command header [0x002B0000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx); + + /** + * APT::LeaveHomeMenu service function + * Inputs: + * 0 : Command header [0x002C0044] + * 1 : Buffer size + * 2 : 0x0 + * 3 : Object handle + * 4 : (Size << 14) | 2 + * 5 : Input buffer virtual address + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void LeaveHomeMenu(Kernel::HLERequestContext& ctx); + /** * APT::LoadSysMenuArg service function * Inputs: @@ -801,7 +909,6 @@ private: // APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode u8 unknown_ns_state_field = 0; - std::vector screen_capture_buffer; std::array sys_menu_arg_buffer; ScreencapPostPermission screen_capture_post_permission = diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 091ccaf0e..824e2b176 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -42,8 +42,8 @@ APT_A::APT_A(std::shared_ptr apt) {0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, &APT_A::StartSystemApplet, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00210000, &APT_A::OrderToCloseApplication, "OrderToCloseApplication"}, + {0x00220040, &APT_A::PrepareToCloseApplication, "PrepareToCloseApplication"}, {0x00230040, nullptr, "PrepareToJumpToApplication"}, {0x00240044, nullptr, "JumpToApplication"}, {0x002500C0, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, @@ -51,11 +51,11 @@ APT_A::APT_A(std::shared_ptr apt) {0x00270044, &APT_A::CloseApplication, "CloseApplication"}, {0x00280044, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"}, {0x00290044, &APT_A::CloseSystemApplet, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002A0000, &APT_A::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, + {0x002B0000, &APT_A::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, + {0x002C0044, &APT_A::JumpToHomeMenu, "JumpToHomeMenu"}, + {0x002D0000, &APT_A::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, + {0x002E0044, &APT_A::LeaveHomeMenu, "LeaveHomeMenu"}, {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 98ee8cb61..639c0d934 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -42,8 +42,8 @@ APT_S::APT_S(std::shared_ptr apt) {0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, &APT_S::StartSystemApplet, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00210000, &APT_S::OrderToCloseApplication, "OrderToCloseApplication"}, + {0x00220040, &APT_S::PrepareToCloseApplication, "PrepareToCloseApplication"}, {0x00230040, nullptr, "PrepareToJumpToApplication"}, {0x00240044, nullptr, "JumpToApplication"}, {0x002500C0, &APT_S::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, @@ -51,11 +51,11 @@ APT_S::APT_S(std::shared_ptr apt) {0x00270044, &APT_S::CloseApplication, "CloseApplication"}, {0x00280044, &APT_S::CloseLibraryApplet, "CloseLibraryApplet"}, {0x00290044, &APT_S::CloseSystemApplet, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002A0000, &APT_S::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, + {0x002B0000, &APT_S::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, + {0x002C0044, &APT_S::JumpToHomeMenu, "JumpToHomeMenu"}, + {0x002D0000, &APT_S::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, + {0x002E0044, &APT_S::LeaveHomeMenu, "LeaveHomeMenu"}, {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 329187153..34e9bc491 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -42,8 +42,8 @@ APT_U::APT_U(std::shared_ptr apt) {0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, &APT_U::StartSystemApplet, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00210000, &APT_U::OrderToCloseApplication, "OrderToCloseApplication"}, + {0x00220040, &APT_U::PrepareToCloseApplication, "PrepareToCloseApplication"}, {0x00230040, nullptr, "PrepareToJumpToApplication"}, {0x00240044, nullptr, "JumpToApplication"}, {0x002500C0, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, @@ -51,11 +51,11 @@ APT_U::APT_U(std::shared_ptr apt) {0x00270044, &APT_U::CloseApplication, "CloseApplication"}, {0x00280044, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"}, {0x00290044, &APT_U::CloseSystemApplet, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002A0000, &APT_U::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, + {0x002B0000, &APT_U::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, + {0x002C0044, &APT_U::JumpToHomeMenu, "JumpToHomeMenu"}, + {0x002D0000, &APT_U::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, + {0x002E0044, &APT_U::LeaveHomeMenu, "LeaveHomeMenu"}, {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, diff --git a/src/core/hle/service/apt/ns_s.cpp b/src/core/hle/service/apt/ns_s.cpp index 2988ea5d8..63666dcae 100644 --- a/src/core/hle/service/apt/ns_s.cpp +++ b/src/core/hle/service/apt/ns_s.cpp @@ -19,12 +19,12 @@ NS_S::NS_S(std::shared_ptr apt) {0x00070042, nullptr, "CardUpdateInitialize"}, {0x00080000, nullptr, "CardUpdateShutdown"}, {0x000D0140, nullptr, "SetTWLBannerHMAC"}, - {0x000E0000, nullptr, "ShutdownAsync"}, - {0x00100180, nullptr, "RebootSystem"}, + {0x000E0000, &NS_S::ShutdownAsync, "ShutdownAsync"}, + {0x00100180, &NS_S::RebootSystem, "RebootSystem"}, {0x00110100, nullptr, "TerminateTitle"}, {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, {0x00150140, nullptr, "LaunchApplication"}, - {0x00160000, nullptr, "RebootSystemClean"}, + {0x00160000, &NS_S::RebootSystemClean, "RebootSystemClean"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index f1ae11615..144339e79 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -663,14 +663,17 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) { void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x18, 0, 0); - // TODO(Subv): We're always returning the framebuffer structures for thread_id = 0, - // because we only support a single running application at a time. - // This should always return the framebuffer data that is currently displayed on the screen. + if (active_thread_id == UINT32_MAX) { + LOG_WARNING(Service_GSP, "Called without an active thread."); - u32 thread_id = 0; + // TODO: Find the right error code. + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(-1); + return; + } - FrameBufferUpdate* top_screen = GetFrameBufferInfo(thread_id, 0); - FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(thread_id, 1); + FrameBufferUpdate* top_screen = GetFrameBufferInfo(active_thread_id, 0); + FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(active_thread_id, 1); struct CaptureInfoEntry { u32_le address_left; @@ -700,6 +703,39 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_GSP, "called"); } +void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x19, 0, 0); + + LOG_INFO(Service_GSP, "called"); + + // TODO: This should also DMA framebuffers into VRAM and save LCD register state. + Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE, + Memory::FlushMode::Flush); + auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR); + saved_vram.emplace(std::vector(Memory::VRAM_SIZE)); + std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1A, 0, 0); + + LOG_INFO(Service_GSP, "called"); + + if (saved_vram) { + // TODO: This should also restore LCD register state. + auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR); + std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE); + Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE, + Memory::FlushMode::Invalidate); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x16, 1, 2); @@ -808,8 +844,8 @@ GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 2), system {0x00160042, &GSP_GPU::AcquireRight, "AcquireRight"}, {0x00170000, &GSP_GPU::ReleaseRight, "ReleaseRight"}, {0x00180000, &GSP_GPU::ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"}, - {0x00190000, nullptr, "SaveVramSysArea"}, - {0x001A0000, nullptr, "RestoreVramSysArea"}, + {0x00190000, &GSP_GPU::SaveVramSysArea, "SaveVramSysArea"}, + {0x001A0000, &GSP_GPU::RestoreVramSysArea, "RestoreVramSysArea"}, {0x001B0000, nullptr, "ResetGpuCore"}, {0x001C0040, &GSP_GPU::SetLedForceOff, "SetLedForceOff"}, {0x001D0040, nullptr, "SetTestCommand"}, diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 0a8517c8c..cf976654e 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "common/bit_field.h" #include "common/common_types.h" @@ -238,6 +239,13 @@ public: */ FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); + /** + * Retreives the ID of the thread with GPU rights. + */ + u32 GetActiveThreadId() { + return active_thread_id; + } + private: /** * Signals that the specified interrupt type has occurred to userland code for the specified GSP @@ -402,6 +410,32 @@ private: */ void ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx); + /** + * GSP_GPU::SaveVramSysArea service function + * + * Returns information about the current framebuffer state + * + * Inputs: + * 0: Header 0x00190000 + * Outputs: + * 0: Header Code[0x00190040] + * 1: Result code + */ + void SaveVramSysArea(Kernel::HLERequestContext& ctx); + + /** + * GSP_GPU::RestoreVramSysArea service function + * + * Returns information about the current framebuffer state + * + * Inputs: + * 0: Header 0x001A0000 + * Outputs: + * 0: Header Code[0x001A0040] + * 1: Result code + */ + void RestoreVramSysArea(Kernel::HLERequestContext& ctx); + /** * GSP_GPU::StoreDataCache service function * @@ -438,6 +472,9 @@ private: bool first_initialization = true; + /// VRAM copy saved using SaveVramSysArea. + boost::optional> saved_vram; + /// Maximum number of threads that can be registered at the same time in the GSP module. static constexpr u32 MaxGSPThreads = 4; @@ -453,6 +490,7 @@ private: ar& active_thread_id; ar& first_initialization; ar& used_thread_ids; + ar& saved_vram; } friend class boost::serialization::access; From 27be16ee315dfb6b28ecd2715075c0bafe4321f2 Mon Sep 17 00:00:00 2001 From: DRayX <7531689+DRayX@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:44:45 -0800 Subject: [PATCH 7/7] Update AndroidManifest for Android TV (#6330) --- src/android/app/src/main/AndroidManifest.xml | 24 ++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index dcae891e4..7d35fe910 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -7,13 +7,24 @@ - - - - + + + android:required="false"/> + + + + + @@ -21,7 +32,6 @@ - - +