Compare commits

..

8 Commits

Author SHA1 Message Date
Marshall Mohror
8626140b73 Improve path splitting speed
And create a range closure so the components can be iterated without allocating a container.
2023-06-09 21:30:48 -05:00
liamwhite
b3e2c9f9f1 Merge pull request #10623 from german77/backup
service: nfc: Add backup support
2023-06-08 21:54:12 -04:00
liamwhite
2a1acbfb4d Merge pull request #10666 from liamwhite/my-framerate-is-fine
nvnflinger: allow locking framerate during video playback
2023-06-08 21:53:57 -04:00
liamwhite
a57150afbd Merge pull request #10676 from bunnei/fix-mi-5-android
android: EmulationActivity: Fix orientation on Mi Pad 5.
2023-06-08 21:53:51 -04:00
liamwhite
60cc611f38 Merge pull request #10677 from tokarevart/fix-warning
Fix a potentially uninitialized local variable warning causing a build error
2023-06-08 21:53:40 -04:00
Tokarev Artem
007c3fa7df Fix potentially uninitialized local variable warning 2023-06-09 05:12:22 +05:00
Liam
6c34adb1de nvnflinger: allow locking framerate during video playback 2023-06-08 01:15:51 -04:00
german77
107aa52cdb service: nfc: Add backup support 2023-06-06 17:06:21 -06:00
26 changed files with 278 additions and 91 deletions

View File

@@ -279,6 +279,7 @@ find_package(LLVM MODULE COMPONENTS Demangle)
find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3 MODULE)
find_package(range-v3 REQUIRED)
find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED)

View File

@@ -47,12 +47,4 @@ AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() {
return *adsp;
}
void AudioCore::SetNVDECActive(bool active) {
nvdec_active = active;
}
bool AudioCore::IsNVDECActive() const {
return nvdec_active;
}
} // namespace AudioCore

View File

