Merge branch 'master' of https://github.com/GPUCode/citra into vulkan-2
This commit is contained in:
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
10
.github/ISSUE_TEMPLATE/blank_issue_template.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
name: New Issue (Developers Only)
|
||||||
|
description: A blank issue template for developers only. If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
**If you are not a developer, do not use this issue template. Your issue WILL BE CLOSED if you do not use the appropriate issue template.**
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: "Issue"
|
@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug Report / Feature Request
|
|
||||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Citra or you are requesting a feature you believe would make Citra better.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!---
|
|
||||||
|
|
||||||
Please read the FAQ:
|
|
||||||
https://citra-emu.org/wiki/faq/
|
|
||||||
|
|
||||||
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
|
||||||
https://community.citra-emu.org/
|
|
||||||
|
|
||||||
If the FAQ does not answer your question, please go to:
|
|
||||||
https://community.citra-emu.org/
|
|
||||||
|
|
||||||
====================================================
|
|
||||||
|
|
||||||
When submitting an issue, please check the following:
|
|
||||||
|
|
||||||
- You have read the above.
|
|
||||||
- You have provided the version (commit hash) of Citra you are using.
|
|
||||||
- You have provided sufficient detail for the issue to be reproduced.
|
|
||||||
- You have provided system specs (if relevant).
|
|
||||||
- Please also provide:
|
|
||||||
- For any issues, a log file
|
|
||||||
- For crashes, a backtrace.
|
|
||||||
- For graphical issues, comparison screenshots with real hardware.
|
|
||||||
- For emulation inaccuracies, a test-case (if able).
|
|
||||||
|
|
||||||
--->
|
|
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
64
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: File a bug report
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Citra.
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there an existing issue for this?
|
||||||
|
description: Please search to see if an issue already exists for the bug you encountered.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Affected Build(s)
|
||||||
|
description: List the affected build(s) that this issue applies to.
|
||||||
|
placeholder: Nightly 1234 / Canary 1234
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: issue-desc
|
||||||
|
attributes:
|
||||||
|
label: Description of Issue
|
||||||
|
description: A brief description of the issue encountered along with any images and/or videos.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behavior
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A brief description of how it is expected to work along with any images and/or videos.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction-steps
|
||||||
|
attributes:
|
||||||
|
label: Reproduction Steps
|
||||||
|
description: A brief explanation of how to reproduce this issue. If possible, provide a save file to aid in reproducing the issue.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Log File
|
||||||
|
description: A log file will help our developers to better diagnose and fix the issue.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: system-config
|
||||||
|
attributes:
|
||||||
|
label: System Configuration
|
||||||
|
placeholder: |
|
||||||
|
CPU: Intel i5-10400 / AMD Ryzen 5 3600
|
||||||
|
GPU/Driver: NVIDIA GeForce GTX 1060 (Driver 512.95)
|
||||||
|
RAM: 16GB DDR4-3200
|
||||||
|
OS: Windows 11 22H2 (Build 22621.819)
|
||||||
|
value: |
|
||||||
|
CPU:
|
||||||
|
GPU/Driver:
|
||||||
|
RAM:
|
||||||
|
OS:
|
||||||
|
validations:
|
||||||
|
required: true
|
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -6,6 +6,3 @@ contact_links:
|
|||||||
- name: Community forums
|
- name: Community forums
|
||||||
url: https://community.citra-emu.org
|
url: https://community.citra-emu.org
|
||||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
about: This is an alternative place for tech support, however helpers there are not as active.
|
||||||
- name: Citra Android
|
|
||||||
url: https://github.com/citra-emu/citra-android
|
|
||||||
about: If you need tech support on Citra Android, you should use either of the above two options. If you want to file an issue, you should go to the Android repo linked here.
|
|
||||||
|
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: File a feature request
|
||||||
|
labels: "request"
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make Citra better.
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there an existing issue for this?
|
||||||
|
description: Please search to see if an issue already exists for the feature you are requesting.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: what-feature
|
||||||
|
attributes:
|
||||||
|
label: What feature are you suggesting?
|
||||||
|
description: A brief description of the requested feature.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: why-feature
|
||||||
|
attributes:
|
||||||
|
label: Why would this feature be useful?
|
||||||
|
description: A brief description of why this feature would make Citra better.
|
||||||
|
validations:
|
||||||
|
required: true
|
@ -210,7 +210,7 @@ if (ENABLE_QT)
|
|||||||
set(QT_PREFIX_HINT)
|
set(QT_PREFIX_HINT)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Widgets Multimedia ${QT_PREFIX_HINT})
|
find_package(Qt5 REQUIRED COMPONENTS Widgets Multimedia Concurrent ${QT_PREFIX_HINT})
|
||||||
|
|
||||||
if (ENABLE_QT_TRANSLATION)
|
if (ENABLE_QT_TRANSLATION)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
||||||
|
@ -17,6 +17,7 @@ function(copy_citra_Qt5_deps target_dir)
|
|||||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||||
|
Qt5Concurrent$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Network$<$<CONFIG:Debug>:d>.*
|
Qt5Network$<$<CONFIG:Debug>:d>.*
|
||||||
)
|
)
|
||||||
|
79
README.md
79
README.md
@ -1,24 +1,49 @@
|
|||||||
**BEFORE FILING AN ISSUE, READ THE RELEVANT SECTION IN THE [CONTRIBUTING](https://github.com/citra-emu/citra/wiki/Contributing#reporting-issues) FILE!!!**
|
<h1 align="center">
|
||||||
|
<br>
|
||||||
|
<a href="https://citra-emu.org/"><img src="https://raw.githubusercontent.com/citra-emu/citra-assets/master/Main/citra_logo.svg" alt="Citra" width="200"></a>
|
||||||
|
<br>
|
||||||
|
<b>Citra</b>
|
||||||
|
<br>
|
||||||
|
</h1>
|
||||||
|
|
||||||
# Citra
|
<h4 align="center"><b>Citra</b> is the world's most popular, open-source, Nintendo 3DS emulator.
|
||||||
|
<br>
|
||||||
|
It is written in C++ with portability in mind and builds are actively maintained for Windows, Linux, Android and macOS.
|
||||||
|
</h4>
|
||||||
|
|
||||||
[](https://github.com/citra-emu/citra/actions)
|
<p align="center">
|
||||||
[](https://app.bitrise.io/app/4ccd8e5720f0d13b)
|
<a href="https://github.com/citra-emu/citra/actions/">
|
||||||
[](https://discord.gg/FAXfZV9)
|
<img src="https://github.com/citra-emu/citra/workflows/citra-ci/badge.svg"
|
||||||
|
alt="GitHub Actions Build Status">
|
||||||
|
</a>
|
||||||
|
<a href="https://discord.gg/FAXfZV9">
|
||||||
|
<img src="https://img.shields.io/discord/220740965957107713?color=%237289DA&label=Citra&logo=discord&logoColor=white"
|
||||||
|
alt="Discord">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS.
|
<p align="center">
|
||||||
|
<a href="#compatibility">Compatibility</a> |
|
||||||
|
<a href="#releases">Releases</a> |
|
||||||
|
<a href="#development">Development</a> |
|
||||||
|
<a href="#building">Building</a> |
|
||||||
|
<a href="#support">Support</a> |
|
||||||
|
<a href="#license">License</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
Citra emulates a subset of 3DS hardware and therefore is useful for running/debugging homebrew applications, and it is also able to run many commercial games! Some of these do not run at a playable state, but we are working every day to advance the project forward. (Playable here means compatibility of at least "Okay" on our [game compatibility list](https://citra-emu.org/game).)
|
|
||||||
|
|
||||||
Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project.
|
## Compatibility
|
||||||
|
|
||||||
Check out our [website](https://citra-emu.org/)!
|
The emulator is capable of running most commercial games at full speed, provided you meet the necessary hardware requirements.
|
||||||
|
|
||||||
|
For a full list of games Citra supports, please visit our [Compatibility page](https://citra-emu.org/game/)
|
||||||
|
|
||||||
|
Check out our [website](https://citra-emu.org/) for the latest news on exciting features, progress reports, and more!
|
||||||
|
Please read the [FAQ](https://citra-emu.org/wiki/faq/) before getting started with the project.
|
||||||
|
|
||||||
Need help? Check out our [asking for help](https://citra-emu.org/help/reference/asking/) guide.
|
Need help? Check out our [asking for help](https://citra-emu.org/help/reference/asking/) guide.
|
||||||
|
|
||||||
For development discussion, please join us on our [Discord server](https://citra-emu.org/discord/) or at #citra-dev on libera.
|
## Releases
|
||||||
|
|
||||||
### Releases
|
|
||||||
|
|
||||||
Citra has two main release channels: Nightly and Canary.
|
Citra has two main release channels: Nightly and Canary.
|
||||||
|
|
||||||
@ -28,30 +53,46 @@ The [Canary](https://github.com/citra-emu/citra-canary) build is based on the ma
|
|||||||
|
|
||||||
Both builds can be installed with the installer provided on the [website](https://citra-emu.org/download/), but those looking for specific versions or standalone releases can find them in the release tabs of the [Nightly](https://github.com/citra-emu/citra-nightly/releases) and [Canary](https://github.com/citra-emu/citra-canary/releases) repositories.
|
Both builds can be installed with the installer provided on the [website](https://citra-emu.org/download/), but those looking for specific versions or standalone releases can find them in the release tabs of the [Nightly](https://github.com/citra-emu/citra-nightly/releases) and [Canary](https://github.com/citra-emu/citra-canary/releases) repositories.
|
||||||
|
|
||||||
Currently, development and releases of the Android version are in [a separate repository](https://github.com/citra-emu/citra-android).
|
Android builds can be downloaded from the Google Play Store.
|
||||||
|
|
||||||
A Flatpak for Citra is available on [Flathub](https://flathub.org/apps/details/org.citra_emu.citra). Details on the build process can be found in [our Flathub repository](https://github.com/flathub/org.citra_emu.citra).
|
A Flatpak for Citra is available on [Flathub](https://flathub.org/apps/details/org.citra_emu.citra). Details on the build process can be found in [our Flathub repository](https://github.com/flathub/org.citra_emu.citra).
|
||||||
|
|
||||||
### Development
|
## Development
|
||||||
|
|
||||||
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/citra-emu/citra) is hosted.
|
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/citra-emu/citra) is hosted.
|
||||||
|
For development discussion, please join us on our [Discord server](https://citra-emu.org/discord/) or at #citra-dev on libera.
|
||||||
|
|
||||||
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/citra-emu/citra/wiki/Contributing) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information). You should also contact any of the developers in the forum in order to know about the current state of the emulator because the [TODO list](https://docs.google.com/document/d/1SWIop0uBI9IW8VGg97TAtoT_CHNoP42FzYmvG1F4QDA) isn't maintained anymore.
|
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/citra-emu/citra/wiki/Contributing) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information). You can also contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||||
|
|
||||||
If you want to contribute to the user interface translation, please check out the [citra project on transifex](https://www.transifex.com/citra/citra). We centralize the translation work there, and periodically upstream translations.
|
If you want to contribute to the user interface translation, please check out the [Citra project on transifex](https://www.transifex.com/citra/citra). We centralize the translation work there, and periodically upstream translations.
|
||||||
|
|
||||||
### Building
|
## Building
|
||||||
|
|
||||||
* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows)
|
* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows)
|
||||||
* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Building-For-Linux)
|
* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Building-For-Linux)
|
||||||
* __macOS__: [macOS Build](https://github.com/citra-emu/citra/wiki/Building-for-macOS)
|
* __macOS__: [macOS Build](https://github.com/citra-emu/citra/wiki/Building-for-macOS)
|
||||||
|
* __Android__: [Android Build](https://github.com/citra-emu/citra/wiki/Building-for-Android)
|
||||||
|
|
||||||
|
|
||||||
### Support
|
## Support
|
||||||
We happily accept monetary donations or donated games and hardware. Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra. Any donations received will go towards things like:
|
|
||||||
|
If you enjoy the project and want to support us financially, check out our Patreon!
|
||||||
|
|
||||||
|
<a href="https://www.patreon.com/citraemu">
|
||||||
|
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
We also happily accept donated games and hardware.
|
||||||
|
Please see our [donations page](https://citra-emu.org/donate/) for more information on how you can contribute to Citra.
|
||||||
|
Any donations received will go towards things like:
|
||||||
* 3DS consoles for developers to explore the hardware
|
* 3DS consoles for developers to explore the hardware
|
||||||
* 3DS games for testing
|
* 3DS games for testing
|
||||||
* Any equipment required for homebrew
|
* Any equipment required for homebrew
|
||||||
* Infrastructure setup
|
* Infrastructure setup
|
||||||
|
|
||||||
We also more than gladly accept used 3DS consoles! If you would like to give yours away, don't hesitate to join our [Discord server](https://citra-emu.org/discord/) and talk to bunnei.
|
We also more than gladly accept used 3DS consoles! If you would like to give yours away, don't hesitate to join our [Discord server](https://citra-emu.org/discord/) and talk to bunnei.
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Citra is licensed under the GPLv2 (or any later version). Refer to the [LICENSE.txt](https://github.com/citra-emu/citra/blob/master/license.txt) file.
|
||||||
|
17
dist/qt_themes/default/style.qss
vendored
17
dist/qt_themes/default/style.qss
vendored
@ -10,4 +10,19 @@ QPushButton#GraphicsAPIStatusBarButton {
|
|||||||
|
|
||||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||||
border: 1px solid #76797C;
|
border: 1px solid #76797C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton {
|
||||||
|
color: #A5A5A5;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0px 3px 0px 3px;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 60px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton:hover {
|
||||||
|
border: 1px solid #76797C;
|
||||||
|
}
|
||||||
|
17
dist/qt_themes/qdarkstyle/style.qss
vendored
17
dist/qt_themes/qdarkstyle/style.qss
vendored
@ -1249,4 +1249,19 @@ QPushButton#GraphicsAPIStatusBarButton {
|
|||||||
|
|
||||||
QPushButton#GraphicsAPIStatusBarButton:hover {
|
QPushButton#GraphicsAPIStatusBarButton:hover {
|
||||||
border: 1px solid #76797C;
|
border: 1px solid #76797C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton {
|
||||||
|
color: #A5A5A5;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0px 3px 0px 3px;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 60px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton#3DOptionStatusBarButton:hover {
|
||||||
|
border: 1px solid #76797C;
|
||||||
|
}
|
||||||
|
@ -178,6 +178,8 @@ void Config::ReadValues() {
|
|||||||
static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_right", 360));
|
static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_right", 360));
|
||||||
Settings::values.custom_bottom_bottom =
|
Settings::values.custom_bottom_bottom =
|
||||||
static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_bottom", 480));
|
static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_bottom", 480));
|
||||||
|
Settings::values.custom_second_layer_opacity =
|
||||||
|
static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_second_layer_opacity", 100));
|
||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
Settings::values.dump_textures = sdl2_config->GetBoolean("Utility", "dump_textures", false);
|
Settings::values.dump_textures = sdl2_config->GetBoolean("Utility", "dump_textures", false);
|
||||||
|
@ -204,6 +204,9 @@ custom_bottom_top =
|
|||||||
custom_bottom_right =
|
custom_bottom_right =
|
||||||
custom_bottom_bottom =
|
custom_bottom_bottom =
|
||||||
|
|
||||||
|
# Opacity of second layer when using custom layout option (bottom screen unless swapped)
|
||||||
|
custom_second_layer_opacity =
|
||||||
|
|
||||||
# Swaps the prominent screen with the other screen.
|
# Swaps the prominent screen with the other screen.
|
||||||
# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
|
# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
|
||||||
# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
|
# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
|
||||||
|
@ -266,7 +266,7 @@ endif()
|
|||||||
create_target_directory_groups(citra-qt)
|
create_target_directory_groups(citra-qt)
|
||||||
|
|
||||||
target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
|
target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
|
||||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad vma vulkan-headers nihstro-headers Qt5::Widgets Qt5::Multimedia)
|
target_link_libraries(citra-qt PRIVATE Boost::boost glad vma vulkan-headers nihstro-headers Qt5::Widgets Qt5::Multimedia Qt5::Concurrent)
|
||||||
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
if (NOT WIN32)
|
if (NOT WIN32)
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include "citra_qt/configuration/config.h"
|
#include "citra_qt/configuration/config.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
#include "common/settings.h"
|
||||||
#include "core/frontend/mic.h"
|
#include "core/frontend/mic.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
@ -55,14 +56,16 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
|||||||
// This must be in alphabetical order according to action name as it must have the same order as
|
// This must be in alphabetical order according to action name as it must have the same order as
|
||||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const std::array<UISettings::Shortcut, 24> Config::default_hotkeys {{
|
const std::array<UISettings::Shortcut, 27> Config::default_hotkeys {{
|
||||||
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
||||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||||
|
{QStringLiteral("Decrease 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+-"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Exit Citra"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
|
{QStringLiteral("Exit Citra"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
|
{QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
|
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
|
||||||
|
{QStringLiteral("Increase 3D Factor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl++"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
||||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
|
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
|
||||||
@ -74,6 +77,7 @@ const std::array<UISettings::Shortcut, 24> Config::default_hotkeys {{
|
|||||||
{QStringLiteral("Save to Oldest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
|
{QStringLiteral("Save to Oldest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
||||||
|
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Per-Game Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Per-Game Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
|
||||||
@ -495,6 +499,7 @@ void Config::ReadLayoutValues() {
|
|||||||
ReadBasicSetting(Settings::values.custom_bottom_top);
|
ReadBasicSetting(Settings::values.custom_bottom_top);
|
||||||
ReadBasicSetting(Settings::values.custom_bottom_right);
|
ReadBasicSetting(Settings::values.custom_bottom_right);
|
||||||
ReadBasicSetting(Settings::values.custom_bottom_bottom);
|
ReadBasicSetting(Settings::values.custom_bottom_bottom);
|
||||||
|
ReadBasicSetting(Settings::values.custom_second_layer_opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
@ -1011,6 +1016,7 @@ void Config::SaveLayoutValues() {
|
|||||||
WriteBasicSetting(Settings::values.custom_bottom_top);
|
WriteBasicSetting(Settings::values.custom_bottom_top);
|
||||||
WriteBasicSetting(Settings::values.custom_bottom_right);
|
WriteBasicSetting(Settings::values.custom_bottom_right);
|
||||||
WriteBasicSetting(Settings::values.custom_bottom_bottom);
|
WriteBasicSetting(Settings::values.custom_bottom_bottom);
|
||||||
|
WriteBasicSetting(Settings::values.custom_second_layer_opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
@ -26,7 +26,7 @@ public:
|
|||||||
|
|
||||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||||
static const std::array<UISettings::Shortcut, 24> default_hotkeys;
|
static const std::array<UISettings::Shortcut, 27> default_hotkeys;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Initialize(const std::string& config_name);
|
void Initialize(const std::string& config_name);
|
||||||
|
@ -34,11 +34,11 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
|||||||
ui->emulation_speed_display_label->setMinimumWidth(tr("unthrottled").size() * 6);
|
ui->emulation_speed_display_label->setMinimumWidth(tr("unthrottled").size() * 6);
|
||||||
ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
|
ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||||
ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal());
|
ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||||
|
ui->updateBox->setVisible(UISettings::values.updater_found);
|
||||||
|
|
||||||
SetupPerGameUI();
|
SetupPerGameUI();
|
||||||
SetConfiguration();
|
SetConfiguration();
|
||||||
|
|
||||||
ui->updateBox->setVisible(UISettings::values.updater_found);
|
|
||||||
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
|
connect(ui->button_reset_defaults, &QPushButton::clicked, this,
|
||||||
&ConfigureGeneral::ResetDefaults);
|
&ConfigureGeneral::ResetDefaults);
|
||||||
|
|
||||||
|
@ -3,14 +3,22 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <QFutureWatcher>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
#include <QtConcurrent/QtConcurrentMap>
|
||||||
#include "citra_qt/configuration/configuration_shared.h"
|
#include "citra_qt/configuration/configuration_shared.h"
|
||||||
#include "citra_qt/configuration/configure_system.h"
|
#include "citra_qt/configuration/configure_system.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/am/am.h"
|
||||||
#include "core/hle/service/cfg/cfg.h"
|
#include "core/hle/service/cfg/cfg.h"
|
||||||
#include "core/hle/service/ptm/ptm.h"
|
#include "core/hle/service/ptm/ptm.h"
|
||||||
|
#include "core/hw/aes/key.h"
|
||||||
#include "ui_configure_system.h"
|
#include "ui_configure_system.h"
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
#include "web_service/nus_titles.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static const std::array<int, 12> days_in_month = {{
|
static const std::array<int, 12> days_in_month = {{
|
||||||
31,
|
31,
|
||||||
@ -239,6 +247,8 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
|
|||||||
&ConfigureSystem::UpdateInitTime);
|
&ConfigureSystem::UpdateInitTime);
|
||||||
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
|
||||||
&ConfigureSystem::RefreshConsoleID);
|
&ConfigureSystem::RefreshConsoleID);
|
||||||
|
connect(ui->button_start_download, &QPushButton::clicked, this,
|
||||||
|
&ConfigureSystem::DownloadFromNUS);
|
||||||
for (u8 i = 0; i < country_names.size(); i++) {
|
for (u8 i = 0; i < country_names.size(); i++) {
|
||||||
if (std::strcmp(country_names.at(i), "") != 0) {
|
if (std::strcmp(country_names.at(i), "") != 0) {
|
||||||
ui->combo_country->addItem(tr(country_names.at(i)), i);
|
ui->combo_country->addItem(tr(country_names.at(i)), i);
|
||||||
@ -257,6 +267,30 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
|
|||||||
ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
|
ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal());
|
||||||
|
|
||||||
SetupPerGameUI();
|
SetupPerGameUI();
|
||||||
|
|
||||||
|
ui->combo_download_mode->setCurrentIndex(1); // set to Recommended
|
||||||
|
bool keys_available = true;
|
||||||
|
HW::AES::InitKeys(true);
|
||||||
|
for (u8 i = 0; i < HW::AES::MaxCommonKeySlot; i++) {
|
||||||
|
HW::AES::SelectCommonKeyIndex(i);
|
||||||
|
if (!HW::AES::IsNormalKeyAvailable(HW::AES::KeySlotID::TicketCommonKey)) {
|
||||||
|
keys_available = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keys_available) {
|
||||||
|
ui->button_start_download->setEnabled(true);
|
||||||
|
ui->combo_download_mode->setEnabled(true);
|
||||||
|
ui->label_nus_download->setText(tr("Download System Files from Nintendo servers"));
|
||||||
|
} else {
|
||||||
|
ui->button_start_download->setEnabled(false);
|
||||||
|
ui->combo_download_mode->setEnabled(false);
|
||||||
|
ui->label_nus_download->setText(
|
||||||
|
tr("Citra is missing keys to download system files. <br><a "
|
||||||
|
"href='https://citra-emu.org/wiki/aes-keys/'><span style=\"text-decoration: "
|
||||||
|
"underline; color:#039be5;\">How to get keys?</span></a>"));
|
||||||
|
}
|
||||||
|
|
||||||
ConfigureTime();
|
ConfigureTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,3 +576,44 @@ void ConfigureSystem::SetupPerGameUI() {
|
|||||||
ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds,
|
ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds,
|
||||||
is_new_3ds);
|
is_new_3ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::DownloadFromNUS() {
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
ui->button_start_download->setEnabled(false);
|
||||||
|
|
||||||
|
const auto mode = static_cast<Title::Mode>(ui->combo_download_mode->currentIndex());
|
||||||
|
const std::vector<u64> titles = BuildFirmwareTitleList(mode, cfg->GetRegionValue());
|
||||||
|
|
||||||
|
QProgressDialog progress(tr("Downloading files..."), tr("Cancel"), 0,
|
||||||
|
static_cast<int>(titles.size()), this);
|
||||||
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
|
|
||||||
|
QFutureWatcher<void> future_watcher;
|
||||||
|
QObject::connect(&future_watcher, &QFutureWatcher<void>::finished, &progress,
|
||||||
|
&QProgressDialog::reset);
|
||||||
|
QObject::connect(&progress, &QProgressDialog::canceled, &future_watcher,
|
||||||
|
&QFutureWatcher<void>::cancel);
|
||||||
|
QObject::connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &progress,
|
||||||
|
&QProgressDialog::setValue);
|
||||||
|
|
||||||
|
auto failed = false;
|
||||||
|
const auto download_title = [&future_watcher, &failed](const u64& title_id) {
|
||||||
|
if (Service::AM::InstallFromNus(title_id) != Service::AM::InstallStatus::Success) {
|
||||||
|
failed = true;
|
||||||
|
future_watcher.cancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
future_watcher.setFuture(QtConcurrent::map(titles, download_title));
|
||||||
|
progress.exec();
|
||||||
|
future_watcher.waitForFinished();
|
||||||
|
|
||||||
|
if (failed) {
|
||||||
|
QMessageBox::critical(this, tr("Citra"), tr("Downloading system files failed."));
|
||||||
|
} else if (!future_watcher.isCanceled()) {
|
||||||
|
QMessageBox::information(this, tr("Citra"), tr("Successfully downloaded system files."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->button_start_download->setEnabled(true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -43,6 +43,8 @@ private:
|
|||||||
|
|
||||||
void SetupPerGameUI();
|
void SetupPerGameUI();
|
||||||
|
|
||||||
|
void DownloadFromNUS();
|
||||||
|
|
||||||
ConfigurationShared::CheckState is_new_3ds;
|
ConfigurationShared::CheckState is_new_3ds;
|
||||||
std::unique_ptr<Ui::ConfigureSystem> ui;
|
std::unique_ptr<Ui::ConfigureSystem> ui;
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
|
@ -361,6 +361,52 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="15" column="0">
|
||||||
|
<widget class="QLabel" name="label_nus_download">
|
||||||
|
<property name="text">
|
||||||
|
<string>Download System Files from Nitendo servers</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_nus_download">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="combo_download_mode">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>All</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Recommended</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Minimal</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="button_start_download">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="layoutDirection">
|
||||||
|
<enum>Qt::RightToLeft</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Download</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -501,8 +501,8 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
|
|||||||
QAction* delete_opengl_disk_shader_cache =
|
QAction* delete_opengl_disk_shader_cache =
|
||||||
shader_menu->addAction(tr("Delete OpenGL Shader Cache"));
|
shader_menu->addAction(tr("Delete OpenGL Shader Cache"));
|
||||||
|
|
||||||
const bool is_application =
|
const u32 program_id_high = (program_id >> 32) & 0xFFFFFFFF;
|
||||||
0x0004000000000000 <= program_id && program_id <= 0x00040000FFFFFFFF;
|
const bool is_application = program_id_high == 0x00040000 || program_id_high == 0x00040010;
|
||||||
|
|
||||||
bool opengl_cache_exists = false;
|
bool opengl_cache_exists = false;
|
||||||
ForEachOpenGLCacheFile(
|
ForEachOpenGLCacheFile(
|
||||||
|
@ -113,6 +113,7 @@ __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr int default_mouse_timeout = 2500;
|
constexpr int default_mouse_timeout = 2500;
|
||||||
|
constexpr int num_options_3d = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
|
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
|
||||||
@ -156,6 +157,28 @@ static void InitializeLogging() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString PrettyProductName() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
|
||||||
|
// With that notation change they changed the registry key used to denote the current version
|
||||||
|
QSettings windows_registry(
|
||||||
|
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
|
||||||
|
QSettings::NativeFormat);
|
||||||
|
const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
|
||||||
|
if (release_id == QStringLiteral("2009")) {
|
||||||
|
const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
|
||||||
|
const QString display_version =
|
||||||
|
windows_registry.value(QStringLiteral("DisplayVersion")).toString();
|
||||||
|
const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
|
||||||
|
const u32 version = current_build >= 22000 ? 11 : 10;
|
||||||
|
return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
|
||||||
|
.arg(QString::number(version), display_version, QString::number(current_build),
|
||||||
|
QString::number(ubr));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return QSysInfo::prettyProductName();
|
||||||
|
}
|
||||||
|
|
||||||
GMainWindow::GMainWindow()
|
GMainWindow::GMainWindow()
|
||||||
: ui{std::make_unique<Ui::MainWindow>()}, config{std::make_unique<Config>()}, emu_thread{
|
: ui{std::make_unique<Ui::MainWindow>()}, config{std::make_unique<Config>()}, emu_thread{
|
||||||
nullptr} {
|
nullptr} {
|
||||||
@ -198,6 +221,7 @@ GMainWindow::GMainWindow()
|
|||||||
|
|
||||||
ConnectMenuEvents();
|
ConnectMenuEvents();
|
||||||
ConnectWidgetEvents();
|
ConnectWidgetEvents();
|
||||||
|
Connect3DStateEvents();
|
||||||
|
|
||||||
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
|
LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
|
||||||
Common::g_scm_desc);
|
Common::g_scm_desc);
|
||||||
@ -217,7 +241,7 @@ GMainWindow::GMainWindow()
|
|||||||
}
|
}
|
||||||
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
|
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
|
||||||
#endif
|
#endif
|
||||||
LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
|
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
|
||||||
const auto& mem_info = Common::GetMemInfo();
|
const auto& mem_info = Common::GetMemInfo();
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", mem_info.total_physical_memory / f64{1_GiB});
|
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", mem_info.total_physical_memory / f64{1_GiB});
|
||||||
@ -338,6 +362,20 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
|
|
||||||
statusBar()->insertPermanentWidget(0, graphics_api_button);
|
statusBar()->insertPermanentWidget(0, graphics_api_button);
|
||||||
|
|
||||||
|
option_3d_button = new QPushButton();
|
||||||
|
option_3d_button->setObjectName(QStringLiteral("3DOptionStatusBarButton"));
|
||||||
|
option_3d_button->setFocusPolicy(Qt::NoFocus);
|
||||||
|
option_3d_button->setToolTip(tr("Indicates the current 3D setting. Click to toggle."));
|
||||||
|
|
||||||
|
factor_3d_slider = new QSlider(Qt::Orientation::Horizontal, this);
|
||||||
|
factor_3d_slider->setStyleSheet(QStringLiteral("QSlider { padding: 4px; }"));
|
||||||
|
factor_3d_slider->setToolTip(tr("Current 3D factor while 3D is enabled."));
|
||||||
|
factor_3d_slider->setRange(0, 100);
|
||||||
|
|
||||||
|
Update3DState();
|
||||||
|
statusBar()->insertPermanentWidget(1, option_3d_button);
|
||||||
|
statusBar()->insertPermanentWidget(2, factor_3d_slider);
|
||||||
|
|
||||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText());
|
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText());
|
||||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon());
|
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon());
|
||||||
|
|
||||||
@ -588,6 +626,35 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
});
|
});
|
||||||
connect_shortcut(QStringLiteral("Mute Audio"),
|
connect_shortcut(QStringLiteral("Mute Audio"),
|
||||||
[] { Settings::values.audio_muted = !Settings::values.audio_muted; });
|
[] { Settings::values.audio_muted = !Settings::values.audio_muted; });
|
||||||
|
|
||||||
|
connect_shortcut(QStringLiteral("Toggle 3D"), &GMainWindow::Toggle3D);
|
||||||
|
|
||||||
|
// We use "static" here in order to avoid capturing by lambda due to a MSVC bug, which makes the
|
||||||
|
// variable hold a garbage value after this function exits
|
||||||
|
static constexpr u16 FACTOR_3D_STEP = 5;
|
||||||
|
connect_shortcut(QStringLiteral("Decrease 3D Factor"), [this] {
|
||||||
|
const auto factor_3d = Settings::values.factor_3d.GetValue();
|
||||||
|
if (factor_3d > 0) {
|
||||||
|
if (factor_3d % FACTOR_3D_STEP != 0) {
|
||||||
|
Settings::values.factor_3d = factor_3d - (factor_3d % FACTOR_3D_STEP);
|
||||||
|
} else {
|
||||||
|
Settings::values.factor_3d = factor_3d - FACTOR_3D_STEP;
|
||||||
|
}
|
||||||
|
UpdateStatusBar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect_shortcut(QStringLiteral("Increase 3D Factor"), [this] {
|
||||||
|
const auto factor_3d = Settings::values.factor_3d.GetValue();
|
||||||
|
if (factor_3d < 100) {
|
||||||
|
if (factor_3d % FACTOR_3D_STEP != 0) {
|
||||||
|
Settings::values.factor_3d =
|
||||||
|
factor_3d + FACTOR_3D_STEP - (factor_3d % FACTOR_3D_STEP);
|
||||||
|
} else {
|
||||||
|
Settings::values.factor_3d = factor_3d + FACTOR_3D_STEP;
|
||||||
|
}
|
||||||
|
UpdateStatusBar();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::ShowUpdaterWidgets() {
|
void GMainWindow::ShowUpdaterWidgets() {
|
||||||
@ -818,6 +885,12 @@ void GMainWindow::UpdateMenuState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::Connect3DStateEvents() {
|
||||||
|
connect(option_3d_button, &QPushButton::clicked, this, &GMainWindow::Toggle3D);
|
||||||
|
connect(factor_3d_slider, qOverload<int>(&QSlider::valueChanged), this,
|
||||||
|
[](int value) { Settings::values.factor_3d = value; });
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnDisplayTitleBars(bool show) {
|
void GMainWindow::OnDisplayTitleBars(bool show) {
|
||||||
QList<QDockWidget*> widgets = findChildren<QDockWidget*>();
|
QList<QDockWidget*> widgets = findChildren<QDockWidget*>();
|
||||||
|
|
||||||
@ -1864,6 +1937,7 @@ void GMainWindow::OnLoadState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnConfigure() {
|
void GMainWindow::OnConfigure() {
|
||||||
|
game_list->SetDirectoryWatcherEnabled(false);
|
||||||
Settings::SetConfiguringGlobal(true);
|
Settings::SetConfiguringGlobal(true);
|
||||||
ConfigureDialog configureDialog(this, hotkey_registry,
|
ConfigureDialog configureDialog(this, hotkey_registry,
|
||||||
!multiplayer_state->IsHostingPublicRoom());
|
!multiplayer_state->IsHostingPublicRoom());
|
||||||
@ -1875,6 +1949,7 @@ void GMainWindow::OnConfigure() {
|
|||||||
const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps;
|
const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps;
|
||||||
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
|
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
|
||||||
auto result = configureDialog.exec();
|
auto result = configureDialog.exec();
|
||||||
|
game_list->SetDirectoryWatcherEnabled(true);
|
||||||
if (result == QDialog::Accepted) {
|
if (result == QDialog::Accepted) {
|
||||||
configureDialog.ApplyConfiguration();
|
configureDialog.ApplyConfiguration();
|
||||||
InitializeHotkeys();
|
InitializeHotkeys();
|
||||||
@ -1896,6 +1971,7 @@ void GMainWindow::OnConfigure() {
|
|||||||
}
|
}
|
||||||
UpdateSecondaryWindowVisibility();
|
UpdateSecondaryWindowVisibility();
|
||||||
UpdateAPIIndicator(false);
|
UpdateAPIIndicator(false);
|
||||||
|
Update3DState();
|
||||||
} else {
|
} else {
|
||||||
Settings::values.input_profiles = old_input_profiles;
|
Settings::values.input_profiles = old_input_profiles;
|
||||||
Settings::values.touch_from_button_maps = old_touch_from_button_maps;
|
Settings::values.touch_from_button_maps = old_touch_from_button_maps;
|
||||||
@ -2156,22 +2232,18 @@ void GMainWindow::UpdateStatusBar() {
|
|||||||
const auto play_mode = Core::Movie::GetInstance().GetPlayMode();
|
const auto play_mode = Core::Movie::GetInstance().GetPlayMode();
|
||||||
if (play_mode == Core::Movie::PlayMode::Recording) {
|
if (play_mode == Core::Movie::PlayMode::Recording) {
|
||||||
message_label->setText(tr("Recording %1").arg(current));
|
message_label->setText(tr("Recording %1").arg(current));
|
||||||
message_label->setVisible(true);
|
|
||||||
message_label_used_for_movie = true;
|
message_label_used_for_movie = true;
|
||||||
ui->action_Save_Movie->setEnabled(true);
|
ui->action_Save_Movie->setEnabled(true);
|
||||||
} else if (play_mode == Core::Movie::PlayMode::Playing) {
|
} else if (play_mode == Core::Movie::PlayMode::Playing) {
|
||||||
message_label->setText(tr("Playing %1 / %2").arg(current, total));
|
message_label->setText(tr("Playing %1 / %2").arg(current, total));
|
||||||
message_label->setVisible(true);
|
|
||||||
message_label_used_for_movie = true;
|
message_label_used_for_movie = true;
|
||||||
ui->action_Save_Movie->setEnabled(false);
|
ui->action_Save_Movie->setEnabled(false);
|
||||||
} else if (play_mode == Core::Movie::PlayMode::MovieFinished) {
|
} else if (play_mode == Core::Movie::PlayMode::MovieFinished) {
|
||||||
message_label->setText(tr("Movie Finished"));
|
message_label->setText(tr("Movie Finished"));
|
||||||
message_label->setVisible(true);
|
|
||||||
message_label_used_for_movie = true;
|
message_label_used_for_movie = true;
|
||||||
ui->action_Save_Movie->setEnabled(false);
|
ui->action_Save_Movie->setEnabled(false);
|
||||||
} else if (message_label_used_for_movie) { // Clear the label if movie was just closed
|
} else if (message_label_used_for_movie) { // Clear the label if movie was just closed
|
||||||
message_label->setText(QString{});
|
message_label->setText(QString{});
|
||||||
message_label->setVisible(false);
|
|
||||||
message_label_used_for_movie = false;
|
message_label_used_for_movie = false;
|
||||||
ui->action_Save_Movie->setEnabled(false);
|
ui->action_Save_Movie->setEnabled(false);
|
||||||
}
|
}
|
||||||
@ -2193,6 +2265,18 @@ void GMainWindow::UpdateStatusBar() {
|
|||||||
emu_frametime_label->setVisible(true);
|
emu_frametime_label->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::Update3DState() {
|
||||||
|
static const std::array options_3d = {tr("Off"), tr("Side by Side"), tr("Anaglyph"),
|
||||||
|
tr("Interlaced"), tr("Reverse Interlaced")};
|
||||||
|
|
||||||
|
option_3d_button->setText(
|
||||||
|
tr("3D: %1").arg(options_3d[static_cast<int>(Settings::values.render_3d.GetValue())]));
|
||||||
|
|
||||||
|
factor_3d_slider->setValue(Settings::values.factor_3d.GetValue());
|
||||||
|
factor_3d_slider->setVisible(Settings::values.render_3d.GetValue() !=
|
||||||
|
Settings::StereoRenderOption::Off);
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::HideMouseCursor() {
|
void GMainWindow::HideMouseCursor() {
|
||||||
if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) {
|
if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) {
|
||||||
mouse_hide_timer.stop();
|
mouse_hide_timer.stop();
|
||||||
@ -2315,7 +2399,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
|
|||||||
if (emu_thread) {
|
if (emu_thread) {
|
||||||
emu_thread->SetRunning(true);
|
emu_thread->SetRunning(true);
|
||||||
message_label->setText(status_message);
|
message_label->setText(status_message);
|
||||||
message_label->setVisible(true);
|
|
||||||
message_label_used_for_movie = false;
|
message_label_used_for_movie = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2325,6 +2408,12 @@ void GMainWindow::OnMenuAboutCitra() {
|
|||||||
about.exec();
|
about.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::Toggle3D() {
|
||||||
|
Settings::values.render_3d = static_cast<Settings::StereoRenderOption>(
|
||||||
|
(static_cast<int>(Settings::values.render_3d.GetValue()) + 1) % num_options_3d);
|
||||||
|
Update3DState();
|
||||||
|
}
|
||||||
|
|
||||||
bool GMainWindow::ConfirmClose() {
|
bool GMainWindow::ConfirmClose() {
|
||||||
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
|
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
|
||||||
return true;
|
return true;
|
||||||
@ -2441,6 +2530,8 @@ void GMainWindow::UpdateUITheme() {
|
|||||||
qApp->setStyleSheet(ts.readAll());
|
qApp->setStyleSheet(ts.readAll());
|
||||||
setStyleSheet(ts.readAll());
|
setStyleSheet(ts.readAll());
|
||||||
} else {
|
} else {
|
||||||
|
LOG_ERROR(Frontend,
|
||||||
|
"Unable to open default stylesheet, falling back to empty stylesheet");
|
||||||
qApp->setStyleSheet({});
|
qApp->setStyleSheet({});
|
||||||
setStyleSheet({});
|
setStyleSheet({});
|
||||||
}
|
}
|
||||||
@ -2610,6 +2701,55 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
|
|||||||
#undef main
|
#undef main
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void SetHighDPIAttributes() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
// For Windows, we want to avoid scaling artifacts on fractional scaling ratios.
|
||||||
|
// This is done by setting the optimal scaling policy for the primary screen.
|
||||||
|
|
||||||
|
// Create a temporary QApplication.
|
||||||
|
int temp_argc = 0;
|
||||||
|
char** temp_argv = nullptr;
|
||||||
|
QApplication temp{temp_argc, temp_argv};
|
||||||
|
|
||||||
|
// Get the current screen geometry.
|
||||||
|
const QScreen* primary_screen = QGuiApplication::primaryScreen();
|
||||||
|
if (primary_screen == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRect screen_rect = primary_screen->geometry();
|
||||||
|
const int real_width = screen_rect.width();
|
||||||
|
const int real_height = screen_rect.height();
|
||||||
|
const float real_ratio = primary_screen->logicalDotsPerInch() / 96.0f;
|
||||||
|
|
||||||
|
// Recommended minimum width and height for proper window fit.
|
||||||
|
// Any screen with a lower resolution than this will still have a scale of 1.
|
||||||
|
constexpr float minimum_width = 1350.0f;
|
||||||
|
constexpr float minimum_height = 900.0f;
|
||||||
|
|
||||||
|
const float width_ratio = std::max(1.0f, real_width / minimum_width);
|
||||||
|
const float height_ratio = std::max(1.0f, real_height / minimum_height);
|
||||||
|
|
||||||
|
// Get the lower of the 2 ratios and truncate, this is the maximum integer scale.
|
||||||
|
const float max_ratio = std::trunc(std::min(width_ratio, height_ratio));
|
||||||
|
|
||||||
|
if (max_ratio > real_ratio) {
|
||||||
|
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||||
|
Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
||||||
|
} else {
|
||||||
|
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||||
|
Qt::HighDpiScaleFactorRoundingPolicy::Floor);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Other OSes should be better than Windows at fractional scaling.
|
||||||
|
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||||
|
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
Common::DetachedTasks detached_tasks;
|
Common::DetachedTasks detached_tasks;
|
||||||
MicroProfileOnThreadCreate("Frontend");
|
MicroProfileOnThreadCreate("Frontend");
|
||||||
@ -2619,6 +2759,8 @@ int main(int argc, char* argv[]) {
|
|||||||
QCoreApplication::setOrganizationName(QStringLiteral("Citra team"));
|
QCoreApplication::setOrganizationName(QStringLiteral("Citra team"));
|
||||||
QCoreApplication::setApplicationName(QStringLiteral("Citra"));
|
QCoreApplication::setApplicationName(QStringLiteral("Citra"));
|
||||||
|
|
||||||
|
SetHighDPIAttributes();
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
|
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
|
||||||
chdir(bin_path.c_str());
|
chdir(bin_path.c_str());
|
||||||
|
@ -41,6 +41,8 @@ template <typename>
|
|||||||
class QFutureWatcher;
|
class QFutureWatcher;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QProgressBar;
|
class QProgressBar;
|
||||||
|
class QPushButton;
|
||||||
|
class QSlider;
|
||||||
class RegistersWidget;
|
class RegistersWidget;
|
||||||
class Updater;
|
class Updater;
|
||||||
class WaitTreeWidget;
|
class WaitTreeWidget;
|
||||||
@ -119,6 +121,7 @@ private:
|
|||||||
void RestoreUIState();
|
void RestoreUIState();
|
||||||
|
|
||||||
void ConnectWidgetEvents();
|
void ConnectWidgetEvents();
|
||||||
|
void Connect3DStateEvents();
|
||||||
void ConnectMenuEvents();
|
void ConnectMenuEvents();
|
||||||
void UpdateMenuState();
|
void UpdateMenuState();
|
||||||
|
|
||||||
@ -226,6 +229,7 @@ private slots:
|
|||||||
void OnStopVideoDumping();
|
void OnStopVideoDumping();
|
||||||
#endif
|
#endif
|
||||||
void OnCoreError(Core::System::ResultStatus, std::string);
|
void OnCoreError(Core::System::ResultStatus, std::string);
|
||||||
|
void Toggle3D();
|
||||||
/// Called whenever a user selects Help->About Citra
|
/// Called whenever a user selects Help->About Citra
|
||||||
void OnMenuAboutCitra();
|
void OnMenuAboutCitra();
|
||||||
void OnUpdateFound(bool found, bool error);
|
void OnUpdateFound(bool found, bool error);
|
||||||
@ -237,6 +241,7 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
Q_INVOKABLE void OnMoviePlaybackCompleted();
|
Q_INVOKABLE void OnMoviePlaybackCompleted();
|
||||||
void UpdateStatusBar();
|
void UpdateStatusBar();
|
||||||
|
void Update3DState();
|
||||||
void LoadTranslation();
|
void LoadTranslation();
|
||||||
void UpdateWindowTitle();
|
void UpdateWindowTitle();
|
||||||
void UpdateUISettings();
|
void UpdateUISettings();
|
||||||
@ -262,6 +267,8 @@ private:
|
|||||||
QLabel* game_fps_label = nullptr;
|
QLabel* game_fps_label = nullptr;
|
||||||
QLabel* emu_frametime_label = nullptr;
|
QLabel* emu_frametime_label = nullptr;
|
||||||
QPushButton* graphics_api_button = nullptr;
|
QPushButton* graphics_api_button = nullptr;
|
||||||
|
QPushButton* option_3d_button = nullptr;
|
||||||
|
QSlider* factor_3d_slider = nullptr;
|
||||||
QTimer status_bar_update_timer;
|
QTimer status_bar_update_timer;
|
||||||
bool message_label_used_for_movie = false;
|
bool message_label_used_for_movie = false;
|
||||||
|
|
||||||
|
@ -16,16 +16,19 @@
|
|||||||
// Call directly after the command or use the error num.
|
// Call directly after the command or use the error num.
|
||||||
// This function might change the error code.
|
// This function might change the error code.
|
||||||
std::string GetLastErrorMsg() {
|
std::string GetLastErrorMsg() {
|
||||||
static const std::size_t buff_size = 255;
|
constexpr std::size_t buff_size = 255;
|
||||||
char err_str[buff_size];
|
char err_str[buff_size];
|
||||||
|
std::size_t msg_len;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
msg_len =
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
||||||
#else
|
#else
|
||||||
// Thread safe (XSI-compliant)
|
// Thread safe (XSI-compliant)
|
||||||
strerror_r(errno, err_str, buff_size);
|
strerror_r(errno, err_str, buff_size);
|
||||||
|
msg_len = strnlen(err_str, buff_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return std::string(err_str, buff_size);
|
return std::string(err_str, msg_len);
|
||||||
}
|
}
|
||||||
|
@ -475,6 +475,7 @@ struct Values {
|
|||||||
Setting<u16> custom_bottom_top{240, "custom_bottom_top"};
|
Setting<u16> custom_bottom_top{240, "custom_bottom_top"};
|
||||||
Setting<u16> custom_bottom_right{360, "custom_bottom_right"};
|
Setting<u16> custom_bottom_right{360, "custom_bottom_right"};
|
||||||
Setting<u16> custom_bottom_bottom{480, "custom_bottom_bottom"};
|
Setting<u16> custom_bottom_bottom{480, "custom_bottom_bottom"};
|
||||||
|
Setting<u16> custom_second_layer_opacity{100, "custom_second_layer_opacity"};
|
||||||
|
|
||||||
SwitchableSetting<double> bg_red{0.f, "bg_red"};
|
SwitchableSetting<double> bg_red{0.f, "bg_red"};
|
||||||
SwitchableSetting<double> bg_green{0.f, "bg_green"};
|
SwitchableSetting<double> bg_green{0.f, "bg_green"};
|
||||||
|
@ -106,7 +106,7 @@ ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& pa
|
|||||||
|
|
||||||
FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
|
FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
|
||||||
if (!file.IsOpen()) {
|
if (!file.IsOpen()) {
|
||||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening {}", full_path);
|
LOG_CRITICAL(Service_FS, "Error opening {}: {}", full_path, GetLastErrorMsg());
|
||||||
return ERROR_NOT_FOUND;
|
return ERROR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
constexpr u32 CIA_SECTION_ALIGNMENT = 0x40;
|
|
||||||
|
|
||||||
Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) {
|
Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) {
|
||||||
std::vector<u8> header_data(sizeof(Header));
|
std::vector<u8> header_data(sizeof(Header));
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ constexpr std::size_t CIA_CONTENT_BITS_SIZE = (CIA_CONTENT_MAX_COUNT / 8);
|
|||||||
constexpr std::size_t CIA_HEADER_SIZE = 0x2020;
|
constexpr std::size_t CIA_HEADER_SIZE = 0x2020;
|
||||||
constexpr std::size_t CIA_DEPENDENCY_SIZE = 0x300;
|
constexpr std::size_t CIA_DEPENDENCY_SIZE = 0x300;
|
||||||
constexpr std::size_t CIA_METADATA_SIZE = 0x400;
|
constexpr std::size_t CIA_METADATA_SIZE = 0x400;
|
||||||
|
constexpr u32 CIA_SECTION_ALIGNMENT = 0x40;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper which implements an interface to read and write CTR Installable Archive (CIA) files.
|
* Helper which implements an interface to read and write CTR Installable Archive (CIA) files.
|
||||||
@ -69,7 +70,6 @@ public:
|
|||||||
|
|
||||||
void Print() const;
|
void Print() const;
|
||||||
|
|
||||||
private:
|
|
||||||
struct Header {
|
struct Header {
|
||||||
u32_le header_size;
|
u32_le header_size;
|
||||||
u16_le type;
|
u16_le type;
|
||||||
@ -87,10 +87,14 @@ private:
|
|||||||
// The bits in the content index are arranged w/ index 0 as the MSB, 7 as the LSB, etc.
|
// The bits in the content index are arranged w/ index 0 as the MSB, 7 as the LSB, etc.
|
||||||
return (content_present[index >> 3] & (0x80 >> (index & 7))) != 0;
|
return (content_present[index >> 3] & (0x80 >> (index & 7))) != 0;
|
||||||
}
|
}
|
||||||
|
void SetContentPresent(u16 index) {
|
||||||
|
content_present[index >> 3] |= (0x80 >> (index & 7));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(Header) == CIA_HEADER_SIZE, "CIA Header structure size is wrong");
|
static_assert(sizeof(Header) == CIA_HEADER_SIZE, "CIA Header structure size is wrong");
|
||||||
|
|
||||||
|
private:
|
||||||
struct Metadata {
|
struct Metadata {
|
||||||
std::array<u64_le, 0x30> dependencies;
|
std::array<u64_le, 0x30> dependencies;
|
||||||
std::array<u8, 0x180> reserved;
|
std::array<u8, 0x180> reserved;
|
||||||
|
@ -178,7 +178,7 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height,
|
|||||||
layout_option.GetValue(), Settings::values.upright_screen.GetValue());
|
layout_option.GetValue(), Settings::values.upright_screen.GetValue());
|
||||||
|
|
||||||
if (Settings::values.custom_layout.GetValue() == true) {
|
if (Settings::values.custom_layout.GetValue() == true) {
|
||||||
layout = Layout::CustomFrameLayout(width, height);
|
layout = Layout::CustomFrameLayout(width, height, Settings::values.swap_screen.GetValue());
|
||||||
} else {
|
} else {
|
||||||
width = std::max(width, min_size.first);
|
width = std::max(width, min_size.first);
|
||||||
height = std::max(height, min_size.second);
|
height = std::max(height, min_size.second);
|
||||||
|
@ -349,7 +349,7 @@ FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary
|
|||||||
return SingleFrameLayout(width, height, is_secondary, upright);
|
return SingleFrameLayout(width, height, is_secondary, upright);
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferLayout CustomFrameLayout(u32 width, u32 height) {
|
FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped) {
|
||||||
ASSERT(width > 0);
|
ASSERT(width > 0);
|
||||||
ASSERT(height > 0);
|
ASSERT(height > 0);
|
||||||
|
|
||||||
@ -364,8 +364,13 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height) {
|
|||||||
Settings::values.custom_bottom_right.GetValue(),
|
Settings::values.custom_bottom_right.GetValue(),
|
||||||
Settings::values.custom_bottom_bottom.GetValue()};
|
Settings::values.custom_bottom_bottom.GetValue()};
|
||||||
|
|
||||||
res.top_screen = top_screen;
|
if (is_swapped) {
|
||||||
res.bottom_screen = bot_screen;
|
res.top_screen = bot_screen;
|
||||||
|
res.bottom_screen = top_screen;
|
||||||
|
} else {
|
||||||
|
res.top_screen = top_screen;
|
||||||
|
res.bottom_screen = bot_screen;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +380,8 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar
|
|||||||
layout = CustomFrameLayout(std::max(Settings::values.custom_top_right.GetValue(),
|
layout = CustomFrameLayout(std::max(Settings::values.custom_top_right.GetValue(),
|
||||||
Settings::values.custom_bottom_right.GetValue()),
|
Settings::values.custom_bottom_right.GetValue()),
|
||||||
std::max(Settings::values.custom_top_bottom.GetValue(),
|
std::max(Settings::values.custom_top_bottom.GetValue(),
|
||||||
Settings::values.custom_bottom_bottom.GetValue()));
|
Settings::values.custom_bottom_bottom.GetValue()),
|
||||||
|
Settings::values.swap_screen.GetValue());
|
||||||
} else {
|
} else {
|
||||||
int width, height;
|
int width, height;
|
||||||
switch (Settings::values.layout_option.GetValue()) {
|
switch (Settings::values.layout_option.GetValue()) {
|
||||||
|
@ -114,7 +114,7 @@ FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary
|
|||||||
* @param height Window framebuffer height in pixels
|
* @param height Window framebuffer height in pixels
|
||||||
* @return Newly created FramebufferLayout object with default screen regions initialized
|
* @return Newly created FramebufferLayout object with default screen regions initialized
|
||||||
*/
|
*/
|
||||||
FramebufferLayout CustomFrameLayout(u32 width, u32 height);
|
FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method to get frame layout by resolution scale
|
* Convenience method to get frame layout by resolution scale
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <cryptopp/aes.h>
|
#include <cryptopp/aes.h>
|
||||||
#include <cryptopp/modes.h>
|
#include <cryptopp/modes.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include "common/alignment.h"
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
@ -31,6 +32,9 @@
|
|||||||
#include "core/hle/service/fs/fs_user.h"
|
#include "core/hle/service/fs/fs_user.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/loader/smdh.h"
|
#include "core/loader/smdh.h"
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
#include "web_service/nus_download.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
@ -138,6 +142,8 @@ ResultCode CIAFile::WriteTitleMetadata() {
|
|||||||
decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(),
|
decryption_state->content[i].SetKeyWithIV(title_key->data(), title_key->size(),
|
||||||
ctr.data());
|
ctr.data());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Service_AM, "Can't get title key from ticket");
|
||||||
}
|
}
|
||||||
|
|
||||||
install_state = CIAInstallState::TMDLoaded;
|
install_state = CIAInstallState::TMDLoaded;
|
||||||
@ -180,6 +186,11 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
|
|||||||
buffer + (range_min - offset) + available_to_write);
|
buffer + (range_min - offset) + available_to_write);
|
||||||
|
|
||||||
if ((tmd.GetContentTypeByIndex(i) & FileSys::TMDContentTypeFlag::Encrypted) != 0) {
|
if ((tmd.GetContentTypeByIndex(i) & FileSys::TMDContentTypeFlag::Encrypted) != 0) {
|
||||||
|
if (decryption_state->content.size() <= i) {
|
||||||
|
// TODO: There is probably no correct error to return here. What error should be
|
||||||
|
// returned?
|
||||||
|
return FileSys::ERROR_INSUFFICIENT_SPACE;
|
||||||
|
}
|
||||||
decryption_state->content[i].ProcessData(temp.data(), temp.data(), temp.size());
|
decryption_state->content[i].ProcessData(temp.data(), temp.data(), temp.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +246,7 @@ ResultVal<std::size_t> CIAFile::Write(u64 offset, std::size_t length, bool flush
|
|||||||
std::size_t buf_offset = buf_loaded - offset;
|
std::size_t buf_offset = buf_loaded - offset;
|
||||||
std::size_t buf_copy_size =
|
std::size_t buf_copy_size =
|
||||||
std::min(length, static_cast<std::size_t>(container.GetContentOffset() - offset)) -
|
std::min(length, static_cast<std::size_t>(container.GetContentOffset() - offset)) -
|
||||||
buf_loaded;
|
buf_offset;
|
||||||
std::size_t buf_max_size = std::min(offset + length, container.GetContentOffset());
|
std::size_t buf_max_size = std::min(offset + length, container.GetContentOffset());
|
||||||
data.resize(buf_max_size);
|
data.resize(buf_max_size);
|
||||||
memcpy(data.data() + copy_offset, buffer + buf_offset, buf_copy_size);
|
memcpy(data.data() + copy_offset, buffer + buf_offset, buf_copy_size);
|
||||||
@ -418,6 +429,99 @@ InstallStatus InstallCIA(const std::string& path,
|
|||||||
return InstallStatus::ErrorInvalid;
|
return InstallStatus::ErrorInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstallStatus InstallFromNus(u64 title_id, int version) {
|
||||||
|
#ifdef ENABLE_WEB_SERVICE
|
||||||
|
LOG_DEBUG(Service_AM, "Downloading {:X}", title_id);
|
||||||
|
|
||||||
|
CIAFile install_file{GetTitleMediaType(title_id)};
|
||||||
|
|
||||||
|
std::string path = fmt::format("/ccs/download/{:016X}/tmd", title_id);
|
||||||
|
if (version != -1) {
|
||||||
|
path += fmt::format(".{}", version);
|
||||||
|
}
|
||||||
|
auto tmd_response = WebService::NUS::Download(path);
|
||||||
|
if (!tmd_response) {
|
||||||
|
LOG_ERROR(Service_AM, "Failed to download tmd for {:016X}", title_id);
|
||||||
|
return InstallStatus::ErrorFileNotFound;
|
||||||
|
}
|
||||||
|
FileSys::TitleMetadata tmd;
|
||||||
|
tmd.Load(*tmd_response);
|
||||||
|
|
||||||
|
path = fmt::format("/ccs/download/{:016X}/cetk", title_id);
|
||||||
|
auto cetk_response = WebService::NUS::Download(path);
|
||||||
|
if (!cetk_response) {
|
||||||
|
LOG_ERROR(Service_AM, "Failed to download cetk for {:016X}", title_id);
|
||||||
|
return InstallStatus::ErrorFileNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> content;
|
||||||
|
const auto content_count = tmd.GetContentCount();
|
||||||
|
for (std::size_t i = 0; i < content_count; ++i) {
|
||||||
|
const std::string filename = fmt::format("{:08x}", tmd.GetContentIDByIndex(i));
|
||||||
|
path = fmt::format("/ccs/download/{:016X}/{}", title_id, filename);
|
||||||
|
const auto temp_response = WebService::NUS::Download(path);
|
||||||
|
if (!temp_response) {
|
||||||
|
LOG_ERROR(Service_AM, "Failed to download content for {:016X}", title_id);
|
||||||
|
return InstallStatus::ErrorFileNotFound;
|
||||||
|
}
|
||||||
|
content.insert(content.end(), temp_response->begin(), temp_response->end());
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSys::CIAContainer::Header fake_header{
|
||||||
|
.header_size = sizeof(FileSys::CIAContainer::Header),
|
||||||
|
.type = 0,
|
||||||
|
.version = 0,
|
||||||
|
.cert_size = 0,
|
||||||
|
.tik_size = static_cast<u32_le>(cetk_response->size()),
|
||||||
|
.tmd_size = static_cast<u32_le>(tmd_response->size()),
|
||||||
|
.meta_size = 0,
|
||||||
|
};
|
||||||
|
for (u16 i = 0; i < content_count; ++i) {
|
||||||
|
fake_header.SetContentPresent(i);
|
||||||
|
}
|
||||||
|
std::vector<u8> header_data(sizeof(fake_header));
|
||||||
|
std::memcpy(header_data.data(), &fake_header, sizeof(fake_header));
|
||||||
|
|
||||||
|
std::size_t current_offset = 0;
|
||||||
|
const auto write_to_cia_file_aligned = [&install_file, ¤t_offset](std::vector<u8>& data) {
|
||||||
|
const u64 offset =
|
||||||
|
Common::AlignUp(current_offset + data.size(), FileSys::CIA_SECTION_ALIGNMENT);
|
||||||
|
data.resize(offset - current_offset, 0);
|
||||||
|
const auto result = install_file.Write(current_offset, data.size(), true, data.data());
|
||||||
|
if (result.Failed()) {
|
||||||
|
LOG_ERROR(Service_AM, "CIA file installation aborted with error code {:08x}",
|
||||||
|
result.Code().raw);
|
||||||
|
return InstallStatus::ErrorAborted;
|
||||||
|
}
|
||||||
|
current_offset += data.size();
|
||||||
|
return InstallStatus::Success;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = write_to_cia_file_aligned(header_data);
|
||||||
|
if (result != InstallStatus::Success) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = write_to_cia_file_aligned(*cetk_response);
|
||||||
|
if (result != InstallStatus::Success) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = write_to_cia_file_aligned(*tmd_response);
|
||||||
|
if (result != InstallStatus::Success) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = write_to_cia_file_aligned(content);
|
||||||
|
if (result != InstallStatus::Success) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return InstallStatus::Success;
|
||||||
|
#else
|
||||||
|
return InstallStatus::ErrorFileNotFound;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
Service::FS::MediaType GetTitleMediaType(u64 titleId) {
|
Service::FS::MediaType GetTitleMediaType(u64 titleId) {
|
||||||
u16 platform = static_cast<u16>(titleId >> 48);
|
u16 platform = static_cast<u16>(titleId >> 48);
|
||||||
u16 category = static_cast<u16>((titleId >> 32) & 0xFFFF);
|
u16 category = static_cast<u16>((titleId >> 32) & 0xFFFF);
|
||||||
|
@ -110,6 +110,13 @@ private:
|
|||||||
InstallStatus InstallCIA(const std::string& path,
|
InstallStatus InstallCIA(const std::string& path,
|
||||||
std::function<ProgressCallback>&& update_callback = nullptr);
|
std::function<ProgressCallback>&& update_callback = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads and installs title form the Nintendo Update Service.
|
||||||
|
* @param title_id the title_id to download
|
||||||
|
* @returns whether the install was successful or error code
|
||||||
|
*/
|
||||||
|
InstallStatus InstallFromNus(u64 title_id, int version = -1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the mediatype for an installed title
|
* Get the mediatype for an installed title
|
||||||
* @param titleId the installed title ID
|
* @param titleId the installed title ID
|
||||||
|
@ -283,8 +283,15 @@ ResultVal<AppletManager::InitializeResult> AppletManager::Initialize(AppletId ap
|
|||||||
slot_data->title_id = system.Kernel().GetCurrentProcess()->codeset->program_id;
|
slot_data->title_id = system.Kernel().GetCurrentProcess()->codeset->program_id;
|
||||||
slot_data->attributes.raw = attributes.raw;
|
slot_data->attributes.raw = attributes.raw;
|
||||||
|
|
||||||
if (slot_data->applet_id == AppletId::Application ||
|
const auto* home_menu_slot = GetAppletSlotData(AppletId::HomeMenu);
|
||||||
slot_data->applet_id == AppletId::HomeMenu) {
|
|
||||||
|
// Applications need to receive a Wakeup signal to actually start up, this signal is usually
|
||||||
|
// sent by the Home Menu after starting the app by way of APT::WakeupApplication. In some cases
|
||||||
|
// such as when starting a game directly or the Home Menu itself, we have to send the signal
|
||||||
|
// ourselves since the Home Menu is not running yet. We detect if the Home Menu is running by
|
||||||
|
// checking if there's an applet registered in the HomeMenu slot.
|
||||||
|
if (slot_data->applet_id == AppletId::HomeMenu ||
|
||||||
|
(slot_data->applet_id == AppletId::Application && !home_menu_slot)) {
|
||||||
// Initialize the APT parameter to wake up the application.
|
// Initialize the APT parameter to wake up the application.
|
||||||
next_parameter.emplace();
|
next_parameter.emplace();
|
||||||
next_parameter->signal = SignalType::Wakeup;
|
next_parameter->signal = SignalType::Wakeup;
|
||||||
@ -310,6 +317,12 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) {
|
|||||||
|
|
||||||
slot_data->registered = true;
|
slot_data->registered = true;
|
||||||
|
|
||||||
|
// Send any outstanding parameters to the newly-registered application
|
||||||
|
if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) {
|
||||||
|
CancelAndSendParameter(*delayed_parameter);
|
||||||
|
delayed_parameter.reset();
|
||||||
|
}
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,6 +593,89 @@ ResultCode AppletManager::DoApplicationJump(DeliverArg arg) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) {
|
||||||
|
// TODO(Subv): This should check that the current applet is of System type and return 0xc8a0cc04
|
||||||
|
// if not.
|
||||||
|
|
||||||
|
// TODO(Subv): This should return 0xc8a0cff0 if the applet preparation state is already set
|
||||||
|
|
||||||
|
const auto& application_slot = applet_slots[static_cast<size_t>(AppletSlot::Application)];
|
||||||
|
|
||||||
|
if (application_slot.registered) {
|
||||||
|
// TODO(Subv): Convert this to the enum constructor of ResultCode
|
||||||
|
return ResultCode(0xc8a0cffc);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_MSG(!app_start_parameters,
|
||||||
|
"Trying to prepare an application when another is already prepared");
|
||||||
|
|
||||||
|
app_start_parameters.emplace();
|
||||||
|
app_start_parameters->next_title_id = title_id;
|
||||||
|
app_start_parameters->next_media_type = media_type;
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::StartApplication(const std::vector<u8>& parameter,
|
||||||
|
const std::vector<u8>& hmac, bool paused) {
|
||||||
|
// The delivery argument is always unconditionally set.
|
||||||
|
deliver_arg.emplace(DeliverArg{parameter, hmac});
|
||||||
|
|
||||||
|
// Note: APT first checks if we can launch the application via AM::CheckDemoLaunchRights and
|
||||||
|
// returns 0xc8a12403 if we can't. We intentionally do not implement that check.
|
||||||
|
|
||||||
|
// TODO(Subv): The APT service performs several checks here related to the exheader flags of the
|
||||||
|
// process we're launching and other things like title id blacklists. We do not yet implement
|
||||||
|
// any of that.
|
||||||
|
|
||||||
|
// TODO(Subv): The real APT service doesn't seem to check whether the titleid to launch is set
|
||||||
|
// or not, it either launches NATIVE_FIRM if some internal state is set, or fails when calling
|
||||||
|
// PM::LaunchTitle. We should research more about that.
|
||||||
|
ASSERT_MSG(app_start_parameters, "Trying to start an application without preparing it first.");
|
||||||
|
|
||||||
|
// Launch the title directly.
|
||||||
|
const auto process =
|
||||||
|
NS::LaunchTitle(app_start_parameters->next_media_type, app_start_parameters->next_title_id);
|
||||||
|
if (!process) {
|
||||||
|
LOG_CRITICAL(Service_APT, "Failed to launch title during application start, exiting.");
|
||||||
|
system.RequestShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
app_start_parameters.reset();
|
||||||
|
|
||||||
|
if (!paused) {
|
||||||
|
return WakeupApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 spinwaiting on another thread until the application is
|
||||||
|
// registered.
|
||||||
|
MessageParameter wakeup_parameter{};
|
||||||
|
wakeup_parameter.signal = SignalType::Wakeup;
|
||||||
|
wakeup_parameter.sender_id = AppletId::HomeMenu;
|
||||||
|
wakeup_parameter.destination_id = AppletId::Application;
|
||||||
|
SendApplicationParameterAfterRegistration(wakeup_parameter);
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppletManager::SendApplicationParameterAfterRegistration(const MessageParameter& parameter) {
|
||||||
|
const auto* slot = GetAppletSlotData(AppletId::Application);
|
||||||
|
|
||||||
|
// If the application is already registered, immediately send the parameter
|
||||||
|
if (slot && slot->registered) {
|
||||||
|
CancelAndSendParameter(parameter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise queue it until the Application calls APT::Enable
|
||||||
|
delayed_parameter = parameter;
|
||||||
|
}
|
||||||
|
|
||||||
void AppletManager::EnsureHomeMenuLoaded() {
|
void AppletManager::EnsureHomeMenuLoaded() {
|
||||||
const auto& system_slot = applet_slots[static_cast<size_t>(AppletSlot::SystemApplet)];
|
const auto& system_slot = applet_slots[static_cast<size_t>(AppletSlot::SystemApplet)];
|
||||||
// TODO(Subv): The real APT service sends signal 12 (WakeupByCancel) to the currently running
|
// TODO(Subv): The real APT service sends signal 12 (WakeupByCancel) to the currently running
|
||||||
|
@ -185,6 +185,11 @@ public:
|
|||||||
deliver_arg = std::move(arg);
|
deliver_arg = std::move(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type);
|
||||||
|
ResultCode StartApplication(const std::vector<u8>& parameter, const std::vector<u8>& hmac,
|
||||||
|
bool paused);
|
||||||
|
ResultCode WakeupApplication();
|
||||||
|
|
||||||
struct AppletInfo {
|
struct AppletInfo {
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
Service::FS::MediaType media_type;
|
Service::FS::MediaType media_type;
|
||||||
@ -221,11 +226,28 @@ public:
|
|||||||
return app_jump_parameters;
|
return app_jump_parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ApplicationStartParameters {
|
||||||
|
u64 next_title_id;
|
||||||
|
FS::MediaType next_media_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
|
ar& next_title_id;
|
||||||
|
ar& next_media_type;
|
||||||
|
}
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
|
/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
|
||||||
// NOTE: A bug in gcc prevents serializing std::optional
|
// NOTE: A bug in gcc prevents serializing std::optional
|
||||||
boost::optional<MessageParameter> next_parameter;
|
boost::optional<MessageParameter> next_parameter;
|
||||||
|
|
||||||
|
/// This parameter will be sent to the application/applet once they register themselves by using
|
||||||
|
/// APT::Initialize.
|
||||||
|
boost::optional<MessageParameter> delayed_parameter;
|
||||||
|
|
||||||
static constexpr std::size_t NumAppletSlot = 4;
|
static constexpr std::size_t NumAppletSlot = 4;
|
||||||
|
|
||||||
enum class AppletSlot : u8 {
|
enum class AppletSlot : u8 {
|
||||||
@ -271,6 +293,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
ApplicationJumpParameters app_jump_parameters{};
|
ApplicationJumpParameters app_jump_parameters{};
|
||||||
|
boost::optional<ApplicationStartParameters> app_start_parameters{};
|
||||||
boost::optional<DeliverArg> deliver_arg{};
|
boost::optional<DeliverArg> deliver_arg{};
|
||||||
|
|
||||||
// Holds data about the concurrently running applets in the system.
|
// Holds data about the concurrently running applets in the system.
|
||||||
@ -280,6 +303,10 @@ private:
|
|||||||
AppletSlotData* GetAppletSlotData(AppletId id);
|
AppletSlotData* GetAppletSlotData(AppletId id);
|
||||||
AppletSlotData* GetAppletSlotData(AppletAttributes attributes);
|
AppletSlotData* GetAppletSlotData(AppletAttributes attributes);
|
||||||
|
|
||||||
|
/// Checks if the Application slot has already been registered and sends the parameter to it,
|
||||||
|
/// otherwise it queues for sending when the application registers itself with APT::Enable.
|
||||||
|
void SendApplicationParameterAfterRegistration(const MessageParameter& parameter);
|
||||||
|
|
||||||
void EnsureHomeMenuLoaded();
|
void EnsureHomeMenuLoaded();
|
||||||
|
|
||||||
// Command that will be sent to the application when a library applet calls CloseLibraryApplet.
|
// Command that will be sent to the application when a library applet calls CloseLibraryApplet.
|
||||||
@ -293,6 +320,8 @@ private:
|
|||||||
ar& next_parameter;
|
ar& next_parameter;
|
||||||
ar& app_jump_parameters;
|
ar& app_jump_parameters;
|
||||||
if (file_version > 0) {
|
if (file_version > 0) {
|
||||||
|
ar& delayed_parameter;
|
||||||
|
ar& app_start_parameters;
|
||||||
ar& deliver_arg;
|
ar& deliver_arg;
|
||||||
}
|
}
|
||||||
ar& applet_slots;
|
ar& applet_slots;
|
||||||
|
@ -540,39 +540,40 @@ void Module::APTInterface::ReceiveDeliverArg(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void Module::APTInterface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) {
|
void Module::APTInterface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140
|
IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140
|
||||||
u32 title_info1 = rp.Pop<u32>();
|
const u64 title_id = rp.Pop<u64>();
|
||||||
u32 title_info2 = rp.Pop<u32>();
|
const auto media_type = rp.PopEnum<FS::MediaType>();
|
||||||
u32 title_info3 = rp.Pop<u32>();
|
rp.Skip(1, false); // Padding
|
||||||
u32 title_info4 = rp.Pop<u32>();
|
const u32 flags = rp.Pop<u32>();
|
||||||
u32 flags = rp.Pop<u32>();
|
|
||||||
|
|
||||||
if (flags & 0x00000100) {
|
|
||||||
apt->unknown_ns_state_field = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
rb.Push(apt->applet_manager->PrepareToStartApplication(title_id, media_type));
|
||||||
|
|
||||||
LOG_WARNING(Service_APT,
|
LOG_INFO(Service_APT, "called title_id={:#010X} media_type={} flags={:#010X}", title_id,
|
||||||
"(STUBBED) called title_info1={:#010X}, title_info2={:#010X}, title_info3={:#010X},"
|
media_type, flags);
|
||||||
"title_info4={:#010X}, flags={:#010X}",
|
|
||||||
title_info1, title_info2, title_info3, title_info4, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::APTInterface::StartApplication(Kernel::HLERequestContext& ctx) {
|
void Module::APTInterface::StartApplication(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x1B, 3, 4); // 0x001B00C4
|
IPC::RequestParser rp(ctx, 0x1B, 3, 4); // 0x001B00C4
|
||||||
const auto buffer1_size = rp.Pop<u32>();
|
const u32 parameter_size = rp.Pop<u32>();
|
||||||
const auto buffer2_size = rp.Pop<u32>();
|
const u32 hmac_size = rp.Pop<u32>();
|
||||||
const auto flag = rp.Pop<u32>();
|
const bool paused = rp.Pop<bool>();
|
||||||
[[maybe_unused]] const std::vector<u8> buffer1 = rp.PopStaticBuffer();
|
const std::vector<u8> parameter = rp.PopStaticBuffer();
|
||||||
[[maybe_unused]] const std::vector<u8> buffer2 = rp.PopStaticBuffer();
|
const std::vector<u8> hmac = rp.PopStaticBuffer();
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
rb.Push(apt->applet_manager->StartApplication(parameter, hmac, paused));
|
||||||
|
|
||||||
LOG_WARNING(Service_APT,
|
LOG_INFO(Service_APT, "called parameter_size={:#010X}, hmac_size={:#010X}, paused={}",
|
||||||
"(STUBBED) called buffer1_size={:#010X}, buffer2_size={:#010X}, flag={:#010X}",
|
parameter_size, hmac_size, paused);
|
||||||
buffer1_size, buffer2_size, flag);
|
}
|
||||||
|
|
||||||
|
void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x1C, 0, 0); // 0x001C0000
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(apt->applet_manager->WakeupApplication());
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_APT, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) {
|
void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -361,6 +361,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
void StartApplication(Kernel::HLERequestContext& ctx);
|
void StartApplication(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APT::WakeupApplication service function.
|
||||||
|
* Inputs:
|
||||||
|
* 0 : Command header [0x001C0000]
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return Header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void WakeupApplication(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APT::AppletUtility service function
|
* APT::AppletUtility service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
|
@ -36,8 +36,8 @@ APT_A::APT_A(std::shared_ptr<Module> apt)
|
|||||||
{0x00180040, &APT_A::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
|
{0x00180040, &APT_A::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
|
||||||
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
|
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
|
||||||
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
|
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
|
||||||
{0x001B00C4, nullptr, "StartApplication"},
|
{0x001B00C4, &APT_A::StartApplication, "StartApplication"},
|
||||||
{0x001C0000, nullptr, "WakeupApplication"},
|
{0x001C0000, &APT_A::WakeupApplication, "WakeupApplication"},
|
||||||
{0x001D0000, nullptr, "CancelApplication"},
|
{0x001D0000, nullptr, "CancelApplication"},
|
||||||
{0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"},
|
{0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"},
|
||||||
{0x001F0084, nullptr, "StartSystemApplet"},
|
{0x001F0084, nullptr, "StartSystemApplet"},
|
||||||
|
@ -36,8 +36,8 @@ APT_S::APT_S(std::shared_ptr<Module> apt)
|
|||||||
{0x00180040, &APT_S::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
|
{0x00180040, &APT_S::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
|
||||||
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
|
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
|
||||||
{0x001A0000, &APT_S::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
|
{0x001A0000, &APT_S::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"},
|
||||||
{0x001B00C4, nullptr, "StartApplication"},
|
{0x001B00C4, &APT_S::StartApplication, "StartApplication"},
|
||||||
{0x001C0000, nullptr, "WakeupApplication"},
|
{0x001C0000, &APT_S::WakeupApplication, "WakeupApplication"},
|
||||||
{0x001D0000, nullptr, "CancelApplication"},
|
{0x001D0000, nullptr, "CancelApplication"},
|
||||||
{0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"},
|
{0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"},
|
||||||
{0x001F0084, nullptr, "StartSystemApplet"},
|
{0x001F0084, nullptr, "StartSystemApplet"},
|
||||||
|
@ -36,8 +36,8 @@ APT_U::APT_U(std::shared_ptr<Module> apt)
|
|||||||
{0x00180040, &APT_U::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
|
{0x00180040, &APT_U::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
|
||||||
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
|
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
|
||||||
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
|
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
|
||||||
{0x001B00C4, nullptr, "StartApplication"},
|
{0x001B00C4, &APT_U::StartApplication, "StartApplication"},
|
||||||
{0x001C0000, nullptr, "WakeupApplication"},
|
{0x001C0000, &APT_U::WakeupApplication, "WakeupApplication"},
|
||||||
{0x001D0000, nullptr, "CancelApplication"},
|
{0x001D0000, nullptr, "CancelApplication"},
|
||||||
{0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"},
|
{0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"},
|
||||||
{0x001F0084, nullptr, "StartSystemApplet"},
|
{0x001F0084, nullptr, "StartSystemApplet"},
|
||||||
|
@ -153,6 +153,20 @@ struct ConsoleCountryInfo {
|
|||||||
u8 country_code; ///< The country code of the console
|
u8 country_code; ///< The country code of the console
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
|
static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
|
||||||
|
|
||||||
|
struct BacklightControls {
|
||||||
|
u8 power_saving_enabled; ///< Whether power saving mode is enabled.
|
||||||
|
u8 brightness_level; ///< The configured brightness level.
|
||||||
|
};
|
||||||
|
static_assert(sizeof(BacklightControls) == 2, "BacklightControls must be exactly 2 bytes");
|
||||||
|
|
||||||
|
struct New3dsBacklightControls {
|
||||||
|
u8 unknown_1[4]; ///< Unknown data
|
||||||
|
u8 auto_brightness_enabled; ///< Whether auto brightness is enabled.
|
||||||
|
u8 unknown_2[3]; ///< Unknown data
|
||||||
|
};
|
||||||
|
static_assert(sizeof(New3dsBacklightControls) == 8,
|
||||||
|
"New3dsBacklightControls must be exactly 8 bytes");
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
constexpr EULAVersion MAX_EULA_VERSION{0x7F, 0x7F};
|
constexpr EULAVersion MAX_EULA_VERSION{0x7F, 0x7F};
|
||||||
@ -166,6 +180,8 @@ constexpr u8 UNITED_STATES_COUNTRY_ID = 49;
|
|||||||
constexpr u8 WASHINGTON_DC_STATE_ID = 2;
|
constexpr u8 WASHINGTON_DC_STATE_ID = 2;
|
||||||
/// TODO(Subv): Find what the other bytes are
|
/// TODO(Subv): Find what the other bytes are
|
||||||
constexpr ConsoleCountryInfo COUNTRY_INFO{{0, 0}, WASHINGTON_DC_STATE_ID, UNITED_STATES_COUNTRY_ID};
|
constexpr ConsoleCountryInfo COUNTRY_INFO{{0, 0}, WASHINGTON_DC_STATE_ID, UNITED_STATES_COUNTRY_ID};
|
||||||
|
constexpr BacklightControls BACKLIGHT_CONTROLS{0, 2};
|
||||||
|
constexpr New3dsBacklightControls NEW_3DS_BACKLIGHT_CONTROLS{{0, 0, 0, 0}, 0, {0, 0, 0}};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games,
|
* TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games,
|
||||||
@ -508,11 +524,23 @@ ResultCode Module::FormatConfig() {
|
|||||||
if (!res.IsSuccess())
|
if (!res.IsSuccess())
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
// 0x00050001 - Backlight controls
|
||||||
|
res = CreateConfigInfoBlk(BacklightControlsBlockID, sizeof(BACKLIGHT_CONTROLS), 0xC,
|
||||||
|
&BACKLIGHT_CONTROLS);
|
||||||
|
if (!res.IsSuccess())
|
||||||
|
return res;
|
||||||
|
|
||||||
res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE,
|
res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE,
|
||||||
STEREO_CAMERA_SETTINGS.data());
|
STEREO_CAMERA_SETTINGS.data());
|
||||||
if (!res.IsSuccess())
|
if (!res.IsSuccess())
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
// 0x00050009 - New 3DS backlight controls
|
||||||
|
res = CreateConfigInfoBlk(BacklightControlNew3dsBlockID, sizeof(NEW_3DS_BACKLIGHT_CONTROLS),
|
||||||
|
0xC, &NEW_3DS_BACKLIGHT_CONTROLS);
|
||||||
|
if (!res.IsSuccess())
|
||||||
|
return res;
|
||||||
|
|
||||||
res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE,
|
res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE,
|
||||||
&SOUND_OUTPUT_MODE);
|
&SOUND_OUTPUT_MODE);
|
||||||
if (!res.IsSuccess())
|
if (!res.IsSuccess())
|
||||||
|
@ -88,7 +88,7 @@ struct KeySlot {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::array<KeySlot, KeySlotID::MaxKeySlotID> key_slots;
|
std::array<KeySlot, KeySlotID::MaxKeySlotID> key_slots;
|
||||||
std::array<std::optional<AESKey>, 6> common_key_y_slots;
|
std::array<std::optional<AESKey>, MaxCommonKeySlot> common_key_y_slots;
|
||||||
|
|
||||||
enum class FirmwareType : u32 {
|
enum class FirmwareType : u32 {
|
||||||
ARM9 = 0, // uses NDMA
|
ARM9 = 0, // uses NDMA
|
||||||
@ -494,9 +494,9 @@ void LoadPresetKeys() {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void InitKeys() {
|
void InitKeys(bool force) {
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
if (initialized)
|
if (initialized && !force)
|
||||||
return;
|
return;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
HW::RSA::InitSlots();
|
HW::RSA::InitSlots();
|
||||||
|
@ -48,11 +48,13 @@ enum KeySlotID : std::size_t {
|
|||||||
MaxKeySlotID = 0x40,
|
MaxKeySlotID = 0x40,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr std::size_t MaxCommonKeySlot = 6;
|
||||||
|
|
||||||
constexpr std::size_t AES_BLOCK_SIZE = 16;
|
constexpr std::size_t AES_BLOCK_SIZE = 16;
|
||||||
|
|
||||||
using AESKey = std::array<u8, AES_BLOCK_SIZE>;
|
using AESKey = std::array<u8, AES_BLOCK_SIZE>;
|
||||||
|
|
||||||
void InitKeys();
|
void InitKeys(bool force = false);
|
||||||
|
|
||||||
void SetGeneratorConstant(const AESKey& key);
|
void SetGeneratorConstant(const AESKey& key);
|
||||||
void SetKeyX(std::size_t slot_id, const AESKey& key);
|
void SetKeyX(std::size_t slot_id, const AESKey& key);
|
||||||
|
@ -876,134 +876,161 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
|||||||
}
|
}
|
||||||
|
|
||||||
glUniform1i(uniform_layer, 0);
|
glUniform1i(uniform_layer, 0);
|
||||||
if (layout.top_screen_enabled) {
|
if (!Settings::values.swap_screen) {
|
||||||
if (layout.is_rotated) {
|
DrawTopScreen(layout, top_screen, stereo_single_screen);
|
||||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
glUniform1i(uniform_layer, 0);
|
||||||
int eye = static_cast<int>(Settings::values.mono_render_option.GetValue());
|
ApplySecondLayerOpacity();
|
||||||
DrawSingleScreenRotated(screen_infos[eye], (float)top_screen.left,
|
DrawBottomScreen(layout, bottom_screen, stereo_single_screen);
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth(),
|
} else {
|
||||||
(float)top_screen.GetHeight());
|
DrawBottomScreen(layout, bottom_screen, stereo_single_screen);
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
glUniform1i(uniform_layer, 0);
|
||||||
Settings::StereoRenderOption::SideBySide) {
|
ApplySecondLayerOpacity();
|
||||||
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
|
DrawTopScreen(layout, top_screen, stereo_single_screen);
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
}
|
||||||
(float)top_screen.GetHeight());
|
state.blend.enabled = false;
|
||||||
glUniform1i(uniform_layer, 1);
|
}
|
||||||
DrawSingleScreenRotated(screen_infos[1],
|
|
||||||
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
void RendererOpenGL::ApplySecondLayerOpacity() {
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
if (Settings::values.custom_layout &&
|
||||||
(float)top_screen.GetHeight());
|
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
state.blend.enabled = true;
|
||||||
Settings::StereoRenderOption::CardboardVR) {
|
state.blend.src_rgb_func = GL_CONSTANT_ALPHA;
|
||||||
DrawSingleScreenRotated(screen_infos[0], layout.top_screen.left,
|
state.blend.src_a_func = GL_CONSTANT_ALPHA;
|
||||||
layout.top_screen.top, layout.top_screen.GetWidth(),
|
state.blend.dst_a_func = GL_ONE_MINUS_CONSTANT_ALPHA;
|
||||||
layout.top_screen.GetHeight());
|
state.blend.dst_rgb_func = GL_ONE_MINUS_CONSTANT_ALPHA;
|
||||||
glUniform1i(uniform_layer, 1);
|
state.blend.color.alpha = Settings::values.custom_second_layer_opacity.GetValue() / 100.0f;
|
||||||
DrawSingleScreenRotated(screen_infos[1],
|
}
|
||||||
layout.cardboard.top_screen_right_eye +
|
}
|
||||||
((float)layout.width / 2),
|
|
||||||
layout.top_screen.top, layout.top_screen.GetWidth(),
|
void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout,
|
||||||
layout.top_screen.GetHeight());
|
const Common::Rectangle<u32>& top_screen,
|
||||||
} else if (stereo_single_screen) {
|
const bool stereo_single_screen) {
|
||||||
DrawSingleScreenStereoRotated(
|
if (!layout.top_screen_enabled) {
|
||||||
screen_infos[0], screen_infos[1], (float)top_screen.left, (float)top_screen.top,
|
return;
|
||||||
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
|
}
|
||||||
}
|
|
||||||
} else {
|
if (layout.is_rotated) {
|
||||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
||||||
int eye = static_cast<int>(Settings::values.mono_render_option.GetValue());
|
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top,
|
||||||
DrawSingleScreen(screen_infos[eye], (float)top_screen.left, (float)top_screen.top,
|
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
|
||||||
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
Settings::StereoRenderOption::SideBySide) {
|
||||||
Settings::StereoRenderOption::SideBySide) {
|
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
|
||||||
DrawSingleScreen(screen_infos[0], (float)top_screen.left / 2, (float)top_screen.top,
|
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||||
(float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight());
|
(float)top_screen.GetHeight());
|
||||||
glUniform1i(uniform_layer, 1);
|
glUniform1i(uniform_layer, 1);
|
||||||
DrawSingleScreen(screen_infos[1],
|
DrawSingleScreenRotated(screen_infos[1],
|
||||||
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||||
(float)top_screen.GetHeight());
|
(float)top_screen.GetHeight());
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
Settings::StereoRenderOption::CardboardVR) {
|
Settings::StereoRenderOption::CardboardVR) {
|
||||||
DrawSingleScreen(screen_infos[0], layout.top_screen.left, layout.top_screen.top,
|
DrawSingleScreenRotated(screen_infos[0], layout.top_screen.left, layout.top_screen.top,
|
||||||
layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
|
layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
|
||||||
glUniform1i(uniform_layer, 1);
|
glUniform1i(uniform_layer, 1);
|
||||||
DrawSingleScreen(screen_infos[1],
|
DrawSingleScreenRotated(
|
||||||
layout.cardboard.top_screen_right_eye + ((float)layout.width / 2),
|
screen_infos[1], layout.cardboard.top_screen_right_eye + ((float)layout.width / 2),
|
||||||
layout.top_screen.top, layout.top_screen.GetWidth(),
|
layout.top_screen.top, layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
|
||||||
layout.top_screen.GetHeight());
|
} else if (stereo_single_screen) {
|
||||||
} else if (stereo_single_screen) {
|
DrawSingleScreenStereoRotated(screen_infos[0], screen_infos[1], (float)top_screen.left,
|
||||||
DrawSingleScreenStereo(screen_infos[0], screen_infos[1], (float)top_screen.left,
|
(float)top_screen.top, (float)top_screen.GetWidth(),
|
||||||
(float)top_screen.top, (float)top_screen.GetWidth(),
|
(float)top_screen.GetHeight());
|
||||||
(float)top_screen.GetHeight());
|
}
|
||||||
}
|
} else {
|
||||||
|
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
||||||
|
DrawSingleScreen(screen_infos[0], (float)top_screen.left, (float)top_screen.top,
|
||||||
|
(float)top_screen.GetWidth(), (float)top_screen.GetHeight());
|
||||||
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
|
Settings::StereoRenderOption::SideBySide) {
|
||||||
|
DrawSingleScreen(screen_infos[0], (float)top_screen.left / 2, (float)top_screen.top,
|
||||||
|
(float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight());
|
||||||
|
glUniform1i(uniform_layer, 1);
|
||||||
|
DrawSingleScreen(screen_infos[1],
|
||||||
|
((float)top_screen.left / 2) + ((float)layout.width / 2),
|
||||||
|
(float)top_screen.top, (float)top_screen.GetWidth() / 2,
|
||||||
|
(float)top_screen.GetHeight());
|
||||||
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
|
Settings::StereoRenderOption::CardboardVR) {
|
||||||
|
DrawSingleScreen(screen_infos[0], layout.top_screen.left, layout.top_screen.top,
|
||||||
|
layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
|
||||||
|
glUniform1i(uniform_layer, 1);
|
||||||
|
DrawSingleScreen(
|
||||||
|
screen_infos[1], layout.cardboard.top_screen_right_eye + ((float)layout.width / 2),
|
||||||
|
layout.top_screen.top, layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
|
||||||
|
} else if (stereo_single_screen) {
|
||||||
|
DrawSingleScreenStereo(screen_infos[0], screen_infos[1], (float)top_screen.left,
|
||||||
|
(float)top_screen.top, (float)top_screen.GetWidth(),
|
||||||
|
(float)top_screen.GetHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glUniform1i(uniform_layer, 0);
|
}
|
||||||
if (layout.bottom_screen_enabled) {
|
|
||||||
if (layout.is_rotated) {
|
void RendererOpenGL::DrawBottomScreen(const Layout::FramebufferLayout& layout,
|
||||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
const Common::Rectangle<u32>& bottom_screen,
|
||||||
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left,
|
const bool stereo_single_screen) {
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
if (!layout.bottom_screen_enabled) {
|
||||||
(float)bottom_screen.GetHeight());
|
return;
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
}
|
||||||
Settings::StereoRenderOption::SideBySide) {
|
|
||||||
DrawSingleScreenRotated(
|
if (layout.is_rotated) {
|
||||||
screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top,
|
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
||||||
(float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight());
|
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left,
|
||||||
glUniform1i(uniform_layer, 1);
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
||||||
DrawSingleScreenRotated(
|
(float)bottom_screen.GetHeight());
|
||||||
screen_infos[2], ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
Settings::StereoRenderOption::SideBySide) {
|
||||||
(float)bottom_screen.GetHeight());
|
DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left / 2,
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||||
Settings::StereoRenderOption::CardboardVR) {
|
(float)bottom_screen.GetHeight());
|
||||||
DrawSingleScreenRotated(screen_infos[2], layout.bottom_screen.left,
|
glUniform1i(uniform_layer, 1);
|
||||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
DrawSingleScreenRotated(screen_infos[2],
|
||||||
layout.bottom_screen.GetHeight());
|
((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
||||||
glUniform1i(uniform_layer, 1);
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||||
DrawSingleScreenRotated(screen_infos[2],
|
(float)bottom_screen.GetHeight());
|
||||||
layout.cardboard.bottom_screen_right_eye +
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
((float)layout.width / 2),
|
Settings::StereoRenderOption::CardboardVR) {
|
||||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
DrawSingleScreenRotated(screen_infos[2], layout.bottom_screen.left,
|
||||||
layout.bottom_screen.GetHeight());
|
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
||||||
} else if (stereo_single_screen) {
|
layout.bottom_screen.GetHeight());
|
||||||
DrawSingleScreenStereoRotated(screen_infos[2], screen_infos[2],
|
glUniform1i(uniform_layer, 1);
|
||||||
(float)bottom_screen.left, (float)bottom_screen.top,
|
DrawSingleScreenRotated(screen_infos[2],
|
||||||
(float)bottom_screen.GetWidth(),
|
layout.cardboard.bottom_screen_right_eye +
|
||||||
(float)bottom_screen.GetHeight());
|
((float)layout.width / 2),
|
||||||
}
|
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
||||||
} else {
|
layout.bottom_screen.GetHeight());
|
||||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
} else if (stereo_single_screen) {
|
||||||
DrawSingleScreen(screen_infos[2], (float)bottom_screen.left,
|
DrawSingleScreenStereoRotated(screen_infos[2], screen_infos[2],
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
(float)bottom_screen.left, (float)bottom_screen.top,
|
||||||
(float)bottom_screen.GetHeight());
|
(float)bottom_screen.GetWidth(),
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
(float)bottom_screen.GetHeight());
|
||||||
Settings::StereoRenderOption::SideBySide) {
|
}
|
||||||
DrawSingleScreen(screen_infos[2], (float)bottom_screen.left / 2,
|
} else {
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
|
||||||
(float)bottom_screen.GetHeight());
|
DrawSingleScreen(screen_infos[2], (float)bottom_screen.left, (float)bottom_screen.top,
|
||||||
glUniform1i(uniform_layer, 1);
|
(float)bottom_screen.GetWidth(), (float)bottom_screen.GetHeight());
|
||||||
DrawSingleScreen(screen_infos[2],
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
Settings::StereoRenderOption::SideBySide) {
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
DrawSingleScreen(screen_infos[2], (float)bottom_screen.left / 2,
|
||||||
(float)bottom_screen.GetHeight());
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||||
} else if (Settings::values.render_3d.GetValue() ==
|
(float)bottom_screen.GetHeight());
|
||||||
Settings::StereoRenderOption::CardboardVR) {
|
glUniform1i(uniform_layer, 1);
|
||||||
DrawSingleScreen(screen_infos[2], layout.bottom_screen.left,
|
DrawSingleScreen(screen_infos[2],
|
||||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
((float)bottom_screen.left / 2) + ((float)layout.width / 2),
|
||||||
layout.bottom_screen.GetHeight());
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
|
||||||
glUniform1i(uniform_layer, 1);
|
(float)bottom_screen.GetHeight());
|
||||||
DrawSingleScreen(screen_infos[2],
|
} else if (Settings::values.render_3d.GetValue() ==
|
||||||
layout.cardboard.bottom_screen_right_eye +
|
Settings::StereoRenderOption::CardboardVR) {
|
||||||
((float)layout.width / 2),
|
DrawSingleScreen(screen_infos[2], layout.bottom_screen.left, layout.bottom_screen.top,
|
||||||
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
layout.bottom_screen.GetWidth(), layout.bottom_screen.GetHeight());
|
||||||
layout.bottom_screen.GetHeight());
|
glUniform1i(uniform_layer, 1);
|
||||||
} else if (stereo_single_screen) {
|
DrawSingleScreen(screen_infos[2],
|
||||||
DrawSingleScreenStereo(screen_infos[2], screen_infos[2], (float)bottom_screen.left,
|
layout.cardboard.bottom_screen_right_eye + ((float)layout.width / 2),
|
||||||
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
|
||||||
(float)bottom_screen.GetHeight());
|
layout.bottom_screen.GetHeight());
|
||||||
}
|
} else if (stereo_single_screen) {
|
||||||
|
DrawSingleScreenStereo(screen_infos[2], screen_infos[2], (float)bottom_screen.left,
|
||||||
|
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
|
||||||
|
(float)bottom_screen.GetHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,12 @@ private:
|
|||||||
* Draws the emulated screens to the emulator window.
|
* Draws the emulated screens to the emulator window.
|
||||||
*/
|
*/
|
||||||
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
|
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
|
||||||
|
void ApplySecondLayerOpacity();
|
||||||
|
void DrawBottomScreen(const Layout::FramebufferLayout& layout,
|
||||||
|
const Common::Rectangle<u32>& bottom_screen,
|
||||||
|
const bool stereo_single_screen);
|
||||||
|
void DrawTopScreen(const Layout::FramebufferLayout& layout,
|
||||||
|
const Common::Rectangle<u32>& top_screen, const bool stereo_single_screen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a single texture to the emulator window.
|
* Draws a single texture to the emulator window.
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
add_library(web_service STATIC
|
add_library(web_service STATIC
|
||||||
announce_room_json.cpp
|
announce_room_json.cpp
|
||||||
announce_room_json.h
|
announce_room_json.h
|
||||||
|
nus_download.cpp
|
||||||
|
nus_download.h
|
||||||
|
nus_titles.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
telemetry_json.cpp
|
telemetry_json.cpp
|
||||||
telemetry_json.h
|
telemetry_json.h
|
||||||
|
47
src/web_service/nus_download.cpp
Normal file
47
src/web_service/nus_download.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2020 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <httplib.h>
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "web_service/nus_download.h"
|
||||||
|
|
||||||
|
namespace WebService::NUS {
|
||||||
|
|
||||||
|
std::optional<std::vector<u8>> Download(const std::string& path) {
|
||||||
|
constexpr auto HOST = "http://nus.cdn.c.shop.nintendowifi.net";
|
||||||
|
|
||||||
|
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(HOST);
|
||||||
|
if (client == nullptr) {
|
||||||
|
LOG_ERROR(WebService, "Invalid URL {}{}", HOST, path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
httplib::Request request{
|
||||||
|
.method = "GET",
|
||||||
|
.path = path,
|
||||||
|
};
|
||||||
|
|
||||||
|
client->set_follow_location(true);
|
||||||
|
const auto result = client->send(request);
|
||||||
|
if (!result) {
|
||||||
|
LOG_ERROR(WebService, "GET to {}{} returned null", HOST, path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& response = result.value();
|
||||||
|
if (response.status >= 400) {
|
||||||
|
LOG_ERROR(WebService, "GET to {}{} returned error status code: {}", HOST, path,
|
||||||
|
response.status);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (!response.headers.contains("content-type")) {
|
||||||
|
LOG_ERROR(WebService, "GET to {}{} returned no content", HOST, path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vector<u8>(response.body.begin(), response.body.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace WebService::NUS
|
15
src/web_service/nus_download.h
Normal file
15
src/web_service/nus_download.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2020 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace WebService::NUS {
|
||||||
|
|
||||||
|
std::optional<std::vector<u8>> Download(const std::string& path);
|
||||||
|
|
||||||
|
}
|
761
src/web_service/nus_titles.h
Normal file
761
src/web_service/nus_titles.h
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
constexpr u32 SYSTEM_FIRMWARE_UPPER_TITLE_ID = 0x00040138;
|
||||||
|
constexpr u32 SYSTEM_APPLICATION_UPPER_TITLE_ID = 0x00040010;
|
||||||
|
constexpr u32 SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID = 0x0004001B;
|
||||||
|
constexpr u32 SYSTEM_APPLET_UPPER_TITLE_ID = 0x00040030;
|
||||||
|
constexpr u32 SHARED_DATA_ARCHIVE_UPPER_TITLE_ID = 0x0004009B;
|
||||||
|
constexpr u32 SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID = 0x000400DB;
|
||||||
|
constexpr u32 SYSTEM_MODULE_UPPER_TITLE_ID = 0x00040130;
|
||||||
|
|
||||||
|
struct Title {
|
||||||
|
enum Mode { All, Recommended, Minimal };
|
||||||
|
std::string name;
|
||||||
|
u32 upper_title_id;
|
||||||
|
std::array<u32, 6> lower_title_id;
|
||||||
|
Mode mode = Mode::All;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::array<Title, 9> SYSTEM_FIRMWARE = {
|
||||||
|
{{"Safe Mode Native Firmware",
|
||||||
|
SYSTEM_FIRMWARE_UPPER_TITLE_ID,
|
||||||
|
{{0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003}},
|
||||||
|
Title::Mode::Minimal},
|
||||||
|
{"New_3DS Safe Mode Native Firmware",
|
||||||
|
SYSTEM_FIRMWARE_UPPER_TITLE_ID,
|
||||||
|
{{0x20000003, 0x20000003, 0x20000003, 0x20000003, 0x20000003, 0x20000003}},
|
||||||
|
Title::Mode::Minimal},
|
||||||
|
{"Native Firmware",
|
||||||
|
SYSTEM_FIRMWARE_UPPER_TITLE_ID,
|
||||||
|
{{0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002}},
|
||||||
|
Title::Mode::Minimal},
|
||||||
|
{"New_3DS Native Firmware",
|
||||||
|
SYSTEM_FIRMWARE_UPPER_TITLE_ID,
|
||||||
|
{{0x20000002, 0x20000002, 0x20000002, 0x20000002, 0x20000002, 0x20000002}},
|
||||||
|
Title::Mode::Minimal}}};
|
||||||
|
static const std::array<Title, 17> SYSTEM_APPLICATIONS = {
|
||||||
|
{{"System Settings",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020000, 0x00021000, 0x00022000, 0x00026000, 0x00027000, 0x00028000}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Download Play",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020100, 0x00021100, 0x00022100, 0x00026100, 0x00027100, 0x00028100}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"Activity Log",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020200, 0x00021200, 0x00022200, 0x00026200, 0x00027200, 0x00028200}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Health and Safety Information",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020300, 0x00021300, 0x00022300, 0x00026300, 0x00027300, 0x00028300}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Health and Safety Information",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x20020300, 0x20021300, 0x20022300, 0x0, 0x20027300, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Nintendo 3DS Camera",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020400, 0x00021400, 0x00022400, 0x00026400, 0x00027400, 0x00028400}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Nintendo 3DS Sound",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020500, 0x00021500, 0x00022500, 0x00026500, 0x00027500, 0x00028500}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Mii Maker",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020700, 0x00021700, 0x00022700, 0x00026700, 0x00027700, 0x00028700}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"StreetPass Mii Plaza",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020800, 0x00021800, 0x00022800, 0x00026800, 0x00027800, 0x00028800}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"eShop",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020900, 0x00021900, 0x00022900, 0x0, 0x00027900, 0x00028900}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"System Transfer",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020A00, 0x00021A00, 0x00022A00, 0x0, 0x00027A00, 0x00028A00}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Nintendo Zone",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020B00, 0x00021B00, 0x00022B00, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Face Raiders",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020D00, 0x00021D00, 0x00022D00, 0x00026D00, 0x00027D00, 0x00028D00}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Face Raiders",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x20020D00, 0x20021D00, 0x20022D00, 0x0, 0x20027D00, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"AR Games",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x00020E00, 0x00021E00, 0x00022E00, 0x00026E00, 0x00027E00, 0x00028E00}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Nintendo Network ID Settings",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x0002BF00, 0x0002C000, 0x0002C100, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"microSD Management",
|
||||||
|
SYSTEM_APPLICATION_UPPER_TITLE_ID,
|
||||||
|
{{0x20023100, 0x20024100, 0x20025100, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All}}};
|
||||||
|
|
||||||
|
static const std::array<Title, 7> SYSTEM_DATA_ARCHIVES = {
|
||||||
|
{{"ClCertA",
|
||||||
|
SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00010002, 0x00010002, 0x00010002, 0x00010002, 0x00010002, 0x00010002}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"NS CFA",
|
||||||
|
SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00010702, 0x00010702, 0x00010702, 0x00010702, 0x00010702, 0x00010702}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"dummy.txt",
|
||||||
|
SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00010802, 0x00010802, 0x00010802, 0x00010802, 0x00010802, 0x00010802}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"CFA web-browser data",
|
||||||
|
SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00018002, 0x00018002, 0x00018002, 0x00018002, 0x00018002, 0x00018002}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"local web-browser data",
|
||||||
|
SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00018102, 0x00018102, 0x00018102, 0x00018102, 0x00018102, 0x00018102}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"webkit/OSS CROs",
|
||||||
|
SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00018202, 0x00018202, 0x00018202, 0x00018202, 0x00018202, 0x00018202}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Fangate_updater",
|
||||||
|
SYSTEM_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00019002, 0x00019002, 0x00019002, 0x00019002, 0x00019002, 0x00019002}},
|
||||||
|
Title::Mode::All}}};
|
||||||
|
|
||||||
|
static const std::array<Title, 27> SYSTEM_APPLETS = {
|
||||||
|
{{"Home Menu",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008202, 0x00008F02, 0x00009802, 0x0000A102, 0x0000A902, 0x0000B102}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Camera applet",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008402, 0x00009002, 0x00009902, 0x0000A202, 0x0000AA02, 0x0000B202}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Instruction Manual",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008602, 0x00009202, 0x00009B02, 0x0000A402, 0x0000AC02, 0x0000B402}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"Game Notes",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008702, 0x00009302, 0x00009C02, 0x0000A502, 0x0000AD02, 0x0000B502}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Internet Browser",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008802, 0x00009402, 0x00009D02, 0x0000A602, 0x0000AE02, 0x0000B602}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New 3DS Internet Browser",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x20008802, 0x20009402, 0x20009D02, 0x0, 0x2000AE02, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Fatal error viewer",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008A02, 0x00008A02, 0x00008A02, 0x00008A02, 0x00008A02, 0x00008A02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode Fatal error viewer",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008A03, 0x00008A03, 0x00008A03, 0x00008A03, 0x00008A03, 0x00008A03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New 3DS Safe Mode Fatal error viewer",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x20008A03, 0x20008A03, 0x20008A03, 0x0, 0x20008A03, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Friend List",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008D02, 0x00009602, 0x00009F02, 0x0000A702, 0x0000AF02, 0x0000B702}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"Notifications",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008E02, 0x000009702, 0x0000A002, 0x0000A802, 0x0000B002, 0x0000B802}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Software Keyboard",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00000C002, 0x0000C802, 0x0000D002, 0x0000D802, 0x0000DE02, 0x0000E402}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"Safe Mode Software Keyboard",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00000C003, 0x0000C803, 0x0000D003, 0x0000D803, 0x0000DE03, 0x0000E403}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New 3DS Safe Mode Software Keyboard",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x2000C003, 0x2000C803, 0x2000D003, 0x0, 0x2000DE03, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Mii picker",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000C102, 0x0000C902, 0x0000D102, 0x0000D902, 0x0000DF02, 0x0000E502}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"Picture picker",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000C302, 0x0000CB02, 0x0000D302, 0x0000DB02, 0x0000E102, 0x0000E702}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Voice memo picker",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000C402, 0x0000CC02, 0x0000D402, 0x0000DC02, 0x0000E202, 0x0000E802}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Error display",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000C502, 0x0000C502, 0x0000C502, 0x0000CF02, 0x0000CF02, 0x0000CF02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe mode error display",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000C503, 0x0000C503, 0x0000C503, 0x0000CF03, 0x0000CF03, 0x0000CF03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New 3DS safe mode error display",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x2000C503, 0x2000C503, 0x2000C503, 0x0, 0x2000CF03, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Circle Pad Pro test/calibration applet",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000CD02, 0x0000CD02, 0x0000CD02, 0x0000D502, 0x0000D502, 0x0000D502}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"eShop applet",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000C602, 0x0000CE02, 0x0000D602, 0x0, 0x0000E302, 0x0000E902}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"Miiverse",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000BC02, 0x0000BC02, 0x0000BC02, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Miiverse system library",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x0000F602, 0x0000F602, 0x0000F602, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Miiverse-posting applet",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00008302, 0x00008B02, 0x0000BA02, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Amiibo Settings",
|
||||||
|
SYSTEM_APPLET_UPPER_TITLE_ID,
|
||||||
|
{{0x00009502, 0x00009E02, 0x0000B902, 0x0, 0x00008C02, 0x0000BF02}},
|
||||||
|
Title::Mode::All}}};
|
||||||
|
|
||||||
|
static const std::array<Title, 25> SHARED_DATA_ARCHIVES = {
|
||||||
|
{{"CFL_Res.dat",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00010202, 0x00010202, 0x00010202, 0x00010202, 0x00010202, 0x00010202}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Region Manifest",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00010402, 0x00010402, 0x00010402, 0x00010402, 0x00010402, 0x00010402}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Non-Nintendo TLS Root-CA Certificates",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00010602, 0x00010602, 0x00010602, 0x00010602, 0x00010602, 0x00010602}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"CHN/CN Dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x0, 0x00011002, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"TWN/TN dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x0, 0x0, 0x0, 0x00011102}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"NL/NL dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x00011202, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"EN/GB dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x00011302, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"EN/US dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x00011402, 0x0, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"FR/FR/regular dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x00011502, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"FR/CA/regular dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x00011602, 0x0, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"DE/regular dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x00011702, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"IT/IT dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x00011802, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"JA_small/32 dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00011902, 0x0, 0x0, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"KO/KO dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x0, 0x0, 0x00011A02, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"PT/PT/regular dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x00011B02, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"RU/regular dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x0, 0x00011C02, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"ES/ES dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x00011D02, 0x00011D02, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"PT/BR/regular dictionary",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x0, 0x00011E02, 0x0, 0x0, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"error strings",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00012202, 0x00012302, 0x00012102, 0x00012402, 0x00012502, 0x00012602}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"eula",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00013202, 0x00013302, 0x00013102, 0x00013502, 0x0, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"JPN/EUR/USA System Font",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00014002, 0x00014002, 0x00014002, 0x00014002, 0x00014002, 0x00014002}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"CHN System Font",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00014102, 0x00014102, 0x00014102, 0x00014102, 0x00014102, 0x00014102}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"KOR System Font",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00014202, 0x00014202, 0x00014202, 0x00014202, 0x00014202, 0x00014202}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"TWN System Font",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00014302, 0x00014302, 0x00014302, 0x00014302, 0x00014302, 0x00014302}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"rate",
|
||||||
|
SHARED_DATA_ARCHIVE_UPPER_TITLE_ID,
|
||||||
|
{{0x00015202, 0x00015302, 0x00015102, 0x0, 0x0015502, 0x00015602}},
|
||||||
|
Title::Mode::All}}};
|
||||||
|
|
||||||
|
static const std::array<Title, 5> SYSTEM_DATA_ARCHIVES_2 = {
|
||||||
|
{{"bad word list",
|
||||||
|
SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID,
|
||||||
|
{{0x00010302, 0x00010302, 0x00010302, 0x00010302, 0x00010302, 0x00010302}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Nintendo Zone hotspot list",
|
||||||
|
SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID,
|
||||||
|
{{0x00010502, 0x00010502, 0x00010502, 0x00010502, 0x00010502, 0x00010502}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"NVer",
|
||||||
|
SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID,
|
||||||
|
{{0x00016102, 0x00016202, 0x00016302, 0x00016402, 0x00016502, 0x00016602}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS NVer",
|
||||||
|
SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID,
|
||||||
|
{{0x20016102, 0x20016202, 0x20016302, 0x0, 0x20016502, 0x0}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"CVer",
|
||||||
|
SYSTEM_DATA_ARCHIVE_2_UPPER_TITLE_ID,
|
||||||
|
{{0x00017102, 0x00017202, 0x00017302, 0x00017402, 0x00017502, 0x00017602}},
|
||||||
|
Title::Mode::All}}};
|
||||||
|
|
||||||
|
static const std::array<Title, 100> SYSTEM_MODULES = {
|
||||||
|
{{"AM ( Application Manager )",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001502, 0x00001502, 0x00001502, 0x00001502, 0x00001502, 0x00001502}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode AM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001503, 0x00001503, 0x00001503, 0x00001503, 0x00001503, 0x00001503}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode AM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001503, 0x20001503, 0x20001503, 0x20001503, 0x20001503, 0x20001503}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Camera",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001602, 0x00001602, 0x00001602, 0x00001602, 0x00001602, 0x00001602}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Camera",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001602, 0x20001602, 0x20001602, 0x20001602, 0x20001602, 0x20001602}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Config (cfg)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001702, 0x00001702, 0x00001702, 0x00001702, 0x00001702, 0x00001702}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode Config (cfg)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001703, 0x00001703, 0x00001703, 0x00001703, 0x00001703, 0x00001703}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode Config (cfg)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001703, 0x20001703, 0x20001703, 0x20001703, 0x20001703, 0x20001703}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Codec",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001802, 0x00001802, 0x00001802, 0x00001802, 0x00001802, 0x00001802}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode Codec",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001803, 0x00001803, 0x00001803, 0x00001803, 0x00001803, 0x00001803}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode Codec",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001803, 0x20001803, 0x20001803, 0x20001803, 0x20001803, 0x20001803}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"DSP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001A02, 0x00001A02, 0x00001A02, 0x00001A02, 0x00001A02, 0x00001A02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode DSP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001A03, 0x00001A03, 0x00001A03, 0x00001A03, 0x00001A03, 0x00001A03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode DSP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001A03, 0x20001A03, 0x20001A03, 0x20001A03, 0x20001A03, 0x20001A03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"GPIO",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001B02, 0x00001B02, 0x00001B02, 0x00001B02, 0x00001B02, 0x00001B02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode GPIO",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001B03, 0x00001B03, 0x00001B03, 0x00001B03, 0x00001B03, 0x00001B03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode GPIO",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001B03, 0x20001B03, 0x20001B03, 0x20001B03, 0x20001B03, 0x20001B03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"GSP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001C02, 0x00001C02, 0x00001C02, 0x00001C02, 0x00001C02, 0x00001C02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS GSP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001C02, 0x20001C02, 0x20001C02, 0x20001C02, 0x20001C02, 0x20001C02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode GSP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001C03, 0x00001C03, 0x00001C03, 0x00001C03, 0x00001C03, 0x00001C03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode GSP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001C03, 0x20001C03, 0x20001C03, 0x20001C03, 0x20001C03, 0x20001C03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"HID (Human Interface Devices)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001D02, 0x00001D02, 0x00001D02, 0x00001D02, 0x00001D02, 0x00001D02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode HID",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001D03, 0x00001D03, 0x00001D03, 0x00001D03, 0x00001D03, 0x00001D03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode HID",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001D03, 0x20001D03, 0x20001D03, 0x20001D03, 0x20001D03, 0x20001D03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"i2c",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001E02, 0x00001E02, 0x00001E02, 0x00001E02, 0x00001E02, 0x00001E02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS i2c",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001E02, 0x20001E02, 0x20001E02, 0x20001E02, 0x20001E02, 0x20001E02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode i2c",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001E03, 0x00001E03, 0x00001E03, 0x00001E03, 0x00001E03, 0x00001E03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode i2c",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001E03, 0x20001E03, 0x20001E03, 0x20001E03, 0x20001E03, 0x20001E03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"MCU",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001F02, 0x00001F02, 0x00001F02, 0x00001F02, 0x00001F02, 0x00001F02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS MCU",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001F02, 0x20001F02, 0x20001F02, 0x20001F02, 0x20001F02, 0x20001F02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode MCU",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00001F03, 0x00001F03, 0x00001F03, 0x00001F03, 0x00001F03, 0x00001F03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode MCU",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20001F03, 0x20001F03, 0x20001F03, 0x20001F03, 0x20001F03, 0x20001F03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"MIC (Microphone)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"PDN",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002102, 0x00002102, 0x00002102, 0x00002102, 0x00002102, 0x00002102}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode PDN",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002103, 0x00002103, 0x00002103, 0x00002103, 0x00002103, 0x00002103}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode PDN",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002103, 0x20002103, 0x20002103, 0x20002103, 0x20002103, 0x20002103}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"PTM (Play time, pedometer, and battery manager)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002202, 0x00002202, 0x00002202, 0x00002202, 0x00002202, 0x00002202}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS PTM (Play time, pedometer, and battery manager)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002202, 0x20002202, 0x20002202, 0x20002202, 0x20002202, 0x20002202}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode PTM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002203, 0x00002203, 0x00002203, 0x00002203, 0x00002203, 0x00002203}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode PTM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002203, 0x20002203, 0x20002203, 0x20002203, 0x20002203, 0x20002203}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"spi",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002302, 0x00002302, 0x00002302, 0x00002302, 0x00002302, 0x00002302}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS spi",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002302, 0x20002302, 0x20002302, 0x20002302, 0x20002302, 0x20002302}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode spi",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002303, 0x00002303, 0x00002303, 0x00002303, 0x00002303, 0x00002303}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode spi",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002303, 0x20002303, 0x20002303, 0x20002303, 0x20002303, 0x20002303}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"AC (Network manager)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002402, 0x00002402, 0x00002402, 0x00002402, 0x00002402, 0x00002402}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode AC",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002403, 0x00002403, 0x00002403, 0x00002403, 0x00002403, 0x00002403}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode AC",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002403, 0x20002403, 0x20002403, 0x20002403, 0x20002403, 0x20002403}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Cecd (StreetPass)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002602, 0x00002602, 0x00002602, 0x00002602, 0x00002602, 0x00002602}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"CSND",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002702, 0x00002702, 0x00002702, 0x00002702, 0x00002702, 0x00002702}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode CSND",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002703, 0x00002703, 0x00002703, 0x00002703, 0x00002703, 0x00002703}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode CSND",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002703, 0x20002703, 0x20002703, 0x20002703, 0x20002703, 0x20002703}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"DLP (Download Play)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002802, 0x00002802, 0x00002802, 0x00002802, 0x00002802, 0x00002802}},
|
||||||
|
Title::Mode::Recommended},
|
||||||
|
{"HTTP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002902, 0x00002902, 0x00002902, 0x00002902, 0x00002902, 0x00002902}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode HTTP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002903, 0x00002903, 0x00002903, 0x00002903, 0x00002903, 0x00002903}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode HTTP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002903, 0x20002903, 0x20002903, 0x20002903, 0x20002903, 0x20002903}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"MP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002A02, 0x00002A02, 0x00002A02, 0x00002A02, 0x00002A02, 0x00002A02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode MP",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002A03, 0x00002A03, 0x00002A03, 0x00002A03, 0x00002A03, 0x00002A03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"NDM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002B02, 0x00002B02, 0x00002B02, 0x00002B02, 0x00002B02, 0x00002B02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"NIM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002C02, 0x00002C02, 0x00002C02, 0x00002C02, 0x00002C02, 0x00002C02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode NIM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002C03, 0x00002C03, 0x00002C03, 0x00002C03, 0x00002C03, 0x00002C03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode NIM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002C03, 0x20002C03, 0x20002C03, 0x20002C03, 0x20002C03, 0x20002C03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"NWM ( Low-level wifi manager )",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002D02, 0x00002D02, 0x00002D02, 0x00002D02, 0x00002D02, 0x00002D02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode NWM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002D03, 0x00002D03, 0x00002D03, 0x00002D03, 0x00002D03, 0x00002D03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode NWM",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002D03, 0x20002D03, 0x20002D03, 0x20002D03, 0x20002D03, 0x20002D03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Sockets",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002E02, 0x00002E02, 0x00002E02, 0x00002E02, 0x00002E02, 0x00002E02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode Sockets",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002E03, 0x00002E03, 0x00002E03, 0x00002E03, 0x00002E03, 0x00002E03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode Sockets",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002E03, 0x20002E03, 0x20002E03, 0x20002E03, 0x20002E03, 0x20002E03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"SSL",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002F02, 0x00002F02, 0x00002F02, 0x00002F02, 0x00002F02, 0x00002F02}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode SSL",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00002F03, 0x00002F03, 0x00002F03, 0x00002F03, 0x00002F03, 0x00002F03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode SSL",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20002F03, 0x20002F03, 0x20002F03, 0x20002F03, 0x20002F03, 0x20002F03}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"PS ( Process Manager )",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003102, 0x00003102, 0x00003102, 0x00003102, 0x00003102, 0x00003102}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode PS",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003103, 0x00003103, 0x00003103, 0x00003103, 0x00003103, 0x00003103}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode PS",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20003103, 0x20003103, 0x20003103, 0x20003103, 0x20003103, 0x20003103}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"friends (Friends list)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003202, 0x00003202, 0x00003202, 0x00003202, 0x00003202, 0x00003202}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode friends (Friends list)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003203, 0x00003203, 0x00003203, 0x00003203, 0x00003203, 0x00003203}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode friends (Friends list)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20003203, 0x20003203, 0x20003203, 0x20003203, 0x20003203, 0x20003203}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"IR (Infrared)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003302, 0x00003302, 0x00003302, 0x00003302, 0x00003302, 0x00003302}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode IR",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003303, 0x00003303, 0x00003303, 0x00003303, 0x00003303, 0x00003303}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode IR",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20003303, 0x20003303, 0x20003303, 0x20003303, 0x20003303, 0x20003303}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"BOSS (SpotPass)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003402, 0x00003402, 0x00003402, 0x00003402, 0x00003402, 0x00003402}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"News (Notifications)",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003502, 0x00003502, 0x00003502, 0x00003502, 0x00003502, 0x00003502}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"RO",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003702, 0x00003702, 0x00003702, 0x00003702, 0x00003702, 0x00003702}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"act",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00003802, 0x00003802, 0x00003802, 0x00003802, 0x00003802, 0x00003802}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"nfc",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00004002, 0x00004002, 0x00004002, 0x00004002, 0x00004002, 0x00004002}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS mvd",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20004102, 0x20004102, 0x20004102, 0x20004102, 0x20004102, 0x20004102}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS qtm",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20004202, 0x20004202, 0x20004202, 0x20004202, 0x20004202, 0x20004202}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"NS",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00008002, 0x00008002, 0x00008002, 0x00008002, 0x00008002, 0x00008002}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"Safe Mode NS",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x00008003, 0x00008003, 0x00008003, 0x00008003, 0x00008003, 0x00008003}},
|
||||||
|
Title::Mode::All},
|
||||||
|
{"New_3DS Safe Mode NS",
|
||||||
|
SYSTEM_MODULE_UPPER_TITLE_ID,
|
||||||
|
{{0x20008003, 0x20008003, 0x20008003, 0x20008003, 0x20008003, 0x20008003}},
|
||||||
|
Title::Mode::All}}};
|
||||||
|
|
||||||
|
std::vector<u64> BuildFirmwareTitleList(const Title::Mode& mode, u32 region) {
|
||||||
|
// Since Australia and Europe share the same title,
|
||||||
|
// offset down by one for Australia and above.
|
||||||
|
const u32 region_index = region >= 3 ? region - 1 : region;
|
||||||
|
|
||||||
|
const auto titles_with_mode = [mode, region_index](const Title& title) {
|
||||||
|
return mode <= title.mode && title.lower_title_id[region_index] != 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Title> titles;
|
||||||
|
const auto inserter = std::back_inserter(titles);
|
||||||
|
std::copy_if(SYSTEM_FIRMWARE.begin(), SYSTEM_FIRMWARE.end(), inserter, titles_with_mode);
|
||||||
|
std::copy_if(SYSTEM_APPLICATIONS.begin(), SYSTEM_APPLICATIONS.end(), inserter,
|
||||||
|
titles_with_mode);
|
||||||
|
std::copy_if(SYSTEM_DATA_ARCHIVES.begin(), SYSTEM_DATA_ARCHIVES.end(), inserter,
|
||||||
|
titles_with_mode);
|
||||||
|
std::copy_if(SYSTEM_APPLETS.begin(), SYSTEM_APPLETS.end(), inserter, titles_with_mode);
|
||||||
|
std::copy_if(SHARED_DATA_ARCHIVES.begin(), SHARED_DATA_ARCHIVES.end(), inserter,
|
||||||
|
titles_with_mode);
|
||||||
|
std::copy_if(SYSTEM_DATA_ARCHIVES_2.begin(), SYSTEM_DATA_ARCHIVES_2.end(), inserter,
|
||||||
|
titles_with_mode);
|
||||||
|
std::copy_if(SYSTEM_MODULES.begin(), SYSTEM_MODULES.end(), inserter, titles_with_mode);
|
||||||
|
|
||||||
|
const auto get_title_id = [region_index](const Title& title) {
|
||||||
|
return (static_cast<u64>(title.upper_title_id) << 32) +
|
||||||
|
static_cast<u64>(title.lower_title_id[region_index]);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<u64> title_ids;
|
||||||
|
std::transform(titles.begin(), titles.end(), std::back_inserter(title_ids), get_title_id);
|
||||||
|
return title_ids;
|
||||||
|
}
|
Reference in New Issue
Block a user