@@ -57,18 +57,6 @@ public:
*/
AudioRenderer::ADSP::ADSP& GetADSP();
/**
* Toggle NVDEC state, used to avoid stall in playback.
*
* @param active - Set true if nvdec is active, otherwise false.
*/
void SetNVDECActive(bool active);
/**
* Get NVDEC state.
*/
bool IsNVDECActive() const;
private:
/**
* Create the sinks on startup.
@@ -83,8 +71,6 @@ private:
std::unique_ptr<Sink::Sink> input_sink;
/// The ADSP in the sysmodule
std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp;
/// Is NVDec currently active?
bool nvdec_active{false};
};
} // namespace AudioCore

View File

@@ -10,6 +10,7 @@
// Sub-directories contained within a yuzu data directory
#define AMIIBO_DIR "amiibo"
#define CACHE_DIR "cache"
#define CONFIG_DIR "config"
#define DUMP_DIR "dump"

View File

@@ -114,6 +114,7 @@ public:
#endif
GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
@@ -349,17 +350,11 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
}
std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string copy(filename);
std::replace(copy.begin(), copy.end(), '\\', '/');
std::vector<std::string> out;
std::stringstream stream(copy);
std::string item;
while (std::getline(stream, item, '/')) {
out.push_back(std::move(item));
std::vector<std::string> copied_components;
for (std::string_view component : filename | split_path_components_view) {
copied_components.emplace_back(component);
}
return out;
return copied_components;
}
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {

View File

@@ -6,12 +6,17 @@
#include <filesystem>
#include <vector>
#include <range/v3/view/remove_if.hpp>
#include <range/v3/view/split_when.hpp>
#include <range/v3/view/transform.hpp>
#include "common/fs/fs_util.h"
namespace Common::FS {
enum class YuzuPath {
YuzuDir, // Where yuzu stores its data.
AmiiboDir, // Where Amiibo backups are stored.
CacheDir, // Where cached filesystem data is stored.
ConfigDir, // Where config files are stored.
DumpDir, // Where dumped data is stored.
@@ -283,6 +288,23 @@ enum class DirectorySeparator {
PlatformDefault,
};
// A range adaptor closure which splits the path on '/' or '\'
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
constexpr auto split_path_components_view =
// Somehow split_when never made it into the standard
ranges::views::split_when([](auto cur, auto end) {
const char c = *cur;
bool do_split = c == '\\' || c == '/';
return std::make_pair(do_split, cur + (do_split ? 1 : 0));
}) |
// Convert from opaque range to std::string_view
ranges::views::transform([](auto&& chunk) {
return std::string_view{&*ranges::begin(chunk),
static_cast<std::size_t>(ranges::distance(chunk))};
}) |
// Skip any empty segments like at the beginning and end of "/root/directory/"
ranges::views::remove_if(&std::string_view::empty);
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);

View File

@@ -235,6 +235,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
values.enable_compute_pipelines.SetGlobal(true);
values.use_video_framerate.SetGlobal(true);
// System
values.language_index.SetGlobal(true);

View File

@@ -482,6 +482,7 @@ struct Values {
SwitchableSetting<AstcRecompression, true> astc_recompression{
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
"astc_recompression"};
SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
SwitchableSetting<u8> bg_red{0, "bg_red"};
SwitchableSetting<u8> bg_green{0, "bg_green"};

View File

@@ -48,7 +48,7 @@ std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
}
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
std::array<u8, 0x10> uuid;
std::array<u8, 0x10> uuid{};
size_t i = 0;

View File

@@ -837,7 +837,7 @@ endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus)
target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus range-v3)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
endif()

View File

@@ -216,6 +216,14 @@ struct System::Impl {
}
}
void SetNVDECActive(bool is_nvdec_active) {
nvdec_active = is_nvdec_active;
}
bool GetNVDECActive() {
return nvdec_active;
}
void InitializeDebugger(System& system, u16 port) {
debugger = std::make_unique<Debugger>(system, port);
}
@@ -485,6 +493,8 @@ struct System::Impl {
std::atomic_bool is_powered_on{};
bool exit_lock = false;
bool nvdec_active{};
Reporter reporter;
std::unique_ptr<Memory::CheatEngine> cheat_engine;
std::unique_ptr<Tools::Freezer> memory_freezer;
@@ -594,6 +604,14 @@ void System::UnstallApplication() {
impl->UnstallApplication();
}
void System::SetNVDECActive(bool is_nvdec_active) {
impl->SetNVDECActive(is_nvdec_active);
}
bool System::GetNVDECActive() {
return impl->GetNVDECActive();
}
void System::InitializeDebugger() {
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
}

View File

@@ -189,6 +189,9 @@ public:
std::unique_lock<std::mutex> StallApplication();
void UnstallApplication();
void SetNVDECActive(bool is_nvdec_active);
[[nodiscard]] bool GetNVDECActive();
/**
* Initialize the debugger.
*/

View File

@@ -200,31 +200,32 @@ std::string VfsFile::GetFullPath() const {
}
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
auto&& component_view = path | Common::FS::split_path_components_view;
auto next_component = component_view.begin();
if (next_component == component_view.end()) {
return nullptr;
}
if (vec.size() == 1) {
return GetFile(vec[0]);
}
auto dir = GetSubdirectory(vec[0]);
for (std::size_t component = 1; component < vec.size() - 1; ++component) {
if (dir == nullptr) {
return nullptr;
}
dir = dir->GetSubdirectory(vec[component]);
std::string_view component = *next_component;
if (++next_component == component_view.end()) {
return GetFile(component);
}
auto dir = GetSubdirectory(component);
if (dir == nullptr) {
return nullptr;
}
component = *next_component;
return dir->GetFile(vec.back());
while (++next_component != component_view.end()) {
dir = dir->GetSubdirectory(component);
if (dir == nullptr) {
return nullptr;
}
component = *next_component;
}
return dir->GetFile(component);
}
VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {

View File

@@ -12,6 +12,11 @@
#pragma warning(pop)
#endif
#include <fmt/format.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/input.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -136,7 +141,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
if (!NFP::AmiiboCrypto::IsKeyAvailable()) {
LOG_INFO(Service_NFC, "Loading amiibo without keys");
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
BuildAmiiboWithoutKeys();
BuildAmiiboWithoutKeys(tag_data, encrypted_tag_data);
is_plain_amiibo = true;
is_write_protected = true;
return true;
@@ -366,16 +371,25 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
// The loaded amiibo is not encrypted
if (is_plain_amiibo) {
std::vector<u8> data(sizeof(NFP::NTAG215File));
memcpy(data.data(), &tag_data, sizeof(tag_data));
WriteBackupData(tag_data.uid, data);
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
return ResultSuccess;
}
if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
return ResultCorruptedData;
bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess();
LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
}
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
WriteBackupData(encrypted_tag_data.uuid.uid, data);
device_state = DeviceState::TagMounted;
mount_target = mount_target_;
return ResultSuccess;
@@ -470,6 +484,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
if (is_plain_amiibo) {
memcpy(data.data(), &tag_data, sizeof(tag_data));
WriteBackupData(tag_data.uid, data);
} else {
if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
LOG_ERROR(Service_NFP, "Failed to encode data");
@@ -477,6 +492,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
}
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
WriteBackupData(encrypted_tag_data.uuid.uid, data);
}
if (!npad_device->WriteNfc(data)) {
@@ -488,7 +504,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
}
Result NfcDevice::Restore() {
if (device_state != DeviceState::TagMounted) {
if (device_state != DeviceState::TagFound) {
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
if (device_state == DeviceState::TagRemoved) {
return ResultTagRemoved;
@@ -496,13 +512,59 @@ Result NfcDevice::Restore() {
return ResultWrongDeviceState;
}
if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
return ResultWrongDeviceState;
NFC::TagInfo tag_info{};
std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{};
Result result = GetTagInfo(tag_info, false);
if (result.IsError()) {
return result;
}
// TODO: Load amiibo from backup on system
LOG_ERROR(Service_NFP, "Not Implemented");
result = ReadBackupData(tag_info.uuid, data);
if (result.IsError()) {
return result;
}
NFP::NTAG215File temporary_tag_data{};
NFP::EncryptedNTAG215File temporary_encrypted_tag_data{};
// Fallback for encrypted amiibos without keys
if (is_write_protected) {
return ResultWriteAmiiboFailed;
}
// Fallback for plain amiibos
if (is_plain_amiibo) {
LOG_INFO(Service_NFP, "Restoring backup of plain amiibo");
memcpy(&temporary_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
temporary_encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(temporary_tag_data);
}
if (!is_plain_amiibo) {
LOG_INFO(Service_NFP, "Restoring backup of encrypted amiibo");
temporary_tag_data = {};
memcpy(&temporary_encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
}
if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
return ResultNotAnAmiibo;
}
if (!is_plain_amiibo) {
if (!NFP::AmiiboCrypto::DecodeAmiibo(temporary_encrypted_tag_data, temporary_tag_data)) {
LOG_ERROR(Service_NFP, "Can't decode amiibo");
return ResultCorruptedData;
}
}
// Overwrite tag contents with backup and mount the tag
tag_data = temporary_tag_data;
encrypted_tag_data = temporary_encrypted_tag_data;
device_state = DeviceState::TagMounted;
mount_target = NFP::MountTarget::All;
is_data_moddified = true;
return ResultSuccess;
}
@@ -1132,13 +1194,69 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) {
return FlushWithBreak(break_type);
}
Result NfcDevice::ReadBackupData(std::span<u8> data) const {
// Not implemented
Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const {
constexpr auto backup_dir = "backup";
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
return ResultUnableToAccessBackupFile;
}
return ResultSuccess;
}
Result NfcDevice::WriteBackupData(std::span<const u8> data) {
// Not implemented
Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const {
constexpr auto backup_dir = "backup";
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
if (!keys_file.IsOpen()) {
LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
return ResultUnableToAccessBackupFile;
}
if (keys_file.Read(data) != data.size()) {
LOG_ERROR(Service_NFP, "Failed to read amiibo backup");
return ResultUnableToAccessBackupFile;
}
return ResultSuccess;
}
Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) {
constexpr auto backup_dir = "backup";
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
if (HasBackup(uid).IsError()) {
if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
return ResultBackupPathAlreadyExist;
}
if (!Common::FS::NewFile(yuzu_amiibo_dir / backup_dir / file_name)) {
return ResultBackupPathAlreadyExist;
}
}
const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
Common::FS::FileAccessMode::ReadWrite,
Common::FS::FileType::BinaryFile};
if (!keys_file.IsOpen()) {
LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
return ResultUnableToAccessBackupFile;
}
if (keys_file.Write(data) != data.size()) {
LOG_ERROR(Service_NFP, "Failed to write amiibo backup");
return ResultUnableToAccessBackupFile;
}
return ResultSuccess;
}
@@ -1177,7 +1295,8 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co
return amiibo_name;
}
void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) {
void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings,
const NFP::AmiiboName& amiibo_name) const {
std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{};
// Convert from utf8 to utf16
@@ -1258,22 +1377,23 @@ void NfcDevice::UpdateRegisterInfoCrc() {
tag_data.register_info_crc = crc.checksum();
}
void NfcDevice::BuildAmiiboWithoutKeys() {
void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
const NFP::EncryptedNTAG215File& encrypted_file) const {
Service::Mii::MiiManager manager;
auto& settings = tag_data.settings;
auto& settings = stubbed_tag_data.settings;
tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_tag_data);
stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file);
// Common info
tag_data.write_counter = 0;
tag_data.amiibo_version = 0;
stubbed_tag_data.write_counter = 0;
stubbed_tag_data.amiibo_version = 0;
settings.write_date = GetAmiiboDate(GetCurrentPosixTime());
// Register info
SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'});
settings.settings.font_region.Assign(0);
settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0));
stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0));
// Admin info
settings.settings.amiibo_initialized.Assign(1);

View File

@@ -86,8 +86,9 @@ public:
Result GetAll(NFP::NfpData& data) const;
Result SetAll(const NFP::NfpData& data);
Result BreakTag(NFP::BreakType break_type);
Result ReadBackupData(std::span<u8> data) const;
Result WriteBackupData(std::span<const u8> data);
Result HasBackup(const NFC::UniqueSerialNumber& uid) const;
Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const;
Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data);
Result WriteNtf(std::span<const u8> data);
u64 GetHandle() const;
@@ -103,14 +104,15 @@ private:
void CloseNfcTag();
NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const;
NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 GetCurrentPosixTime() const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
void BuildAmiiboWithoutKeys();
void BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
const NFP::EncryptedNTAG215File& encrypted_file) const;
bool is_controller_set{};
int callback_key;

View File

@@ -543,9 +543,14 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
NFC::TagInfo tag_info{};
if (result.IsSuccess()) {
result = device->ReadBackupData(data);
result = device->GetTagInfo(tag_info, false);
}
if (result.IsSuccess()) {
result = device->ReadBackupData(tag_info.uuid, data);
result = VerifyDeviceResult(device, result);
}
@@ -557,9 +562,14 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
NFC::TagInfo tag_info{};
if (result.IsSuccess()) {
result = device->WriteBackupData(data);
result = device->GetTagInfo(tag_info, false);
}
if (result.IsSuccess()) {
result = device->WriteBackupData(tag_info.uuid, data);
result = VerifyDeviceResult(device, result);
}

View File

@@ -302,7 +302,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const {
return TranslateResultToNfp(result);
}
default:
if (result != ResultUnknown216) {
if (result != ResultBackupPathAlreadyExist) {
return result;
}
return ResultUnknown74;
@@ -343,6 +343,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultApplicationAreaIsNotInitialized) {
return NFP::ResultApplicationAreaIsNotInitialized;
}
if (result == ResultCorruptedDataWithBackup) {
return NFP::ResultCorruptedDataWithBackup;
}
if (result == ResultCorruptedData) {
return NFP::ResultCorruptedData;
}
@@ -355,6 +358,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultNotAnAmiibo) {
return NFP::ResultNotAnAmiibo;
}
if (result == ResultUnableToAccessBackupFile) {
return NFP::ResultUnableToAccessBackupFile;
}
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}

View File

@@ -9,20 +9,22 @@ namespace Service::NFC {
constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFC, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultUnknown216(ErrorModule::NFC, 216);
constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128);
constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136);
constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178);
constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
} // namespace Service::NFC

View File

@@ -126,7 +126,7 @@ void Interface::Flush(HLERequestContext& ctx) {
void Interface::Restore(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
auto result = GetManager()->Restore(device_handle);
result = TranslateResultToServiceError(result);
@@ -394,7 +394,7 @@ void Interface::BreakTag(HLERequestContext& ctx) {
void Interface::ReadBackupData(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
std::vector<u8> backup_data{};
auto result = GetManager()->ReadBackupData(device_handle, backup_data);
@@ -412,7 +412,7 @@ void Interface::WriteBackupData(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto backup_data_buffer{ctx.ReadBuffer()};
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer);
result = TranslateResultToServiceError(result);

View File

@@ -17,9 +17,11 @@ constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFP, 136);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFP, 200);
} // namespace Service::NFP

View File

@@ -69,7 +69,7 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
void nvhost_nvdec::OnOpen(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
system.AudioCore().SetNVDECActive(true);
system.SetNVDECActive(true);
}
void nvhost_nvdec::OnClose(DeviceFD fd) {
@@ -79,7 +79,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) {
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
system.AudioCore().SetNVDECActive(false);
system.SetNVDECActive(false);
}
} // namespace Service::Nvidia::Devices

View File

@@ -324,6 +324,10 @@ s64 Nvnflinger::GetNextTicks() const {
speed_scale = 0.01f;
}
}
if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
// Run at intended presentation rate during video playback.
speed_scale = 1.f;
}
// As an extension, treat nonpositive swap interval as framerate multiplier.
const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval)

View File

@@ -42,6 +42,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
ui->enable_compute_pipelines_checkbox->setChecked(
Settings::values.enable_compute_pipelines.GetValue());
ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue());
if (Settings::IsConfiguringGlobal()) {
ui->gpu_accuracy->setCurrentIndex(
@@ -91,6 +92,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
ui->enable_compute_pipelines_checkbox,
enable_compute_pipelines);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate,
ui->use_video_framerate_checkbox, use_video_framerate);
}
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -125,6 +128,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
Settings::values.max_anisotropy.UsingGlobal());
ui->enable_compute_pipelines_checkbox->setEnabled(
Settings::values.enable_compute_pipelines.UsingGlobal());
ui->use_video_framerate_checkbox->setEnabled(
Settings::values.use_video_framerate.UsingGlobal());
return;
}
@@ -149,6 +154,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox,
Settings::values.enable_compute_pipelines,
enable_compute_pipelines);
ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox,
Settings::values.use_video_framerate,
use_video_framerate);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));

View File

@@ -47,6 +47,7 @@ private:
ConfigurationShared::CheckState use_fast_gpu_time;
ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
ConfigurationShared::CheckState enable_compute_pipelines;
ConfigurationShared::CheckState use_video_framerate;
const Core::System& system;
};

View File

@@ -191,6 +191,16 @@ Compute pipelines are always enabled on all other drivers.</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_video_framerate_checkbox">
<property name="toolTip">
<string>Run the game at normal speed during video playback, even when the framerate is unlocked.</string>
</property>
<property name="text">
<string>Sync to framerate of video playback</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="af_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_1">

View File

@@ -24,6 +24,7 @@
"fmt",
"lz4",
"nlohmann-json",
"range-v3",
"zlib",
"zstd"
],