mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-03-22 17:46:08 -04:00
Merge pull request 'Hotfix: QLaunch Technical Overhaul & Various Misc. Bugfixes' (#120) from hotfix/qlaunch/etc into main
Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/120
This commit is contained in:
@@ -44,7 +44,7 @@ protected:
|
||||
void SaveUIValues() override;
|
||||
void SaveUIGamelistValues() override;
|
||||
void SaveUILayoutValues() override;
|
||||
void SaveMultiplayerValues();
|
||||
void SaveMultiplayerValues() override;
|
||||
void SaveNetworkValues();
|
||||
|
||||
public:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,27 +12,29 @@
|
||||
#include <QLineEdit>
|
||||
#include <QList>
|
||||
#include <QListView>
|
||||
#include <QPushButton>
|
||||
#include <QSlider>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QProgressBar>
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
#include <QSlider>
|
||||
#include <QStandardItemModel>
|
||||
#include <QString>
|
||||
#include <QResizeEvent>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVector>
|
||||
#include <QWidget>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
|
||||
#include "citron/compatibility_list.h"
|
||||
#include "citron/multiplayer/state.h"
|
||||
#include "citron/play_time_manager.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "uisettings.h"
|
||||
#include "citron/compatibility_list.h"
|
||||
#include "citron/play_time_manager.h"
|
||||
#include "citron/multiplayer/state.h"
|
||||
|
||||
|
||||
class ControllerNavigation;
|
||||
class GameListWorker;
|
||||
@@ -43,8 +45,8 @@ enum class AmLaunchType;
|
||||
enum class StartGameType;
|
||||
|
||||
namespace FileSys {
|
||||
class ManualContentProvider;
|
||||
class VfsFilesystem;
|
||||
class ManualContentProvider;
|
||||
class VfsFilesystem;
|
||||
} // namespace FileSys
|
||||
|
||||
enum class GameListOpenTarget {
|
||||
@@ -190,8 +192,10 @@ private:
|
||||
void FilterTreeView(const QString& filter_text);
|
||||
|
||||
void PopupContextMenu(const QPoint& menu_location);
|
||||
void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path, const QString& game_name);
|
||||
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||
void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path,
|
||||
const QString& game_name);
|
||||
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected,
|
||||
bool show_hidden_action = true);
|
||||
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
|
||||
void AddFavoritesPopup(QMenu& context_menu);
|
||||
|
||||
|
||||
@@ -11,16 +11,22 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
|
||||
#include "citron/compatibility_list.h"
|
||||
#include "citron/game_list.h"
|
||||
#include "citron/game_list_p.h"
|
||||
#include "citron/game_list_worker.h"
|
||||
#include "citron/uisettings.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "core/core.h"
|
||||
@@ -33,11 +39,7 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "citron/compatibility_list.h"
|
||||
#include "citron/game_list.h"
|
||||
#include "citron/game_list_p.h"
|
||||
#include "citron/game_list_worker.h"
|
||||
#include "citron/uisettings.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -76,9 +78,8 @@ std::string GetCacheKey(const std::string& file_path) {
|
||||
}
|
||||
|
||||
const auto path_str = Common::FS::PathToUTF8String(normalized_path);
|
||||
const auto hash = QCryptographicHash::hash(
|
||||
QByteArray::fromStdString(path_str),
|
||||
QCryptographicHash::Sha256);
|
||||
const auto hash =
|
||||
QCryptographicHash::hash(QByteArray::fromStdString(path_str), QCryptographicHash::Sha256);
|
||||
return hash.toHex().toStdString();
|
||||
}
|
||||
|
||||
@@ -90,7 +91,8 @@ void LoadGameMetadataCache() {
|
||||
|
||||
game_metadata_cache.clear();
|
||||
|
||||
const auto cache_dir = Common::FS::GetCitronPath(Common::FS::CitronPath::CacheDir) / "game_list";
|
||||
const auto cache_dir =
|
||||
Common::FS::GetCitronPath(Common::FS::CitronPath::CacheDir) / "game_list";
|
||||
const auto cache_file = Common::FS::PathToUTF8String(cache_dir / "game_metadata_cache.json");
|
||||
|
||||
if (!Common::FS::Exists(cache_file)) {
|
||||
@@ -115,15 +117,19 @@ void LoadGameMetadataCache() {
|
||||
const std::string key = entry[QStringLiteral("key")].toString().toStdString();
|
||||
|
||||
CachedGameMetadata metadata;
|
||||
metadata.program_id = entry[QStringLiteral("program_id")].toString().toULongLong(nullptr, 16);
|
||||
metadata.file_type = static_cast<Loader::FileType>(entry[QStringLiteral("file_type")].toInt());
|
||||
metadata.file_size = static_cast<std::size_t>(entry[QStringLiteral("file_size")].toVariant().toULongLong());
|
||||
metadata.program_id =
|
||||
entry[QStringLiteral("program_id")].toString().toULongLong(nullptr, 16);
|
||||
metadata.file_type =
|
||||
static_cast<Loader::FileType>(entry[QStringLiteral("file_type")].toInt());
|
||||
metadata.file_size =
|
||||
static_cast<std::size_t>(entry[QStringLiteral("file_size")].toVariant().toULongLong());
|
||||
metadata.title = entry[QStringLiteral("title")].toString().toStdString();
|
||||
metadata.file_path = entry[QStringLiteral("file_path")].toString().toStdString();
|
||||
metadata.modification_time = entry[QStringLiteral("modification_time")].toVariant().toLongLong();
|
||||
metadata.modification_time =
|
||||
entry[QStringLiteral("modification_time")].toVariant().toLongLong();
|
||||
|
||||
const QByteArray icon_data = QByteArray::fromBase64(
|
||||
entry[QStringLiteral("icon")].toString().toUtf8());
|
||||
const QByteArray icon_data =
|
||||
QByteArray::fromBase64(entry[QStringLiteral("icon")].toString().toUtf8());
|
||||
metadata.icon.assign(icon_data.begin(), icon_data.end());
|
||||
|
||||
if (metadata.IsValid()) {
|
||||
@@ -138,7 +144,8 @@ void SaveGameMetadataCache() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cache_dir = Common::FS::GetCitronPath(Common::FS::CitronPath::CacheDir) / "game_list";
|
||||
const auto cache_dir =
|
||||
Common::FS::GetCitronPath(Common::FS::CitronPath::CacheDir) / "game_list";
|
||||
const auto cache_file = Common::FS::PathToUTF8String(cache_dir / "game_metadata_cache.json");
|
||||
|
||||
void(Common::FS::CreateParentDirs(cache_file));
|
||||
@@ -154,7 +161,8 @@ void SaveGameMetadataCache() {
|
||||
entry[QStringLiteral("file_size")] = static_cast<qint64>(metadata.file_size);
|
||||
entry[QStringLiteral("title")] = QString::fromStdString(metadata.title);
|
||||
entry[QStringLiteral("file_path")] = QString::fromStdString(metadata.file_path);
|
||||
entry[QStringLiteral("modification_time")] = static_cast<qint64>(metadata.modification_time);
|
||||
entry[QStringLiteral("modification_time")] =
|
||||
static_cast<qint64>(metadata.modification_time);
|
||||
|
||||
const QByteArray icon_data(reinterpret_cast<const char*>(metadata.icon.data()),
|
||||
static_cast<int>(metadata.icon.size()));
|
||||
@@ -189,8 +197,8 @@ const CachedGameMetadata* GetCachedGameMetadata(const std::string& file_path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto mod_time_seconds = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
mod_time.time_since_epoch()).count();
|
||||
const auto mod_time_seconds =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(mod_time.time_since_epoch()).count();
|
||||
|
||||
const std::string key = GetCacheKey(file_path);
|
||||
const auto it = game_metadata_cache.find(key);
|
||||
@@ -211,7 +219,8 @@ const CachedGameMetadata* GetCachedGameMetadata(const std::string& file_path) {
|
||||
|
||||
// Store game metadata in cache
|
||||
void CacheGameMetadata(const std::string& file_path, u64 program_id, Loader::FileType file_type,
|
||||
std::size_t file_size, const std::string& title, const std::vector<u8>& icon) {
|
||||
std::size_t file_size, const std::string& title,
|
||||
const std::vector<u8>& icon) {
|
||||
if (!UISettings::values.cache_game_list) {
|
||||
return;
|
||||
}
|
||||
@@ -222,8 +231,8 @@ void CacheGameMetadata(const std::string& file_path, u64 program_id, Loader::Fil
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mod_time_seconds = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
mod_time.time_since_epoch()).count();
|
||||
const auto mod_time_seconds =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(mod_time.time_since_epoch()).count();
|
||||
|
||||
const std::string key = GetCacheKey(file_path);
|
||||
|
||||
@@ -411,12 +420,13 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
|
||||
return out;
|
||||
}
|
||||
|
||||
QList<QStandardItem*> MakeGameListEntry(
|
||||
const std::string& path, const std::string& name, const std::size_t size,
|
||||
const std::vector<u8>& icon, Loader::AppLoader& loader, u64 program_id,
|
||||
const CompatibilityList& compatibility_list, const PlayTime::PlayTimeManager& play_time_manager,
|
||||
const FileSys::PatchManager& patch,
|
||||
const std::map<u64, std::pair<int, int>>& online_stats) {
|
||||
QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name,
|
||||
const std::size_t size, const std::vector<u8>& icon,
|
||||
Loader::AppLoader& loader, u64 program_id,
|
||||
const CompatibilityList& compatibility_list,
|
||||
const PlayTime::PlayTimeManager& play_time_manager,
|
||||
const FileSys::PatchManager& patch,
|
||||
const std::map<u64, std::pair<int, int>>& online_stats) {
|
||||
const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
// The game list uses this as compatibility number for untested games
|
||||
@@ -432,17 +442,18 @@ QList<QStandardItem*> MakeGameListEntry(
|
||||
auto it_stats = online_stats.find(program_id);
|
||||
if (it_stats != online_stats.end()) {
|
||||
const auto& stats = it_stats->second;
|
||||
online_text = QStringLiteral("Players: %1 | Servers: %2").arg(stats.first).arg(stats.second);
|
||||
online_text =
|
||||
QStringLiteral("Players: %1 | Servers: %2").arg(stats.first).arg(stats.second);
|
||||
}
|
||||
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name),
|
||||
file_type_string, program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(file_type_string),
|
||||
new GameListItemSize(size),
|
||||
new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)),
|
||||
new GameListItemOnline(online_text)};
|
||||
QList<QStandardItem*> list{new GameListItemPath(FormatGameName(path), icon,
|
||||
QString::fromStdString(name), file_type_string,
|
||||
program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(file_type_string),
|
||||
new GameListItemSize(size),
|
||||
new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)),
|
||||
new GameListItemOnline(online_text)};
|
||||
|
||||
const auto patch_versions = GetGameListCachedObject(
|
||||
fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
|
||||
@@ -510,7 +521,8 @@ void GameListWorker::RecordEvent(F&& func) {
|
||||
emit DataAvailable();
|
||||
}
|
||||
|
||||
void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir, const std::map<u64, std::pair<int, int>>& online_stats) {
|
||||
void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir,
|
||||
const std::map<u64, std::pair<int, int>>& online_stats) {
|
||||
using namespace FileSys;
|
||||
|
||||
const auto& cache = system.GetContentProviderUnion();
|
||||
@@ -556,17 +568,21 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir, const std::map
|
||||
GetMetadataFromControlNCA(patch, *control, icon, name);
|
||||
}
|
||||
|
||||
auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
||||
program_id, compatibility_list, play_time_manager, patch, online_stats);
|
||||
auto entry =
|
||||
MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader, program_id,
|
||||
compatibility_list, play_time_manager, patch, online_stats);
|
||||
RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
}
|
||||
}
|
||||
|
||||
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
||||
GameListDir* parent_dir, const std::map<u64, std::pair<int, int>>& online_stats,
|
||||
GameListDir* parent_dir,
|
||||
const std::map<u64, std::pair<int, int>>& online_stats,
|
||||
int& processed_files, int total_files) {
|
||||
const auto callback = [this, target, parent_dir, &online_stats, &processed_files, total_files](const std::filesystem::path& path) -> bool {
|
||||
if (stop_requested) return false;
|
||||
const auto callback = [this, target, parent_dir, &online_stats, &processed_files,
|
||||
total_files](const std::filesystem::path& path) -> bool {
|
||||
if (stop_requested)
|
||||
return false;
|
||||
|
||||
const auto physical_name = Common::FS::PathToUTF8String(path);
|
||||
|
||||
@@ -583,19 +599,49 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
|
||||
// Cache Check
|
||||
const auto* cached = GetCachedGameMetadata(physical_name);
|
||||
if (cached && cached->IsValid() && (target == ScanTarget::PopulateGameList || target == ScanTarget::Both)) {
|
||||
if ((cached->program_id & 0xFFF) == 0) {
|
||||
const FileSys::PatchManager patch{cached->program_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read);
|
||||
if (file) {
|
||||
if (cached && cached->IsValid() &&
|
||||
(target == ScanTarget::PopulateGameList || target == ScanTarget::Both)) {
|
||||
auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read);
|
||||
if (file) {
|
||||
// 1. Register with provider if requested
|
||||
if (target == ScanTarget::FillManualContentProvider || target == ScanTarget::Both) {
|
||||
if (cached->file_type == Loader::FileType::NCA) {
|
||||
provider->AddEntry(
|
||||
FileSys::TitleType::Application,
|
||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
||||
cached->program_id, file);
|
||||
} else if (cached->file_type == Loader::FileType::XCI ||
|
||||
cached->file_type == Loader::FileType::NSP) {
|
||||
const auto nsp = cached->file_type == Loader::FileType::NSP
|
||||
? std::make_shared<FileSys::NSP>(file)
|
||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
provider->AddEntry(entry.first.first, entry.first.second,
|
||||
title.first, entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Populate UI if requested (only for base games)
|
||||
if ((cached->program_id & 0xFFF) == 0 &&
|
||||
(target == ScanTarget::PopulateGameList || target == ScanTarget::Both)) {
|
||||
const FileSys::PatchManager patch{cached->program_id,
|
||||
system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
auto loader = Loader::GetLoader(system, file);
|
||||
if (loader) {
|
||||
auto entry = MakeGameListEntry(physical_name, cached->title, cached->file_size, cached->icon, *loader,
|
||||
cached->program_id, compatibility_list, play_time_manager, patch, online_stats);
|
||||
RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
auto entry = MakeGameListEntry(physical_name, cached->title,
|
||||
cached->file_size, cached->icon, *loader,
|
||||
cached->program_id, compatibility_list,
|
||||
play_time_manager, patch, online_stats);
|
||||
RecordEvent(
|
||||
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processed_files++;
|
||||
emit ProgressUpdated(std::min(100, (processed_files * 100) / total_files));
|
||||
return true;
|
||||
@@ -622,12 +668,18 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
|
||||
if (target == ScanTarget::FillManualContentProvider || target == ScanTarget::Both) {
|
||||
if (file_type == Loader::FileType::NCA) {
|
||||
provider->AddEntry(FileSys::TitleType::Application, FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id, file);
|
||||
} else if (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP) {
|
||||
const auto nsp = file_type == Loader::FileType::NSP ? std::make_shared<FileSys::NSP>(file) : FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
provider->AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
||||
program_id, file);
|
||||
} else if (file_type == Loader::FileType::XCI ||
|
||||
file_type == Loader::FileType::NSP) {
|
||||
const auto nsp = file_type == Loader::FileType::NSP
|
||||
? std::make_shared<FileSys::NSP>(file)
|
||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
provider->AddEntry(entry.first.first, entry.first.second, title.first, entry.second->GetBaseFile());
|
||||
provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||
entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,9 +696,13 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
|
||||
CacheGameMetadata(physical_name, program_id, file_type, file_size, name, icon);
|
||||
|
||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
auto entry = MakeGameListEntry(physical_name, name, file_size, icon, *loader, program_id, compatibility_list, play_time_manager, patch, online_stats);
|
||||
RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
auto entry = MakeGameListEntry(physical_name, name, file_size, icon, *loader,
|
||||
program_id, compatibility_list,
|
||||
play_time_manager, patch, online_stats);
|
||||
RecordEvent(
|
||||
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -657,7 +713,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
};
|
||||
|
||||
if (deep_scan) {
|
||||
Common::FS::IterateDirEntriesRecursively(dir_path, callback, Common::FS::DirEntryFilter::File);
|
||||
Common::FS::IterateDirEntriesRecursively(dir_path, callback,
|
||||
Common::FS::DirEntryFilter::File);
|
||||
} else {
|
||||
Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File);
|
||||
}
|
||||
@@ -685,7 +742,8 @@ void GameListWorker::run() {
|
||||
int processed_files = 0;
|
||||
|
||||
for (const auto& game_dir : game_dirs) {
|
||||
if (game_dir.path == "SDMC" || game_dir.path == "UserNAND" || game_dir.path == "SysNAND") continue;
|
||||
if (game_dir.path == "SDMC" || game_dir.path == "UserNAND" || game_dir.path == "SysNAND")
|
||||
continue;
|
||||
|
||||
auto count_callback = [&](const std::filesystem::path& path) -> bool {
|
||||
const std::string physical_name = Common::FS::PathToUTF8String(path);
|
||||
@@ -696,28 +754,34 @@ void GameListWorker::run() {
|
||||
};
|
||||
|
||||
if (game_dir.deep_scan) {
|
||||
Common::FS::IterateDirEntriesRecursively(game_dir.path, count_callback, Common::FS::DirEntryFilter::File);
|
||||
Common::FS::IterateDirEntriesRecursively(game_dir.path, count_callback,
|
||||
Common::FS::DirEntryFilter::File);
|
||||
} else {
|
||||
Common::FS::IterateDirEntries(game_dir.path, count_callback, Common::FS::DirEntryFilter::File);
|
||||
Common::FS::IterateDirEntries(game_dir.path, count_callback,
|
||||
Common::FS::DirEntryFilter::File);
|
||||
}
|
||||
}
|
||||
|
||||
if (total_files <= 0) total_files = 1;
|
||||
if (total_files <= 0)
|
||||
total_files = 1;
|
||||
|
||||
const auto DirEntryReady = [&](GameListDir* game_list_dir) {
|
||||
RecordEvent([=](GameList* game_list) { game_list->AddDirEntry(game_list_dir); });
|
||||
};
|
||||
|
||||
for (UISettings::GameDir& game_dir : game_dirs) {
|
||||
if (stop_requested) break;
|
||||
if (stop_requested)
|
||||
break;
|
||||
|
||||
if (game_dir.path == "SDMC" || game_dir.path == "UserNAND" || game_dir.path == "SysNAND") continue;
|
||||
if (game_dir.path == "SDMC" || game_dir.path == "UserNAND" || game_dir.path == "SysNAND")
|
||||
continue;
|
||||
|
||||
watch_list.append(QString::fromStdString(game_dir.path));
|
||||
auto* const game_list_dir = new GameListDir(game_dir);
|
||||
DirEntryReady(game_list_dir);
|
||||
|
||||
ScanFileSystem(ScanTarget::Both, game_dir.path, game_dir.deep_scan, game_list_dir, online_stats, processed_files, total_files);
|
||||
ScanFileSystem(ScanTarget::Both, game_dir.path, game_dir.deep_scan, game_list_dir,
|
||||
online_stats, processed_files, total_files);
|
||||
}
|
||||
|
||||
RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); });
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,23 +4,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "common/common_types.h"
|
||||
#include "configuration/qt_config.h"
|
||||
#include "frontend_common/content_manager.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
#include "citron/compatibility_list.h"
|
||||
#include "citron/hotkeys.h"
|
||||
#include "citron/util/controller_navigation.h"
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "common/common_types.h"
|
||||
#include "configuration/qt_config.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "frontend_common/content_manager.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
|
||||
|
||||
#ifdef __unix__
|
||||
#include <QVariant>
|
||||
@@ -61,23 +62,61 @@ class QtControllerSelectorDialog;
|
||||
class QtProfileSelectionDialog;
|
||||
class QtSoftwareKeyboardDialog;
|
||||
class QtNXWebEngineView;
|
||||
namespace Updater { class UpdaterDialog; }
|
||||
namespace Updater {
|
||||
class UpdaterDialog;
|
||||
}
|
||||
|
||||
enum class StartGameType { Normal, Global };
|
||||
|
||||
namespace Core { enum class SystemResultStatus : u32; class System; }
|
||||
namespace Core::Frontend { struct CabinetParameters; struct ControllerParameters; struct InlineAppearParameters; struct InlineTextParameters; struct KeyboardInitializeParameters; struct ProfileSelectParameters; }
|
||||
namespace DiscordRPC { class DiscordInterface; }
|
||||
namespace PlayTime { class PlayTimeManager; }
|
||||
namespace FileSys { class ContentProvider; class ManualContentProvider; class VfsFilesystem; }
|
||||
namespace InputCommon { class InputSubsystem; }
|
||||
namespace Service::AM { struct FrontendAppletParameters; enum class AppletId : u32; }
|
||||
namespace Service::AM::Frontend { enum class SwkbdResult : u32; enum class SwkbdTextCheckResult : u32; enum class SwkbdReplyType : u32; enum class WebExitReason : u32; }
|
||||
namespace Service::NFC { class NfcDevice; }
|
||||
namespace Service::NFP { enum class CabinetMode : u8; }
|
||||
namespace Ui { class MainWindow; }
|
||||
namespace Core {
|
||||
enum class SystemResultStatus : u32;
|
||||
class System;
|
||||
} // namespace Core
|
||||
namespace Core::Frontend {
|
||||
struct CabinetParameters;
|
||||
struct ControllerParameters;
|
||||
struct InlineAppearParameters;
|
||||
struct InlineTextParameters;
|
||||
struct KeyboardInitializeParameters;
|
||||
struct ProfileSelectParameters;
|
||||
} // namespace Core::Frontend
|
||||
namespace DiscordRPC {
|
||||
class DiscordInterface;
|
||||
}
|
||||
namespace PlayTime {
|
||||
class PlayTimeManager;
|
||||
}
|
||||
namespace FileSys {
|
||||
class ContentProvider;
|
||||
class ManualContentProvider;
|
||||
class VfsFilesystem;
|
||||
} // namespace FileSys
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
namespace Service::AM {
|
||||
struct FrontendAppletParameters;
|
||||
enum class AppletId : u32;
|
||||
} // namespace Service::AM
|
||||
namespace Service::AM::Frontend {
|
||||
enum class SwkbdResult : u32;
|
||||
enum class SwkbdTextCheckResult : u32;
|
||||
enum class SwkbdReplyType : u32;
|
||||
enum class WebExitReason : u32;
|
||||
} // namespace Service::AM::Frontend
|
||||
namespace Service::NFC {
|
||||
class NfcDevice;
|
||||
}
|
||||
namespace Service::NFP {
|
||||
enum class CabinetMode : u8;
|
||||
}
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
enum class EmulatedDirectoryTarget { NAND, SDMC };
|
||||
namespace VkDeviceInfo { class Record; }
|
||||
namespace VkDeviceInfo {
|
||||
class Record;
|
||||
}
|
||||
|
||||
class VolumeButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
@@ -87,10 +126,12 @@ public:
|
||||
}
|
||||
signals:
|
||||
void VolumeChanged();
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent* event) override;
|
||||
private slots:
|
||||
void ResetMultiplier();
|
||||
|
||||
private:
|
||||
int scroll_multiplier;
|
||||
QTimer scroll_timer;
|
||||
@@ -102,24 +143,47 @@ class GMainWindow : public QMainWindow {
|
||||
static const int max_recent_files_item = 10;
|
||||
friend class PerformanceOverlay;
|
||||
friend class VramOverlay;
|
||||
enum { CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, CREATE_SHORTCUT_MSGBOX_SUCCESS, CREATE_SHORTCUT_MSGBOX_ERROR, CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING };
|
||||
enum {
|
||||
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
|
||||
CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
||||
CREATE_SHORTCUT_MSGBOX_ERROR,
|
||||
CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING
|
||||
};
|
||||
|
||||
public:
|
||||
void filterBarSetChecked(bool state);
|
||||
void UpdateUITheme();
|
||||
bool IsConfiguring() const { return m_is_configuring; }
|
||||
bool IsConfiguring() const {
|
||||
return m_is_configuring;
|
||||
}
|
||||
explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
|
||||
~GMainWindow() override;
|
||||
bool DropAction(QDropEvent* event);
|
||||
void AcceptDropEvent(QDropEvent* event);
|
||||
MultiplayerState* GetMultiplayerState() { return multiplayer_state; }
|
||||
Core::System* GetSystem() { return system.get(); }
|
||||
const std::shared_ptr<FileSys::VfsFilesystem>& GetVFS() const { return vfs; }
|
||||
bool IsEmulationRunning() const { return emulation_running; }
|
||||
MultiplayerState* GetMultiplayerState() {
|
||||
return multiplayer_state;
|
||||
}
|
||||
Core::System* GetSystem() {
|
||||
return system.get();
|
||||
}
|
||||
const std::shared_ptr<FileSys::VfsFilesystem>& GetVFS() const {
|
||||
return vfs;
|
||||
}
|
||||
bool IsEmulationRunning() const {
|
||||
return emulation_running;
|
||||
}
|
||||
void RefreshGameList();
|
||||
GRenderWindow* GetRenderWindow() const { return render_window; }
|
||||
bool ExtractZipToDirectoryPublic(const std::filesystem::path& zip_path, const std::filesystem::path& extract_path);
|
||||
[[nodiscard]] bool HasPerformedInitialSync() const { return has_performed_initial_sync; }
|
||||
void SetPerformedInitialSync(bool synced) { has_performed_initial_sync = synced; }
|
||||
GRenderWindow* GetRenderWindow() const {
|
||||
return render_window;
|
||||
}
|
||||
bool ExtractZipToDirectoryPublic(const std::filesystem::path& zip_path,
|
||||
const std::filesystem::path& extract_path);
|
||||
[[nodiscard]] bool HasPerformedInitialSync() const {
|
||||
return has_performed_initial_sync;
|
||||
}
|
||||
void SetPerformedInitialSync(bool synced) {
|
||||
has_performed_initial_sync = synced;
|
||||
}
|
||||
signals:
|
||||
void EmulationStarting(EmuThread* emu_thread);
|
||||
void EmulationStopping();
|
||||
@@ -130,8 +194,10 @@ signals:
|
||||
void ControllerSelectorReconfigureFinished(bool is_success);
|
||||
void ErrorDisplayFinished();
|
||||
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
|
||||
void SoftwareKeyboardSubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text, bool confirmed);
|
||||
void SoftwareKeyboardSubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position);
|
||||
void SoftwareKeyboardSubmitNormalText(Service::AM::Frontend::SwkbdResult result,
|
||||
std::u16string submitted_text, bool confirmed);
|
||||
void SoftwareKeyboardSubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type,
|
||||
std::u16string submitted_text, s32 cursor_position);
|
||||
void WebBrowserExtractOfflineRomFS();
|
||||
void WebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url);
|
||||
void SigInterrupt();
|
||||
@@ -141,13 +207,18 @@ public slots:
|
||||
void OnExecuteProgram(std::size_t program_index);
|
||||
void OnExit();
|
||||
void OnSaveConfig();
|
||||
void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, std::shared_ptr<Service::NFC::NfcDevice> nfp_device);
|
||||
void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
|
||||
std::shared_ptr<Service::NFC::NfcDevice> nfp_device);
|
||||
void AmiiboSettingsRequestExit();
|
||||
void ControllerSelectorReconfigureControllers(const Core::Frontend::ControllerParameters& parameters);
|
||||
void ControllerSelectorReconfigureControllers(
|
||||
const Core::Frontend::ControllerParameters& parameters);
|
||||
void ControllerSelectorRequestExit();
|
||||
void SoftwareKeyboardInitialize(bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters);
|
||||
void SoftwareKeyboardInitialize(
|
||||
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters);
|
||||
void SoftwareKeyboardShowNormal();
|
||||
void SoftwareKeyboardShowTextCheck(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message);
|
||||
void SoftwareKeyboardShowTextCheck(
|
||||
Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
|
||||
std::u16string text_check_message);
|
||||
void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters);
|
||||
void SoftwareKeyboardHideInline();
|
||||
void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
|
||||
@@ -156,13 +227,16 @@ public slots:
|
||||
void ErrorDisplayRequestExit();
|
||||
void ProfileSelectorSelectProfile(const Core::Frontend::ProfileSelectParameters& parameters);
|
||||
void ProfileSelectorRequestExit();
|
||||
void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, bool is_local);
|
||||
void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args,
|
||||
bool is_local);
|
||||
void WebBrowserRequestExit();
|
||||
void OnAppFocusStateChanged(Qt::ApplicationState state);
|
||||
void OnTasStateChanged();
|
||||
void IncrementInstallProgress();
|
||||
|
||||
private:
|
||||
void LinkActionShortcut(QAction* action, const QString& action_name, const bool tas_allowed = false);
|
||||
void LinkActionShortcut(QAction* action, const QString& action_name,
|
||||
const bool tas_allowed = false);
|
||||
void RegisterMetaTypes();
|
||||
void RegisterAutoloaderContents();
|
||||
void InitializeWidgets();
|
||||
@@ -177,7 +251,8 @@ private:
|
||||
void PreventOSSleep();
|
||||
void AllowOSSleep();
|
||||
bool LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params);
|
||||
void BootGame(const QString& filename, Service::AM::FrontendAppletParameters params, StartGameType with_config = StartGameType::Normal);
|
||||
void BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
|
||||
StartGameType with_config = StartGameType::Normal);
|
||||
void BootGameFromList(const QString& filename, StartGameType with_config);
|
||||
void ShutdownGame();
|
||||
void ShowTelemetryCallout();
|
||||
@@ -192,17 +267,20 @@ private:
|
||||
void RequestGameExit();
|
||||
void changeEvent(QEvent* event) override;
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
std::string CreateTASFramesString(std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
|
||||
#ifdef __unix__
|
||||
std::string CreateTASFramesString(
|
||||
std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const;
|
||||
#ifdef __unix__
|
||||
void SetupSigInterrupts();
|
||||
static void HandleSigInterrupt(int);
|
||||
void OnSigInterruptNotifierActivated();
|
||||
void SetGamemodeEnabled(bool state);
|
||||
#endif
|
||||
#endif
|
||||
Core::PerfStatsResults last_perf_stats{};
|
||||
Service::AM::FrontendAppletParameters ApplicationAppletParameters();
|
||||
Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id, Service::AM::AppletId applet_id);
|
||||
Service::AM::FrontendAppletParameters SystemAppletParameters(u64 program_id, Service::AM::AppletId applet_id);
|
||||
Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id,
|
||||
Service::AM::AppletId applet_id);
|
||||
Service::AM::FrontendAppletParameters SystemAppletParameters(u64 program_id,
|
||||
Service::AM::AppletId applet_id);
|
||||
void SetupHomeMenuCallback();
|
||||
std::unique_ptr<FileSys::ManualContentProvider> autoloader_provider;
|
||||
u64 current_title_id{0};
|
||||
@@ -216,16 +294,20 @@ private slots:
|
||||
void OnMenuReportCompatibility();
|
||||
void OnOpenSupport();
|
||||
void OnGameListLoadFile(QString game_path, u64 program_id);
|
||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, const std::string& game_path);
|
||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
|
||||
const std::string& game_path);
|
||||
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
||||
void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
|
||||
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, const std::string& game_path);
|
||||
void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
|
||||
const std::string& game_path);
|
||||
void OnGameListRemovePlayTimeData(u64 program_id);
|
||||
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
|
||||
void OnGameListVerifyIntegrity(const std::string& game_path);
|
||||
void OnGameListCopyTID(u64 program_id);
|
||||
void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list);
|
||||
void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target);
|
||||
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
const CompatibilityList& compatibility_list);
|
||||
void OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
|
||||
GameListShortcutTarget target);
|
||||
void OnGameListOpenDirectory(const QString& directory);
|
||||
void OnGameListAddDirectory();
|
||||
void OnGameListShowList(bool show);
|
||||
@@ -252,10 +334,12 @@ private slots:
|
||||
void OnConfigurePerGame();
|
||||
void OnLoadAmiibo();
|
||||
void OnOpenCitronFolder();
|
||||
void OnOpenLogFolder();
|
||||
void OnVerifyInstalledContents();
|
||||
void OnInstallFirmware();
|
||||
void OnInstallFirmwareFromZip();
|
||||
bool ExtractZipToDirectory(const std::filesystem::path& zip_path, const std::filesystem::path& extract_path);
|
||||
bool ExtractZipToDirectory(const std::filesystem::path& zip_path,
|
||||
const std::filesystem::path& extract_path);
|
||||
void OnInstallDecryptionKeys();
|
||||
void OnAbout();
|
||||
void OnCheckForUpdates();
|
||||
@@ -300,6 +384,7 @@ private slots:
|
||||
void OnShutdownBeginDialog();
|
||||
void OnEmulationStopped();
|
||||
void OnEmulationStopTimeExpired();
|
||||
|
||||
private:
|
||||
QString GetGameListErrorRemoving(InstalledEntryType type) const;
|
||||
void RemoveBaseContent(u64 program_id, InstalledEntryType type);
|
||||
@@ -311,10 +396,12 @@ private:
|
||||
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
|
||||
void RemovePlayTimeData(u64 program_id);
|
||||
void RemoveCacheStorage(u64 program_id);
|
||||
bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, u64* selected_title_id, u8* selected_content_record_type);
|
||||
bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
|
||||
u64* selected_title_id, u8* selected_content_record_type);
|
||||
ContentManager::InstallResult InstallNCA(const QString& filename);
|
||||
void MigrateConfigFiles();
|
||||
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, std::string_view gpu_vendor = {});
|
||||
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
|
||||
std::string_view gpu_vendor = {});
|
||||
void UpdateDockedButton();
|
||||
void UpdateAPIText();
|
||||
void UpdateFilterText();
|
||||
@@ -337,9 +424,17 @@ private:
|
||||
bool ConfirmShutdownGame();
|
||||
QString GetTasStateDescription() const;
|
||||
bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
|
||||
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path);
|
||||
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, const std::filesystem::path& icon_path, const std::filesystem::path& command, const std::string& arguments, const std::string& categories, const std::string& keywords, const std::string& name);
|
||||
bool question(QWidget* parent, const QString& title, const QString& text, QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
|
||||
std::filesystem::path& out_icon_path);
|
||||
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment,
|
||||
const std::filesystem::path& icon_path,
|
||||
const std::filesystem::path& command, const std::string& arguments,
|
||||
const std::string& categories, const std::string& keywords,
|
||||
const std::string& name);
|
||||
bool question(QWidget* parent, const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons =
|
||||
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
std::unique_ptr<Core::System> system;
|
||||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
@@ -410,10 +505,10 @@ private:
|
||||
bool m_is_updating_theme = false;
|
||||
bool m_is_configuring = false;
|
||||
bool has_performed_initial_sync = false;
|
||||
#ifdef __unix__
|
||||
#ifdef __unix__
|
||||
QSocketNotifier* sig_interrupt_notifier;
|
||||
static std::array<int, 3> sig_interrupt_fds;
|
||||
#endif
|
||||
#endif
|
||||
protected:
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
|
||||
@@ -192,6 +192,8 @@
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="action_Open_Support"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Open_Log_Folder"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Check_For_Updates"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_About"/>
|
||||
@@ -450,6 +452,11 @@
|
||||
<string>Open &citron Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Open_Log_Folder">
|
||||
<property name="text">
|
||||
<string>Open &Log Folder</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Capture_Screenshot">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
|
||||
@@ -459,6 +459,8 @@ add_library(core STATIC
|
||||
hle/service/am/process_holder.h
|
||||
hle/service/am/service/all_system_applet_proxies_service.cpp
|
||||
hle/service/am/service/all_system_applet_proxies_service.h
|
||||
hle/service/am/service/applet_alternative_functions.cpp
|
||||
hle/service/am/service/applet_alternative_functions.h
|
||||
hle/service/am/service/applet_common_functions.cpp
|
||||
hle/service/am/service/applet_common_functions.h
|
||||
hle/service/am/service/application_accessor.cpp
|
||||
@@ -499,6 +501,8 @@ add_library(core STATIC
|
||||
hle/service/am/service/lock_accessor.h
|
||||
hle/service/am/service/overlay_applet_proxy.cpp
|
||||
hle/service/am/service/overlay_applet_proxy.h
|
||||
hle/service/am/service/overlay_functions.cpp
|
||||
hle/service/am/service/overlay_functions.h
|
||||
hle/service/am/service/process_winding_controller.cpp
|
||||
hle/service/am/service/process_winding_controller.h
|
||||
hle/service/am/service/self_controller.cpp
|
||||
@@ -1257,7 +1261,8 @@ target_link_libraries(core
|
||||
nlohmann_json::nlohmann_json
|
||||
mbedtls
|
||||
RenderDoc::API
|
||||
)
|
||||
stb::headers
|
||||
)
|
||||
|
||||
# Conditionally link against Boost::process ONLY if it was found by the main CMakeLists.txt.
|
||||
if(Boost_PROCESS_FOUND)
|
||||
|
||||
@@ -790,7 +790,9 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
|
||||
if (GetFileAtID(id) != nullptr) {
|
||||
LOG_WARNING(Loader, "Overwriting existing NCA...");
|
||||
VirtualDir c_dir;
|
||||
{ c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
|
||||
{
|
||||
c_dir = dir->GetFileRelative(path)->GetContainingDirectory();
|
||||
}
|
||||
c_dir->DeleteFile(Common::FS::GetFilename(path));
|
||||
}
|
||||
|
||||
@@ -1033,4 +1035,5 @@ std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
|
||||
out.erase(std::unique(out.begin(), out.end()), out.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -165,8 +165,8 @@ public:
|
||||
const VfsCopyFunction& copy = &VfsRawCopy);
|
||||
|
||||
// Due to the fact that we must use Meta-type NCAs to determine the existence of files, this
|
||||
// poses quite a challenge. Instead of creating a new meta NCA for this file, citron will create a
|
||||
// dir inside the NAND called 'citron_meta' and store the raw CNMT there.
|
||||
// poses quite a challenge. Instead of creating a new meta NCA for this file, citron will create
|
||||
// a dir inside the NAND called 'citron_meta' and store the raw CNMT there.
|
||||
// TODO(DarkLordZach): Author real meta-type NCAs and install those.
|
||||
InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
|
||||
const VfsCopyFunction& copy = &VfsRawCopy);
|
||||
@@ -209,7 +209,8 @@ enum class ContentProviderUnionSlot {
|
||||
UserNAND, ///< User NAND
|
||||
SDMC, ///< SD Card
|
||||
FrontendManual, ///< Frontend-defined game list or similar
|
||||
Autoloader, ///< Separate functionality for multiple Updates/DLCs without being overwritten by NAND.
|
||||
Autoloader, ///< Separate functionality for multiple Updates/DLCs without being overwritten by
|
||||
///< NAND.
|
||||
};
|
||||
|
||||
// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
|
||||
|
||||
@@ -60,13 +60,13 @@ Result ResetSignal(Core::System& system, Handle handle) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle not found - log once and return success to prevent infinite loops
|
||||
// Handle not found
|
||||
if (should_log) {
|
||||
LOG_WARNING(Kernel_SVC, "ResetSignal called with invalid handle 0x{:08X}, returning success to prevent hang", handle);
|
||||
LOG_WARNING(Kernel_SVC, "ResetSignal called with invalid handle 0x{:08X}", handle);
|
||||
logged_handles.insert(handle);
|
||||
}
|
||||
|
||||
R_SUCCEED(); // Return success instead of throwing to prevent infinite loops
|
||||
R_RETURN(ResultInvalidHandle);
|
||||
}
|
||||
|
||||
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/glue/glue_manager.h"
|
||||
#include "core/hle/service/ns/ns_types.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@@ -108,6 +109,7 @@ public:
|
||||
{150, nullptr, "CreateAuthorizationRequest"},
|
||||
{160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"},
|
||||
{161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"},
|
||||
{143, D<&IManagerForSystemService::GetNetworkServiceLicenseCacheEx>, "GetNetworkServiceLicenseCacheEx"}, // 15.0.0+
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -136,6 +138,15 @@ private:
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetNetworkServiceLicenseCacheEx(Out<u32> out_license, Out<s64> out_expiration) {
|
||||
LOG_INFO(Service_ACC, "called");
|
||||
|
||||
*out_license = 0;
|
||||
*out_expiration = 0;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Common::UUID account_id;
|
||||
};
|
||||
|
||||
@@ -333,6 +344,9 @@ public:
|
||||
{1, &IProfileCommon::GetBase, "GetBase"},
|
||||
{10, &IProfileCommon::GetImageSize, "GetImageSize"},
|
||||
{11, &IProfileCommon::LoadImage, "LoadImage"},
|
||||
{20, &IProfileCommon::Unknown20, "Unknown20"},
|
||||
{21, &IProfileCommon::Unknown21, "Unknown21"},
|
||||
{30, &IProfileCommon::Unknown30, "Unknown30"},
|
||||
};
|
||||
|
||||
RegisterHandlers(functions);
|
||||
@@ -341,6 +355,7 @@ public:
|
||||
static const FunctionInfo editor_functions[] = {
|
||||
{100, &IProfileCommon::Store, "Store"},
|
||||
{101, &IProfileCommon::StoreWithImage, "StoreWithImage"},
|
||||
{110, &IProfileCommon::Unknown110, "Unknown110"},
|
||||
};
|
||||
|
||||
RegisterHandlers(editor_functions);
|
||||
@@ -432,6 +447,34 @@ protected:
|
||||
rb.Push(static_cast<u32>(buffer.size()));
|
||||
}
|
||||
|
||||
void Unknown20(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "(STUBBED) called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Unknown21(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "(STUBBED) called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Unknown30(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "(STUBBED) called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Unknown110(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "(STUBBED) called.");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Store(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto base = rp.PopRaw<ProfileBase>();
|
||||
@@ -602,7 +645,8 @@ protected:
|
||||
|
||||
class CheckNetworkServiceAvailabilityAsyncInterface final : public IAsyncContext {
|
||||
public:
|
||||
explicit CheckNetworkServiceAvailabilityAsyncInterface(Core::System& system_) : IAsyncContext{system_} {
|
||||
explicit CheckNetworkServiceAvailabilityAsyncInterface(Core::System& system_)
|
||||
: IAsyncContext{system_} {
|
||||
MarkComplete();
|
||||
}
|
||||
~CheckNetworkServiceAvailabilityAsyncInterface() = default;
|
||||
@@ -621,7 +665,8 @@ protected:
|
||||
|
||||
class EnsureSignedDeviceIdentifierCacheAsyncInterface final : public IAsyncContext {
|
||||
public:
|
||||
explicit EnsureSignedDeviceIdentifierCacheAsyncInterface(Core::System& system_) : IAsyncContext{system_} {
|
||||
explicit EnsureSignedDeviceIdentifierCacheAsyncInterface(Core::System& system_)
|
||||
: IAsyncContext{system_} {
|
||||
MarkComplete();
|
||||
}
|
||||
~EnsureSignedDeviceIdentifierCacheAsyncInterface() = default;
|
||||
@@ -659,7 +704,8 @@ protected:
|
||||
|
||||
class SynchronizeNetworkServiceAccountsSnapshotAsyncInterface final : public IAsyncContext {
|
||||
public:
|
||||
explicit SynchronizeNetworkServiceAccountsSnapshotAsyncInterface(Core::System& system_) : IAsyncContext{system_} {
|
||||
explicit SynchronizeNetworkServiceAccountsSnapshotAsyncInterface(Core::System& system_)
|
||||
: IAsyncContext{system_} {
|
||||
MarkComplete();
|
||||
}
|
||||
~SynchronizeNetworkServiceAccountsSnapshotAsyncInterface() = default;
|
||||
@@ -1302,7 +1348,8 @@ void Module::Interface::ActivateOpenContextRetention(HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<ISessionObject>(system, dummy_uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync(HLERequestContext& ctx) {
|
||||
void Module::Interface::EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
@@ -1366,7 +1413,8 @@ void Module::Interface::SetUserPosition(HLERequestContext& ctx) {
|
||||
const auto position = rp.Pop<u32>();
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, position={}, uuid=0x{}", position, uuid.RawString());
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, position={}, uuid=0x{}", position,
|
||||
uuid.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -1429,7 +1477,8 @@ void Module::Interface::ResumeProcedureToCreateUserWithNintendoAccount(HLEReques
|
||||
rb.PushIpcInterface<IOAuthProcedureForUserRegistration>(system, dummy_uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ResumeProcedureToCreateUserWithNintendoAccountAfterApplyResponse(HLERequestContext& ctx) {
|
||||
void Module::Interface::ResumeProcedureToCreateUserWithNintendoAccountAfterApplyResponse(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
const Common::UUID dummy_uuid{};
|
||||
@@ -1474,7 +1523,8 @@ void Module::Interface::ProxyProcedureForGuestLoginWithNintendoAccount(HLEReques
|
||||
rb.PushIpcInterface<IOAuthProcedureForExternalNsa>(system, dummy_uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ProxyProcedureForFloatingRegistrationWithNintendoAccount(HLERequestContext& ctx) {
|
||||
void Module::Interface::ProxyProcedureForFloatingRegistrationWithNintendoAccount(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
const Common::UUID dummy_uuid{};
|
||||
@@ -1483,7 +1533,8 @@ void Module::Interface::ProxyProcedureForFloatingRegistrationWithNintendoAccount
|
||||
rb.PushIpcInterface<IOAuthProcedureForExternalNsa>(system, dummy_uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ProxyProcedureForDeviceMigrationAuthenticatingOperatingUser(HLERequestContext& ctx) {
|
||||
void Module::Interface::ProxyProcedureForDeviceMigrationAuthenticatingOperatingUser(
|
||||
HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
const Common::UUID dummy_uuid{};
|
||||
@@ -1591,8 +1642,8 @@ void Module::Interface::DebugSetUserStateOpen(HLERequestContext& ctx) {
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_,
|
||||
std::shared_ptr<ProfileManager> profile_manager_,
|
||||
Core::System& system_, const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)}, profile_manager{std::move(
|
||||
profile_manager_)} {}
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)},
|
||||
profile_manager{std::move(profile_manager_)} {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
@@ -1605,18 +1656,17 @@ void LoopProcess(Core::System& system) {
|
||||
std::make_shared<ACC_AA>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService("acc:e",
|
||||
std::make_shared<ACC_E>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService("acc:e:u1",
|
||||
std::make_shared<ACC_E_U1>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService("acc:e:u2",
|
||||
std::make_shared<ACC_E_U2>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService(
|
||||
"acc:e:u1", std::make_shared<ACC_E_U1>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService(
|
||||
"acc:e:u2", std::make_shared<ACC_E_U2>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService("acc:su",
|
||||
std::make_shared<ACC_SU>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService("acc:u0",
|
||||
std::make_shared<ACC_U0>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService("acc:u1",
|
||||
std::make_shared<ACC_U1>(module, profile_manager, system));
|
||||
server_manager->RegisterNamedService("dauth:0",
|
||||
std::make_shared<DAUTH_0>(system));
|
||||
server_manager->RegisterNamedService("dauth:0", std::make_shared<DAUTH_0>(system));
|
||||
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class FrontendApplet;
|
||||
enum class AppletType {
|
||||
Application,
|
||||
LibraryApplet,
|
||||
OverlayApplet,
|
||||
SystemApplet,
|
||||
};
|
||||
|
||||
@@ -212,7 +213,7 @@ struct AppletIdentityInfo {
|
||||
};
|
||||
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
|
||||
|
||||
struct AppletAttribute {
|
||||
struct alignas(8) AppletAttribute {
|
||||
u8 flag;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x7F);
|
||||
};
|
||||
|
||||
@@ -113,6 +113,7 @@ struct Applet {
|
||||
bool is_activity_runnable{};
|
||||
bool is_interactible{true};
|
||||
bool window_visible{true};
|
||||
bool overlay_in_foreground{};
|
||||
|
||||
// Events
|
||||
Event gpu_error_detected_event;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "common/uuid.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applet_data_broker.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
@@ -12,10 +14,13 @@
|
||||
#include "core/hle/service/am/frontend/applet_controller.h"
|
||||
#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
|
||||
#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
@@ -262,6 +267,25 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
m_cv.wait(lk, [&] { return m_pending_process != nullptr; });
|
||||
|
||||
if (true && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
if (auto overlay_process =
|
||||
CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet =
|
||||
std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
overlay_applet->library_applet_mode = LibraryAppletMode::PartialForeground;
|
||||
overlay_applet->window_visible = true;
|
||||
overlay_applet->home_button_short_pressed_blocked = false;
|
||||
overlay_applet->home_button_long_pressed_blocked = false;
|
||||
m_window_system->TrackApplet(overlay_applet, false);
|
||||
overlay_applet->process->Run();
|
||||
LOG_INFO(Service_AM, "called, Overlay applet launched before application (initially "
|
||||
"hidden, watching home button)");
|
||||
}
|
||||
}
|
||||
|
||||
const auto& params = m_pending_parameters;
|
||||
auto applet = std::make_shared<Applet>(m_system, std::move(m_pending_process),
|
||||
params.applet_id == AppletId::Application);
|
||||
@@ -274,6 +298,37 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
// Push UserChannel data from previous application
|
||||
if (params.launch_type == LaunchType::ApplicationInitiated) {
|
||||
applet->user_channel_launch_parameter.swap(m_system.GetUserChannel());
|
||||
|
||||
// Register game NCAs for QLaunch DLC support
|
||||
m_manual_provider.ClearAllEntries();
|
||||
const auto title_id = params.program_id;
|
||||
auto& system_provider = m_system.GetContentProviderUnion();
|
||||
|
||||
LOG_INFO(Service_AM, "QLaunch Support: Registering NCAs for title_id={:016X}", title_id);
|
||||
|
||||
// Register Program NCA
|
||||
auto game_nca = system_provider.GetEntry(title_id, FileSys::ContentRecordType::Program);
|
||||
if (game_nca) {
|
||||
m_manual_provider.AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::ContentRecordType::Program, title_id,
|
||||
game_nca->GetBaseFile());
|
||||
LOG_DEBUG(Service_AM, "Registered Program NCA");
|
||||
} else {
|
||||
LOG_WARNING(Service_AM, "Program NCA not found for title_id={:016X}", title_id);
|
||||
}
|
||||
|
||||
// Register Control NCA
|
||||
auto control_nca = system_provider.GetEntry(title_id, FileSys::ContentRecordType::Control);
|
||||
if (control_nca) {
|
||||
m_manual_provider.AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::ContentRecordType::Control, title_id,
|
||||
control_nca->GetBaseFile());
|
||||
LOG_DEBUG(Service_AM, "Registered Control NCA");
|
||||
}
|
||||
|
||||
// Update the system's manual content provider slot to point to our populated provider
|
||||
system_provider.SetSlot(FileSys::ContentProviderUnionSlot::FrontendManual,
|
||||
&m_manual_provider);
|
||||
}
|
||||
|
||||
// TODO: Read whether we need a preselected user from NACP?
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -46,6 +47,10 @@ public:
|
||||
void OperationModeChanged();
|
||||
|
||||
public:
|
||||
WindowSystem* GetWindowSystem() const {
|
||||
return m_window_system;
|
||||
}
|
||||
|
||||
void SetWindowSystem(WindowSystem* window_system);
|
||||
void SetHomeMenuRequestCallback(std::function<void()> callback);
|
||||
|
||||
@@ -59,6 +64,8 @@ private:
|
||||
|
||||
FrontendAppletParameters m_pending_parameters{};
|
||||
std::unique_ptr<Process> m_pending_process{};
|
||||
|
||||
FileSys::ManualContentProvider m_manual_provider;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -69,6 +69,17 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||
} else {
|
||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
m_managed_display_layers.emplace(*out_layer_id);
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -105,7 +116,16 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// We succeeded, so set up remaining state.
|
||||
m_buffer_sharing_enabled = true;
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = 1;
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = -1;
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -128,10 +148,14 @@ void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||
|
||||
if (m_manager_display_service) {
|
||||
if (m_system_shared_layer_id) {
|
||||
LOG_INFO(Service_VI, "shared_layer={} visible={} applet_id={}",
|
||||
m_system_shared_layer_id, m_visible, static_cast<u32>(m_applet_id));
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
}
|
||||
|
||||
for (const auto layer_id : m_managed_display_layers) {
|
||||
LOG_INFO(Service_VI, "managed_layer={} visible={} applet_id={}", layer_id, m_visible,
|
||||
static_cast<u32>(m_applet_id));
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
|
||||
}
|
||||
}
|
||||
@@ -141,6 +165,22 @@ bool DisplayLayerManager::GetWindowVisibility() const {
|
||||
return m_visible;
|
||||
}
|
||||
|
||||
void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
||||
if (!m_manager_display_service) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_system_shared_layer_id) {
|
||||
m_manager_display_service->SetLayerZIndex(z_index, m_system_shared_layer_id);
|
||||
LOG_INFO(Service_VI, "called, shared_layer={} z={}", m_system_shared_layer_id, z_index);
|
||||
}
|
||||
|
||||
for (const auto layer_id : m_managed_display_layers) {
|
||||
m_manager_display_service->SetLayerZIndex(z_index, layer_id);
|
||||
LOG_INFO(Service_VI, "called, managed_layer={} z={}", layer_id, z_index);
|
||||
}
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
void SetWindowVisibility(bool visible);
|
||||
bool GetWindowVisibility() const;
|
||||
|
||||
void SetOverlayZIndex(s32 z_index);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
||||
|
||||
private:
|
||||
|
||||
@@ -190,6 +190,10 @@ void PhotoViewer::Execute() {
|
||||
case PhotoViewerAppletMode::AllApps:
|
||||
frontend.ShowAllPhotos(callback);
|
||||
break;
|
||||
case PhotoViewerAppletMode::ShowAllFiles:
|
||||
// For now, treat ShowAllFiles the same as AllApps in the HLE stub
|
||||
frontend.ShowAllPhotos(callback);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode);
|
||||
break;
|
||||
|
||||
@@ -46,6 +46,7 @@ private:
|
||||
enum class PhotoViewerAppletMode : u8 {
|
||||
CurrentApp = 0,
|
||||
AllApps = 1,
|
||||
ShowAllFiles = 2,
|
||||
};
|
||||
|
||||
class PhotoViewer final : public FrontendApplet {
|
||||
|
||||
@@ -280,6 +280,9 @@ void WebBrowser::Initialize() {
|
||||
case ShimKind::Lobby:
|
||||
InitializeLobby();
|
||||
break;
|
||||
case ShimKind::Unknown8:
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, Unknown8 Applet is not implemented");
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
|
||||
break;
|
||||
@@ -317,6 +320,9 @@ void WebBrowser::Execute() {
|
||||
case ShimKind::Lobby:
|
||||
ExecuteLobby();
|
||||
break;
|
||||
case ShimKind::Unknown8:
|
||||
WebBrowserExit(WebExitReason::EndButtonPressed);
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
|
||||
WebBrowserExit(WebExitReason::EndButtonPressed);
|
||||
|
||||
@@ -30,6 +30,7 @@ enum class ShimKind : u32 {
|
||||
Web = 5,
|
||||
Wifi = 6,
|
||||
Lobby = 7,
|
||||
Unknown8 = 8,
|
||||
};
|
||||
|
||||
enum class WebExitReason : u32 {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/service/all_system_applet_proxies_service.h"
|
||||
#include "core/hle/service/am/service/applet_alternative_functions.h"
|
||||
#include "core/hle/service/am/service/debug_functions.h"
|
||||
#include "core/hle/service/am/service/library_applet_creator.h"
|
||||
#include "core/hle/service/am/service/library_applet_proxy.h"
|
||||
@@ -31,7 +32,7 @@ IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& sys
|
||||
{400, D<&IAllSystemAppletProxiesService::CreateSelfLibraryAppletCreatorForDevelop>, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
{410, D<&IAllSystemAppletProxiesService::GetSystemAppletControllerForDebug>, "GetSystemAppletControllerForDebug"},
|
||||
{450, D<&IAllSystemAppletProxiesService::GetSystemProcessCommonFunctions>, "GetSystemProcessCommonFunctions"},
|
||||
{460, D<&IAllSystemAppletProxiesService::Cmd460>, "Cmd460"},
|
||||
{460, D<&IAllSystemAppletProxiesService::GetAppletAlternativeFunctions>, "GetAppletAlternativeFunctions"},
|
||||
{1000, D<&IAllSystemAppletProxiesService::GetDebugFunctions>, "GetDebugFunctions"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -73,9 +74,8 @@ Result IAllSystemAppletProxiesService::OpenHomeMenuProxy(
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
|
||||
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
InCopyHandle<Kernel::KProcess> process_handle, AppletAttribute attribute) {
|
||||
LOG_WARNING(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
*out_library_applet_proxy = std::make_shared<ILibraryAppletProxy>(
|
||||
@@ -104,8 +104,7 @@ std::shared_ptr<Applet> IAllSystemAppletProxiesService::GetAppletFromProcessId(
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
||||
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||
InCopyHandle<Kernel::KProcess> process_handle, AppletAttribute attribute) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
if (const auto applet = GetAppletFromProcessId(pid)) {
|
||||
*out_overlay_applet_proxy = std::make_shared<IOverlayAppletProxy>(
|
||||
@@ -137,7 +136,8 @@ Result IAllSystemAppletProxiesService::CreateSelfLibraryAppletCreatorForDevelop(
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
*out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, applet, m_window_system);
|
||||
*out_library_applet_creator =
|
||||
std::make_shared<ILibraryAppletCreator>(system, applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
R_THROW(ResultUnknown);
|
||||
@@ -156,8 +156,11 @@ Result IAllSystemAppletProxiesService::GetSystemProcessCommonFunctions(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAllSystemAppletProxiesService::Cmd460() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
Result IAllSystemAppletProxiesService::GetAppletAlternativeFunctions(
|
||||
Out<SharedPointer<IAppletAlternativeFunctions>> out_applet_alternative_functions) {
|
||||
LOG_WARNING(Service_AM, "called");
|
||||
*out_applet_alternative_functions =
|
||||
std::make_shared<IAppletAlternativeFunctions>(system, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -19,7 +20,9 @@ class ILibraryAppletProxy;
|
||||
class IOverlayAppletProxy;
|
||||
class ISystemAppletProxy;
|
||||
class ISystemApplicationProxy;
|
||||
class ISystemApplicationProxy;
|
||||
class ISystemProcessCommonFunctions;
|
||||
class IAppletAlternativeFunctions;
|
||||
class WindowSystem;
|
||||
|
||||
class IAllSystemAppletProxiesService final
|
||||
@@ -33,29 +36,29 @@ private:
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result OpenHomeMenuProxy(Out<SharedPointer<ISystemAppletProxy>> out_system_applet_proxy,
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result OpenLibraryAppletProxy(Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy,
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
AppletAttribute attribute);
|
||||
Result OpenLibraryAppletProxyOld(
|
||||
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
Result OpenSystemApplicationProxy(Out<SharedPointer<ISystemApplicationProxy>> out_system_application_proxy,
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result CreateSelfLibraryAppletCreatorForDevelop(Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator,
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
AppletAttribute attribute);
|
||||
Result OpenSystemApplicationProxy(
|
||||
Out<SharedPointer<ISystemApplicationProxy>> out_system_application_proxy,
|
||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result CreateSelfLibraryAppletCreatorForDevelop(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
Result GetSystemAppletControllerForDebug();
|
||||
Result GetSystemProcessCommonFunctions(
|
||||
Out<SharedPointer<ISystemProcessCommonFunctions>> out_system_process_common_functions);
|
||||
Result Cmd460();
|
||||
Result GetAppletAlternativeFunctions(
|
||||
Out<SharedPointer<IAppletAlternativeFunctions>> out_applet_alternative_functions);
|
||||
Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/am/service/applet_alternative_functions.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IAppletAlternativeFunctions::IAppletAlternativeFunctions(Core::System& system_,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IAppletAlternativeFunctions"}, m_window_system{window_system} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, D<&IAppletAlternativeFunctions::GetAlternativeTarget>, "GetAlternativeTarget"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IAppletAlternativeFunctions::~IAppletAlternativeFunctions() = default;
|
||||
|
||||
Result IAppletAlternativeFunctions::GetAlternativeTarget(Out<u64> out_target) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_target = 0; // Return 0? Or some applet ID?
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AM {
|
||||
class WindowSystem;
|
||||
|
||||
class IAppletAlternativeFunctions final : public ServiceFramework<IAppletAlternativeFunctions> {
|
||||
public:
|
||||
explicit IAppletAlternativeFunctions(Core::System& system_, WindowSystem& window_system);
|
||||
~IAppletAlternativeFunctions() override;
|
||||
|
||||
private:
|
||||
Result GetAlternativeTarget(Out<u64> out_target);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
@@ -20,7 +20,7 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
|
||||
{20, nullptr, "PushToAppletBoundChannel"},
|
||||
{21, nullptr, "TryPopFromAppletBoundChannel"},
|
||||
{40, nullptr, "GetDisplayLogicalResolution"},
|
||||
{42, nullptr, "SetDisplayMagnification"},
|
||||
{42, D<&IAppletCommonFunctions::SetDisplayMagnification>, "SetDisplayMagnification"},
|
||||
{50, D<&IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled>, "SetHomeButtonDoubleClickEnabled"},
|
||||
{51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"},
|
||||
{52, nullptr, "IsHomeButtonShortPressedBlocked"},
|
||||
@@ -70,6 +70,13 @@ Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAppletCommonFunctions::SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height) {
|
||||
LOG_DEBUG(Service_AM, "(STUBBED) called, x={}, y={}, width={}, height={}", x, y, width, height);
|
||||
std::scoped_lock lk{applet->lock};
|
||||
applet->display_magnification = Common::Rectangle<f32>{x, y, x + width, y + height};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
std::scoped_lock lk{applet->lock};
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
private:
|
||||
Result SetHomeButtonDoubleClickEnabled(bool home_button_double_click_enabled);
|
||||
Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled);
|
||||
Result SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height);
|
||||
Result SetCpuBoostRequestPriority(s32 priority);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
Result IsSystemAppletHomeMenu(Out<bool> out_is_home_menu);
|
||||
|
||||
@@ -39,7 +39,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"},
|
||||
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"},
|
||||
{40, nullptr, "GetCradleFwVersion"},
|
||||
{40, D<&ICommonStateGetter::GetCradleFwVersion>, "GetCradleFwVersion"},
|
||||
{50, D<&ICommonStateGetter::IsVrModeEnabled>, "IsVrModeEnabled"},
|
||||
{51, D<&ICommonStateGetter::SetVrModeEnabled>, "SetVrModeEnabled"},
|
||||
{52, D<&ICommonStateGetter::SetLcdBacklighOffEnabled>, "SetLcdBacklighOffEnabled"},
|
||||
@@ -265,6 +265,17 @@ Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mod
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::GetCradleFwVersion(Out<u32> out_major, Out<u32> out_minor,
|
||||
Out<u32> out_micro) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
*out_major = 0;
|
||||
*out_minor = 0;
|
||||
*out_micro = 0;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::GetAppletLaunchedHistory(
|
||||
Out<s32> out_count, OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
@@ -37,6 +37,7 @@ private:
|
||||
Result GetDefaultDisplayResolutionChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetOperationMode(Out<OperationMode> out_operation_mode);
|
||||
Result GetPerformanceMode(Out<APM::PerformanceMode> out_performance_mode);
|
||||
Result GetCradleFwVersion(Out<u32> out_major, Out<u32> out_minor, Out<u32> out_micro);
|
||||
Result GetBootMode(Out<PM::SystemBootMode> out_boot_mode);
|
||||
Result IsVrModeEnabled(Out<bool> out_is_vr_mode_enabled);
|
||||
Result SetVrModeEnabled(bool is_vr_mode_enabled);
|
||||
|
||||
@@ -106,15 +106,11 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO: enable other versions of applets
|
||||
enum : u8 {
|
||||
Firmware1400 = 14,
|
||||
Firmware1500 = 15,
|
||||
Firmware1600 = 16,
|
||||
Firmware1700 = 17,
|
||||
};
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware1700);
|
||||
// We allow any firmware generation to be loaded as a guest process if possible.
|
||||
// If the emulator lacks the necessary keys, it will fail later and fallback to a stub.
|
||||
const u8 min_gen = 1;
|
||||
const u8 max_gen = 255;
|
||||
auto process = CreateProcess(system, program_id, min_gen, max_gen);
|
||||
if (!process) {
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
@@ -166,8 +162,8 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "ILibraryAppletCreator"},
|
||||
m_window_system{window_system}, m_applet{std::move(applet)} {
|
||||
: ServiceFramework{system_, "ILibraryAppletCreator"}, m_window_system{window_system},
|
||||
m_applet{std::move(applet)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||
#include "core/hle/service/am/service/audio_controller.h"
|
||||
#include "core/hle/service/am/service/common_state_getter.h"
|
||||
#include "core/hle/service/am/service/debug_functions.h"
|
||||
@@ -9,55 +10,33 @@
|
||||
#include "core/hle/service/am/service/home_menu_functions.h"
|
||||
#include "core/hle/service/am/service/library_applet_creator.h"
|
||||
#include "core/hle/service/am/service/overlay_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/overlay_functions.h"
|
||||
#include "core/hle/service/am/service/process_winding_controller.h"
|
||||
#include "core/hle/service/am/service/self_controller.h"
|
||||
#include "core/hle/service/am/service/window_controller.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
// Forward declaration for IOverlayAppletFunctions
|
||||
class IOverlayAppletFunctions final : public ServiceFramework<IOverlayAppletFunctions> {
|
||||
public:
|
||||
explicit IOverlayAppletFunctions(Core::System& system_) : ServiceFramework{system_, "IOverlayAppletFunctions"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "BeginToWatchShortHomeButtonMessage"},
|
||||
{1, nullptr, "EndToWatchShortHomeButtonMessage"},
|
||||
{2, nullptr, "GetApplicationIdForLogo"},
|
||||
{3, nullptr, "SetGpuTimeSliceBoost"},
|
||||
{4, nullptr, "SetAutoSleepTimeAndDimmingTimeEnabled"},
|
||||
{5, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{6, nullptr, "SetHandlingCaptureButtonShortPressedEnabledForOverlayApplet"},
|
||||
{7, nullptr, "SetHandlingCaptureButtonLongPressedEnabledForOverlayApplet"},
|
||||
{8, nullptr, "GetShortHomeButtonMessage"},
|
||||
{9, nullptr, "IsHomeButtonShortPressedBlocked"},
|
||||
{10, nullptr, "IsVrModeCurtainRequired"},
|
||||
{11, nullptr, "SetInputDetectionPolicy"},
|
||||
{20, nullptr, "SetCpuBoostRequestPriority"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
namespace Service::AM {
|
||||
|
||||
IOverlayAppletProxy::IOverlayAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process, WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IOverlayAppletProxy"},
|
||||
m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} {
|
||||
: ServiceFramework{system_, "IOverlayAppletProxy"}, m_window_system{window_system},
|
||||
m_process{process}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IOverlayAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
|
||||
{1, D<&IOverlayAppletProxy::GetSelfController>, "GetSelfController"},
|
||||
{2, D<&IOverlayAppletProxy::GetWindowController>, "GetWindowController"},
|
||||
{3, D<&IOverlayAppletProxy::GetAudioController>, "GetAudioController"},
|
||||
{4, D<&IOverlayAppletProxy::GetDisplayController>, "GetDisplayController"},
|
||||
{11, D<&IOverlayAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"},
|
||||
{20, D<&IOverlayAppletProxy::GetOverlayAppletFunctions>, "GetOverlayAppletFunctions"},
|
||||
{21, D<&IOverlayAppletProxy::GetHomeMenuFunctions>, "GetHomeMenuFunctions"},
|
||||
{22, D<&IOverlayAppletProxy::GetGlobalStateController>, "GetGlobalStateController"},
|
||||
{1000, D<&IOverlayAppletProxy::GetDebugFunctions>, "GetDebugFunctions"},
|
||||
};
|
||||
{0, D<&IOverlayAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
|
||||
{1, D<&IOverlayAppletProxy::GetSelfController>, "GetSelfController"},
|
||||
{2, D<&IOverlayAppletProxy::GetWindowController>, "GetWindowController"},
|
||||
{3, D<&IOverlayAppletProxy::GetAudioController>, "GetAudioController"},
|
||||
{4, D<&IOverlayAppletProxy::GetDisplayController>, "GetDisplayController"},
|
||||
{10, D<&IOverlayAppletProxy::GetProcessWindingController>, "GetProcessWindingController"},
|
||||
{11, D<&IOverlayAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"},
|
||||
{20, D<&IOverlayAppletProxy::GetOverlayFunctions>, "GetOverlayFunctions"},
|
||||
{21, D<&IOverlayAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"},
|
||||
{23, D<&IOverlayAppletProxy::GetGlobalStateController>, "GetGlobalStateController"},
|
||||
{1000, D<&IOverlayAppletProxy::GetDebugFunctions>, "GetDebugFunctions"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
@@ -100,13 +79,6 @@ Result IOverlayAppletProxy::GetDisplayController(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
@@ -115,21 +87,6 @@ Result IOverlayAppletProxy::GetLibraryAppletCreator(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetOverlayAppletFunctions(
|
||||
Out<SharedPointer<IOverlayAppletFunctions>> out_overlay_applet_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_overlay_applet_functions = std::make_shared<IOverlayAppletFunctions>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetHomeMenuFunctions(
|
||||
Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_home_menu_functions =
|
||||
std::make_shared<IHomeMenuFunctions>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController>> out_global_state_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
@@ -144,4 +101,25 @@ Result IOverlayAppletProxy::GetDebugFunctions(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
Result IOverlayAppletProxy::GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController>> out_process_winding_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetOverlayFunctions(
|
||||
Out<SharedPointer<IOverlayFunctions>> out_overlay_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_overlay_functions = std::make_shared<IOverlayFunctions>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetAppletCommonFunctions(
|
||||
Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_applet_common_functions = std::make_shared<IAppletCommonFunctions>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||
#include "core/hle/service/am/service/overlay_functions.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
@@ -25,6 +27,8 @@ class IProcessWindingController;
|
||||
class ISelfController;
|
||||
class IWindowController;
|
||||
class WindowSystem;
|
||||
class IOverlayFunctions;
|
||||
class IAppletCommonFunctions;
|
||||
|
||||
class IOverlayAppletProxy final : public ServiceFramework<IOverlayAppletProxy> {
|
||||
public:
|
||||
@@ -38,11 +42,15 @@ private:
|
||||
Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
|
||||
Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
|
||||
Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
|
||||
Result GetProcessWindingController(Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
|
||||
Result GetLibraryAppletCreator(Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
|
||||
Result GetOverlayAppletFunctions(Out<SharedPointer<IOverlayAppletFunctions>> out_overlay_applet_functions);
|
||||
Result GetHomeMenuFunctions(Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions);
|
||||
Result GetGlobalStateController(Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
|
||||
Result GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
|
||||
Result GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
|
||||
Result GetOverlayFunctions(Out<SharedPointer<IOverlayFunctions>> out_overlay_functions);
|
||||
Result GetAppletCommonFunctions(
|
||||
Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions);
|
||||
Result GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
|
||||
Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
@@ -50,4 +58,4 @@ private:
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
} // namespace Service::AM
|
||||
|
||||
128
src/core/hle/service/am/service/overlay_functions.cpp
Normal file
128
src/core/hle/service/am/service/overlay_functions.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/service/overlay_functions.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
IOverlayFunctions::IOverlayFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "IOverlayFunctions"}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IOverlayFunctions::BeginToWatchShortHomeButtonMessage>, "BeginToWatchShortHomeButtonMessage"},
|
||||
{1, D<&IOverlayFunctions::EndToWatchShortHomeButtonMessage>, "EndToWatchShortHomeButtonMessage"},
|
||||
{2, D<&IOverlayFunctions::GetApplicationIdForLogo>, "GetApplicationIdForLogo"},
|
||||
{3, nullptr, "SetGpuTimeSliceBoost"},
|
||||
{4, D<&IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled>, "SetAutoSleepTimeAndDimmingTimeEnabled"},
|
||||
{5, nullptr, "TerminateApplicationAndSetReason"},
|
||||
{6, nullptr, "SetScreenShotPermissionGlobally"},
|
||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
||||
{30, nullptr, "SetHealthWarningShowingState"},
|
||||
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||
{41, nullptr, "GetApplicationStartupMovie"},
|
||||
{50, nullptr, "SetGpuTimeSliceBoostForApplication"},
|
||||
{60, nullptr, "Unknown60"},
|
||||
{70, D<&IOverlayFunctions::Unknown70>, "Unknown70"},
|
||||
{90, nullptr, "SetRequiresGpuResourceUse"},
|
||||
{101, nullptr, "BeginToObserveHidInputForDevelop"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IOverlayFunctions::~IOverlayFunctions() = default;
|
||||
|
||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = true;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = false;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> target_applet;
|
||||
|
||||
auto* window_system = system.GetAppletManager().GetWindowSystem();
|
||||
if (window_system) {
|
||||
target_applet = window_system->GetMainApplet();
|
||||
if (target_applet) {
|
||||
std::scoped_lock lk{target_applet->lock};
|
||||
LOG_DEBUG(Service_AM, "applet_id={}, program_id={:016X}, type={}",
|
||||
static_cast<u32>(target_applet->applet_id), target_applet->program_id,
|
||||
static_cast<u32>(target_applet->type));
|
||||
|
||||
u64 id = target_applet->screen_shot_identity.application_id;
|
||||
if (id == 0) {
|
||||
id = target_applet->program_id;
|
||||
}
|
||||
LOG_DEBUG(Service_AM, "application_id={:016X}", id);
|
||||
*out_application_id = id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
u64 id = m_applet->screen_shot_identity.application_id;
|
||||
if (id == 0) {
|
||||
id = m_applet->program_id;
|
||||
}
|
||||
LOG_DEBUG(Service_AM, "application_id={:016X} (fallback)", id);
|
||||
*out_application_id = id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, enabled={}", enabled);
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->auto_sleep_disabled = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::IsHealthWarningRequired(Out<bool> is_required) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
*is_required = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->home_button_short_pressed_blocked = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::Unknown70() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::AM
|
||||
30
src/core/hle/service/am/service/overlay_functions.h
Normal file
30
src/core/hle/service/am/service/overlay_functions.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
|
||||
class IOverlayFunctions final : public ServiceFramework<IOverlayFunctions> {
|
||||
public:
|
||||
explicit IOverlayFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
|
||||
~IOverlayFunctions() override;
|
||||
|
||||
private:
|
||||
Result BeginToWatchShortHomeButtonMessage();
|
||||
Result EndToWatchShortHomeButtonMessage();
|
||||
Result GetApplicationIdForLogo(Out<u64> out_application_id);
|
||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||
Result IsHealthWarningRequired(Out<bool> is_required);
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
Result Unknown70();
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
@@ -21,6 +21,12 @@ void WindowSystem::SetEventObserver(EventObserver* observer) {
|
||||
m_system.GetAppletManager().SetWindowSystem(this);
|
||||
}
|
||||
|
||||
void WindowSystem::RequestUpdate() {
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::Update() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
@@ -40,7 +46,10 @@ void WindowSystem::Update() {
|
||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
if (applet->type == AppletType::OverlayApplet) {
|
||||
ASSERT(overlay_display_applet == nullptr);
|
||||
overlay_display_applet = applet;
|
||||
} else if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (is_application) {
|
||||
@@ -320,4 +329,8 @@ void WindowSystem::SetHomeMenuRequestCallback(HomeMenuRequestCallback callback)
|
||||
m_home_menu_request_callback = std::move(callback);
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> WindowSystem::GetOverlayDisplayApplet() {
|
||||
return overlay_display_applet;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -35,11 +35,13 @@ public:
|
||||
public:
|
||||
void SetEventObserver(EventObserver* event_observer);
|
||||
void Update();
|
||||
void RequestUpdate();
|
||||
|
||||
public:
|
||||
void TrackApplet(std::shared_ptr<Applet> applet, bool is_application);
|
||||
std::shared_ptr<Applet> GetByAppletResourceUserId(u64 aruid);
|
||||
std::shared_ptr<Applet> GetMainApplet();
|
||||
std::shared_ptr<Applet> GetOverlayDisplayApplet();
|
||||
|
||||
public:
|
||||
void RequestHomeMenuToGetForeground();
|
||||
@@ -74,6 +76,9 @@ private:
|
||||
// Lock.
|
||||
std::mutex m_lock{};
|
||||
|
||||
// Overlay Display Applet.
|
||||
std::shared_ptr<Applet> overlay_display_applet;
|
||||
|
||||
// Home menu state.
|
||||
bool m_home_menu_foreground_locked{};
|
||||
Applet* m_foreground_requested_applet{};
|
||||
|
||||
@@ -45,6 +45,11 @@ static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
Loader::ResultStatus::Success;
|
||||
}),
|
||||
add_on_content.end());
|
||||
|
||||
LOG_WARNING(Service_AOC, "Accumulated {} AOC title IDs", add_on_content.size());
|
||||
for (const auto& tid : add_on_content) {
|
||||
LOG_WARNING(Service_AOC, "Found AOC: {:016X}", tid);
|
||||
}
|
||||
return add_on_content;
|
||||
}
|
||||
|
||||
@@ -53,8 +58,8 @@ IAddOnContentManager::IAddOnContentManager(Core::System& system_)
|
||||
service_context{system_, "aoc:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CountAddOnContentByApplicationId"},
|
||||
{1, nullptr, "ListAddOnContentByApplicationId"},
|
||||
{0, D<&IAddOnContentManager::CountAddOnContentByApplicationId>, "CountAddOnContentByApplicationId"},
|
||||
{1, D<&IAddOnContentManager::ListAddOnContentByApplicationId>, "ListAddOnContentByApplicationId"},
|
||||
{2, D<&IAddOnContentManager::CountAddOnContent>, "CountAddOnContent"},
|
||||
{3, D<&IAddOnContentManager::ListAddOnContent>, "ListAddOnContent"},
|
||||
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
|
||||
@@ -108,21 +113,85 @@ Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcess
|
||||
Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_addons,
|
||||
u32 offset, u32 count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id.pid);
|
||||
LOG_WARNING(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id.pid);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
LOG_WARNING(Service_AOC, "Filtering AOCs for base title ID: {:016X}", current);
|
||||
for (u64 content_id : add_on_content) {
|
||||
if (FileSys::GetBaseTitleID(content_id) != current) {
|
||||
const auto aoc_base = FileSys::GetBaseTitleID(content_id);
|
||||
if (aoc_base != current) {
|
||||
// LOG_WARNING(Service_AOC, "Skipping AOC {:016X} (Base: {:016X})", content_id,
|
||||
// aoc_base);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_AOC, "Match! AOC {:016X} belongs to current app. Adding to list.",
|
||||
content_id);
|
||||
out.push_back(static_cast<u32>(FileSys::GetAOCID(content_id)));
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_AOC, "DLCs are disabled for this title {:016X}", current);
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
||||
|
||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
|
||||
std::memcpy(out_addons.data(), out.data(), *out_count * sizeof(u32));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CountAddOnContentByApplicationId(Out<u32> out_count,
|
||||
u64 application_id) {
|
||||
LOG_WARNING(Service_AOC, "called. application_id={:016X}", application_id);
|
||||
|
||||
const auto current = application_id;
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
*out_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
*out_count = static_cast<u32>(
|
||||
std::count_if(add_on_content.begin(), add_on_content.end(),
|
||||
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::ListAddOnContentByApplicationId(
|
||||
Out<u32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_addons, u32 offset, u32 count,
|
||||
u64 application_id) {
|
||||
LOG_WARNING(Service_AOC, "called with offset={}, count={}, application_id={:016X}", offset,
|
||||
count, application_id);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(application_id);
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
LOG_WARNING(Service_AOC, "Filtering AOCs for base title ID: {:016X}", current);
|
||||
for (u64 content_id : add_on_content) {
|
||||
const auto aoc_base = FileSys::GetBaseTitleID(content_id);
|
||||
if (aoc_base != current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_AOC, "Match! AOC {:016X} belongs to current app. Adding to list.",
|
||||
content_id);
|
||||
out.push_back(static_cast<u32>(FileSys::GetAOCID(content_id)));
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_AOC, "DLCs are disabled for this title {:016X}", current);
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
|
||||
@@ -29,6 +29,10 @@ public:
|
||||
Result CountAddOnContent(Out<u32> out_count, ClientProcessId process_id);
|
||||
Result ListAddOnContent(Out<u32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_addons,
|
||||
u32 offset, u32 count, ClientProcessId process_id);
|
||||
Result CountAddOnContentByApplicationId(Out<u32> out_count, u64 application_id);
|
||||
Result ListAddOnContentByApplicationId(Out<u32> out_count,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_addons,
|
||||
u32 offset, u32 count, u64 application_id);
|
||||
Result GetAddOnContentBaseId(Out<u64> out_title_id, ClientProcessId process_id);
|
||||
Result PrepareAddOnContent(s32 addon_index, ClientProcessId process_id);
|
||||
Result GetAddOnContentListChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
||||
@@ -53,6 +53,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
|
||||
{8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
|
||||
{10011, nullptr, "SetInternalErrorConversionEnabled"},
|
||||
{50000, nullptr, "LoadMakerNoteInfoForDebug"},
|
||||
{50011, C<&IAlbumAccessorService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
|
||||
{60002, nullptr, "OpenAccessorSession"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -137,6 +138,14 @@ Result IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(
|
||||
R_RETURN(TranslateResult(result));
|
||||
}
|
||||
|
||||
Result IAlbumAccessorService::SetShimLibraryVersion(u64 shim_library_version,
|
||||
u64 applet_resource_user_id) {
|
||||
LOG_WARNING(Service_Capture,
|
||||
"(STUBBED) called, shim_library_version={}, applet_resource_user_id={}",
|
||||
shim_library_version, applet_resource_user_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAlbumAccessorService::TranslateResult(Result in_result) {
|
||||
if (in_result.IsSuccess()) {
|
||||
return in_result;
|
||||
|
||||
@@ -50,6 +50,8 @@ private:
|
||||
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
|
||||
OutArray<u8, BufferAttr_HipcMapAlias> out_buffer);
|
||||
|
||||
Result SetShimLibraryVersion(u64 shim_library_version, u64 applet_resource_user_id);
|
||||
|
||||
Result TranslateResult(Result in_result);
|
||||
|
||||
std::shared_ptr<AlbumManager> manager = nullptr;
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "core/hle/service/filesystem/save_data_controller.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
@@ -226,7 +225,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
||||
|
||||
// Different parent directories - need to move by copying then deleting.
|
||||
// Based on LibHac's approach: create dest, copy contents recursively, delete source.
|
||||
LOG_DEBUG(Service_FS, "Moving directory across tree from \"{}\" to \"{}\"", src_path, dest_path);
|
||||
LOG_DEBUG(Service_FS, "Moving directory across tree from \"{}\" to \"{}\"", src_path,
|
||||
dest_path);
|
||||
|
||||
// Create the destination directory
|
||||
auto dest_parent = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(dest_path));
|
||||
@@ -429,7 +429,8 @@ std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFa
|
||||
!Settings::values.global_custom_save_path.GetValue().empty()) {
|
||||
|
||||
base_save_path_str = Settings::values.global_custom_save_path.GetValue();
|
||||
LOG_INFO(Service_FS, "Save Path: Using Global Custom Save Path as the base: {}", base_save_path_str);
|
||||
LOG_INFO(Service_FS, "Save Path: Using Global Custom Save Path as the base: {}",
|
||||
base_save_path_str);
|
||||
} else {
|
||||
base_save_path_str = Common::FS::GetCitronPathString(CitronPath::NANDDir);
|
||||
LOG_INFO(Service_FS, "Save Path: Using default NAND as the base.");
|
||||
@@ -440,10 +441,11 @@ std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFa
|
||||
// 2. Check for Mirroring.
|
||||
if (Settings::values.mirrored_save_paths.count(program_id)) {
|
||||
LOG_INFO(Service_FS,
|
||||
"Save Path: Mirroring detected for Program ID {:016X}. Syncing against the determined base directory.",
|
||||
"Save Path: Mirroring detected for Program ID {:016X}. Syncing against the "
|
||||
"determined base directory.",
|
||||
program_id);
|
||||
return std::make_shared<FileSys::SaveDataFactory>(system, program_id,
|
||||
std::move(base_directory));
|
||||
std::move(base_directory));
|
||||
}
|
||||
|
||||
// 3. Check for Per-Game Custom Path override.
|
||||
@@ -466,7 +468,6 @@ std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFa
|
||||
LOG_INFO(Service_FS, "Save Path: No overrides found. Using the determined base directory.");
|
||||
return std::make_shared<FileSys::SaveDataFactory>(system, program_id,
|
||||
std::move(base_directory));
|
||||
|
||||
}
|
||||
|
||||
Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
|
||||
@@ -616,7 +617,6 @@ FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
|
||||
|
||||
return sdmc_factory->GetSDMCContents();
|
||||
}
|
||||
|
||||
FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
|
||||
LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
|
||||
|
||||
|
||||
@@ -92,7 +92,6 @@ public:
|
||||
FileSys::RegisteredCache* GetUserNANDContents() const;
|
||||
FileSys::RegisteredCache* GetSDMCContents() const;
|
||||
FileSys::RegisteredCache* GetGameCardContents() const;
|
||||
|
||||
FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const;
|
||||
FileSys::PlaceholderCache* GetUserNANDPlaceholder() const;
|
||||
FileSys::PlaceholderCache* GetSDMCPlaceholder() const;
|
||||
@@ -122,7 +121,9 @@ public:
|
||||
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
|
||||
|
||||
// getter for main.cpp to trigger the sync between custom game paths for separate emulators
|
||||
FileSys::SaveDataFactory& GetSaveDataFactory() { return *global_save_data_factory; }
|
||||
FileSys::SaveDataFactory& GetSaveDataFactory() {
|
||||
return *global_save_data_factory;
|
||||
}
|
||||
|
||||
void Reset();
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
||||
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
|
||||
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
|
||||
{26, nullptr, "FormatSdCardDryRun"},
|
||||
{27, nullptr, "IsExFatSupported"},
|
||||
{27, D<&FSP_SRV::IsExFatSupported>, "IsExFatSupported"},
|
||||
{28, nullptr, "DeleteSaveDataFileSystemBySaveDataAttribute"},
|
||||
{30, D<&FSP_SRV::OpenGameCardStorage>, "OpenGameCardStorage"},
|
||||
{31, D<&FSP_SRV::OpenGameCardFileSystem>, "OpenGameCardFileSystem"},
|
||||
@@ -81,6 +81,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
||||
{34, D<&FSP_SRV::GetCacheStorageSize>, "GetCacheStorageSize"},
|
||||
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
|
||||
{36, nullptr, "OpenHostFileSystemWithOption"},
|
||||
{37, D<&FSP_SRV::OpenSaveDataTransferManager>, "OpenSaveDataTransferManager"},
|
||||
{51, D<&FSP_SRV::OpenSaveDataFileSystem>, "OpenSaveDataFileSystem"},
|
||||
{52, D<&FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId>, "OpenSaveDataFileSystemBySystemSaveDataId"},
|
||||
{53, D<&FSP_SRV::OpenReadOnlySaveDataFileSystem>, "OpenReadOnlySaveDataFileSystem"},
|
||||
@@ -252,8 +253,8 @@ Result FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(
|
||||
Result FSP_SRV::OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
|
||||
FileSys::SaveDataSpaceId space_id,
|
||||
FileSys::SaveDataAttribute attribute) {
|
||||
LOG_INFO(Service_FS, "called, space_id={:02X}, program_id={:016X}",
|
||||
static_cast<u8>(space_id), attribute.program_id);
|
||||
LOG_INFO(Service_FS, "called, space_id={:02X}, program_id={:016X}", static_cast<u8>(space_id),
|
||||
attribute.program_id);
|
||||
|
||||
FileSys::VirtualDir dir{};
|
||||
// This triggers the 'Smart Pull' (Ryujinx -> Citron) in savedata_factory.cpp
|
||||
@@ -278,9 +279,9 @@ Result FSP_SRV::OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
|
||||
|
||||
// Wrap the directory in the IFileSystem interface.
|
||||
// We pass 'save_data_controller->GetFactory()' so the Commit function can find the Mirror.
|
||||
*out_interface = std::make_shared<IFileSystem>(
|
||||
system, std::move(dir), SizeGetter::FromStorageId(fsc, id),
|
||||
save_data_controller->GetFactory(), space_id, attribute);
|
||||
*out_interface =
|
||||
std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id),
|
||||
save_data_controller->GetFactory(), space_id, attribute);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -338,8 +339,7 @@ Result FSP_SRV::WriteSaveDataFileSystemExtraData(InBuffer<BufferAttr_HipcMapAlia
|
||||
FileSys::SaveDataExtraData extra_data{};
|
||||
std::memcpy(&extra_data, buffer.data(), sizeof(FileSys::SaveDataExtraData));
|
||||
|
||||
R_RETURN(save_data_controller->WriteSaveDataExtraData(extra_data, space_id,
|
||||
extra_data.attr));
|
||||
R_RETURN(save_data_controller->WriteSaveDataExtraData(extra_data, space_id, extra_data.attr));
|
||||
}
|
||||
|
||||
Result FSP_SRV::WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
|
||||
@@ -362,8 +362,8 @@ Result FSP_SRV::WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
|
||||
std::memcpy(&extra_data, buffer.data(), sizeof(FileSys::SaveDataExtraData));
|
||||
std::memcpy(&mask, mask_buffer.data(), sizeof(FileSys::SaveDataExtraData));
|
||||
|
||||
R_RETURN(
|
||||
save_data_controller->WriteSaveDataExtraDataWithMask(extra_data, mask, space_id, attribute));
|
||||
R_RETURN(save_data_controller->WriteSaveDataExtraDataWithMask(extra_data, mask, space_id,
|
||||
attribute));
|
||||
}
|
||||
|
||||
Result FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
|
||||
@@ -415,7 +415,7 @@ Result FSP_SRV::ReadSaveDataFileSystemExtraData(OutBuffer<BufferAttr_HipcMapAlia
|
||||
|
||||
FileSys::SaveDataExtraData extra_data{};
|
||||
R_TRY(save_data_controller->ReadSaveDataExtraData(&extra_data, FileSys::SaveDataSpaceId::User,
|
||||
attribute));
|
||||
attribute));
|
||||
|
||||
std::memcpy(out_buffer.data(), &extra_data, sizeof(FileSys::SaveDataExtraData));
|
||||
R_SUCCEED();
|
||||
@@ -660,7 +660,8 @@ Result FSP_SRV::OpenSaveDataTransferManager(OutInterface<ISaveDataTransferManage
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FSP_SRV::OpenSaveDataTransferManagerVersion2(OutInterface<ISaveDataTransferManager> out_interface) {
|
||||
Result FSP_SRV::OpenSaveDataTransferManagerVersion2(
|
||||
OutInterface<ISaveDataTransferManager> out_interface) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
*out_interface = std::make_shared<ISaveDataTransferManager>(system);
|
||||
@@ -690,18 +691,28 @@ Result FSP_SRV::DeleteSaveDataFileSystem(u64 save_data_id) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FSP_SRV::OpenGameCardStorage(OutInterface<IStorage> out_interface, u32 handle, u32 partition_id) {
|
||||
Result FSP_SRV::OpenGameCardStorage(OutInterface<IStorage> out_interface, u32 handle,
|
||||
u32 partition_id) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, handle={}, partition_id={}", handle, partition_id);
|
||||
|
||||
// Would need to open game card storage for the specified handle and partition
|
||||
R_THROW(FileSys::ResultTargetNotFound);
|
||||
}
|
||||
|
||||
Result FSP_SRV::OpenGameCardFileSystem(OutInterface<IFileSystem> out_interface, u32 handle, u32 partition_id) {
|
||||
Result FSP_SRV::OpenGameCardFileSystem(OutInterface<IFileSystem> out_interface, u32 handle,
|
||||
u32 partition_id) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, handle={}, partition_id={}", handle, partition_id);
|
||||
|
||||
// Would need to open game card filesystem for the specified handle and partition
|
||||
R_THROW(FileSys::ResultTargetNotFound);
|
||||
}
|
||||
|
||||
Result FSP_SRV::IsExFatSupported(Out<bool> out_is_supported) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
*out_is_supported = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -116,12 +116,15 @@ private:
|
||||
Result OpenSdCardDetectionEventNotifier(OutInterface<IEventNotifier> out_interface);
|
||||
Result OpenGameCardDetectionEventNotifier(OutInterface<IEventNotifier> out_interface);
|
||||
Result OpenSaveDataTransferManager(OutInterface<ISaveDataTransferManager> out_interface);
|
||||
Result OpenSaveDataTransferManagerVersion2(OutInterface<ISaveDataTransferManager> out_interface);
|
||||
Result OpenSaveDataTransferManagerVersion2(
|
||||
OutInterface<ISaveDataTransferManager> out_interface);
|
||||
Result OpenBisWiper(OutInterface<IWiper> out_interface);
|
||||
Result OpenBisStorage(OutInterface<IStorage> out_interface, u32 partition_id);
|
||||
Result DeleteSaveDataFileSystem(u64 save_data_id);
|
||||
Result OpenGameCardStorage(OutInterface<IStorage> out_interface, u32 handle, u32 partition_id);
|
||||
Result OpenGameCardFileSystem(OutInterface<IFileSystem> out_interface, u32 handle, u32 partition_id);
|
||||
Result OpenGameCardFileSystem(OutInterface<IFileSystem> out_interface, u32 handle,
|
||||
u32 partition_id);
|
||||
Result IsExFatSupported(Out<bool> out_is_supported);
|
||||
|
||||
FileSystemController& fsc;
|
||||
const FileSys::ContentProvider& content_provider;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
@@ -172,8 +173,8 @@ constexpr Result ResultNetworkCommunicationDisabled{ErrorModule::NIFM, 1111};
|
||||
|
||||
class IScanRequest final : public ServiceFramework<IScanRequest> {
|
||||
public:
|
||||
explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"},
|
||||
service_context{system_, "IScanRequest"} {
|
||||
explicit IScanRequest(Core::System& system_)
|
||||
: ServiceFramework{system_, "IScanRequest"}, service_context{system_, "IScanRequest"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IScanRequest::Submit, "Submit"},
|
||||
@@ -604,6 +605,41 @@ void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IGeneralService::EnumerateNetworkInterfaces(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
const auto adapters = Network::GetAvailableNetworkInterfaces();
|
||||
constexpr size_t kEntrySize = 0x3F0; // From official
|
||||
|
||||
std::vector<u8> blob(adapters.size() * kEntrySize, 0);
|
||||
|
||||
for (size_t i = 0; i < adapters.size(); ++i) {
|
||||
const auto& host = adapters[i];
|
||||
u8* const base = blob.data() + i * kEntrySize;
|
||||
|
||||
// Match expected structure
|
||||
// Citron NetworkInterface doesn't have type/kind, so we use 2u (Ethernet) as a safe default
|
||||
*reinterpret_cast<u32*>(base + 0x0) = 2u;
|
||||
*reinterpret_cast<u32*>(base + 0x4) = 1u; // Status?
|
||||
|
||||
std::memcpy(base + 0x18, &host.ip_address, sizeof(host.ip_address));
|
||||
std::memcpy(base + 0x1C, &host.subnet_mask, sizeof(host.subnet_mask));
|
||||
std::memcpy(base + 0x20, &host.gateway, sizeof(host.gateway));
|
||||
|
||||
std::string name_utf8 = host.name;
|
||||
name_utf8.resize(0x110, '\0');
|
||||
std::memcpy(base + 0x2E0, name_utf8.data(), 0x110);
|
||||
}
|
||||
|
||||
if (ctx.GetWriteBufferSize() > 0 && !blob.empty()) {
|
||||
ctx.WriteBuffer(blob.data(), std::min(ctx.GetWriteBufferSize(), blob.size()));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(adapters.size()));
|
||||
}
|
||||
|
||||
void IGeneralService::RemoveNetworkProfile(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
@@ -691,6 +727,31 @@ void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) {
|
||||
rb.PushRaw<IpConfigInfo>(ip_config_info);
|
||||
}
|
||||
|
||||
void IGeneralService::EnumerateNetworkProfiles(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
// Return 0 profiles for now (stub)
|
||||
ctx.WriteBuffer(std::span<u8>{});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(0); // Number of profiles
|
||||
}
|
||||
|
||||
void IGeneralService::ConfirmSystemAvailability(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IGeneralService::SetBackgroundRequestEnabled(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IGeneralService::IsWirelessCommunicationEnabled(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
@@ -750,6 +811,16 @@ void IGeneralService::IsAnyForegroundRequestAccepted(HLERequestContext& ctx) {
|
||||
rb.Push<u8>(is_accepted);
|
||||
}
|
||||
|
||||
void IGeneralService::GetSsidListVersion(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
constexpr u32 ssid_list_version = 0;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(ssid_list_version);
|
||||
}
|
||||
|
||||
void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
@@ -846,7 +917,8 @@ void IGeneralService::IsNetworkEmulationFeatureEnabled(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IGeneralService::SelectActiveNetworkEmulationProfileIdForDebug(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called SelectActiveNetworkEmulationProfileIdForDebug [18.0.0+]");
|
||||
LOG_WARNING(Service_NIFM,
|
||||
"(STUBBED) called SelectActiveNetworkEmulationProfileIdForDebug [18.0.0+]");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -898,7 +970,8 @@ void IGeneralService::DestroyRewriteRule(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void IGeneralService::IsActiveNetworkEmulationProfileIdSelected(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called IsActiveNetworkEmulationProfileIdSelected [20.0.0+]");
|
||||
LOG_WARNING(Service_NIFM,
|
||||
"(STUBBED) called IsActiveNetworkEmulationProfileIdSelected [20.0.0+]");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -935,8 +1008,8 @@ IGeneralService::IGeneralService(Core::System& system_)
|
||||
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
|
||||
{4, &IGeneralService::CreateRequest, "CreateRequest"},
|
||||
{5, &IGeneralService::GetCurrentNetworkProfile, "GetCurrentNetworkProfile"},
|
||||
{6, nullptr, "EnumerateNetworkInterfaces"},
|
||||
{7, nullptr, "EnumerateNetworkProfiles"},
|
||||
{6, &IGeneralService::EnumerateNetworkInterfaces, "EnumerateNetworkInterfaces"},
|
||||
{7, &IGeneralService::EnumerateNetworkProfiles, "EnumerateNetworkProfiles"},
|
||||
{8, &IGeneralService::GetNetworkProfile, "GetNetworkProfile"},
|
||||
{9, &IGeneralService::SetNetworkProfile, "SetNetworkProfile"},
|
||||
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
|
||||
@@ -954,7 +1027,7 @@ IGeneralService::IGeneralService(Core::System& system_)
|
||||
{22, &IGeneralService::IsAnyForegroundRequestAccepted, "IsAnyForegroundRequestAccepted"},
|
||||
{23, nullptr, "PutToSleep"},
|
||||
{24, nullptr, "WakeUp"},
|
||||
{25, nullptr, "GetSsidListVersion"},
|
||||
{25, &IGeneralService::GetSsidListVersion, "GetSsidListVersion"},
|
||||
{26, nullptr, "SetExclusiveClient"},
|
||||
{27, nullptr, "GetDefaultIpSetting"},
|
||||
{28, nullptr, "SetDefaultIpSetting"},
|
||||
@@ -962,8 +1035,8 @@ IGeneralService::IGeneralService(Core::System& system_)
|
||||
{30, nullptr, "SetEthernetCommunicationEnabledForTest"},
|
||||
{31, nullptr, "GetTelemetorySystemEventReadableHandle"},
|
||||
{32, nullptr, "GetTelemetryInfo"},
|
||||
{33, nullptr, "ConfirmSystemAvailability"},
|
||||
{34, nullptr, "SetBackgroundRequestEnabled"},
|
||||
{33, &IGeneralService::ConfirmSystemAvailability, "ConfirmSystemAvailability"},
|
||||
{34, &IGeneralService::SetBackgroundRequestEnabled, "SetBackgroundRequestEnabled"},
|
||||
{35, &IGeneralService::GetScanData, "GetScanData"},
|
||||
{36, &IGeneralService::GetCurrentAccessPoint, "GetCurrentAccessPoint"},
|
||||
{37, &IGeneralService::Shutdown, "Shutdown"},
|
||||
|
||||
@@ -28,6 +28,7 @@ private:
|
||||
void CreateScanRequest(HLERequestContext& ctx);
|
||||
void CreateRequest(HLERequestContext& ctx);
|
||||
void GetCurrentNetworkProfile(HLERequestContext& ctx);
|
||||
void EnumerateNetworkInterfaces(HLERequestContext& ctx);
|
||||
void RemoveNetworkProfile(HLERequestContext& ctx);
|
||||
void GetCurrentIpAddress(HLERequestContext& ctx);
|
||||
void CreateTemporaryNetworkProfile(HLERequestContext& ctx);
|
||||
@@ -37,6 +38,7 @@ private:
|
||||
void IsEthernetCommunicationEnabled(HLERequestContext& ctx);
|
||||
void IsAnyInternetRequestAccepted(HLERequestContext& ctx);
|
||||
void IsAnyForegroundRequestAccepted(HLERequestContext& ctx);
|
||||
void GetSsidListVersion(HLERequestContext& ctx);
|
||||
void SetWowlDelayedWakeTime(HLERequestContext& ctx);
|
||||
void GetNetworkProfile(HLERequestContext& ctx);
|
||||
void SetNetworkProfile(HLERequestContext& ctx);
|
||||
@@ -47,6 +49,9 @@ private:
|
||||
void SetAcceptableNetworkTypeFlag(HLERequestContext& ctx);
|
||||
void GetAcceptableNetworkTypeFlag(HLERequestContext& ctx);
|
||||
void NotifyConnectionStateChanged(HLERequestContext& ctx);
|
||||
void EnumerateNetworkProfiles(HLERequestContext& ctx);
|
||||
void ConfirmSystemAvailability(HLERequestContext& ctx);
|
||||
void SetBackgroundRequestEnabled(HLERequestContext& ctx);
|
||||
void SetWowlTcpKeepAliveTimeout(HLERequestContext& ctx);
|
||||
void IsWiredConnectionAvailable(HLERequestContext& ctx);
|
||||
void IsNetworkEmulationFeatureEnabled(HLERequestContext& ctx);
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{5, C<&INpnsSystem::GetReceiveEvent>, "GetReceiveEvent"},
|
||||
{6, nullptr, "ListenUndelivered"},
|
||||
{7, nullptr, "GetStateChangeEvent"},
|
||||
{8, nullptr, "ListenToByName"},
|
||||
{8, C<&INpnsSystem::ListenToByName>, "ListenToByName"},
|
||||
{11, nullptr, "SubscribeTopic"},
|
||||
{12, nullptr, "UnsubscribeTopic"},
|
||||
{13, nullptr, "QueryIsTopicExist"},
|
||||
@@ -64,10 +64,10 @@ public:
|
||||
{70, nullptr, "UnknownCmd70"},
|
||||
{101, nullptr, "Suspend"},
|
||||
{102, nullptr, "Resume"},
|
||||
{103, nullptr, "GetState"},
|
||||
{103, C<&INpnsSystem::GetState>, "GetState"},
|
||||
{104, nullptr, "GetStatistics"},
|
||||
{105, nullptr, "GetPlayReportRequestEvent"},
|
||||
{106, nullptr, "GetLastNotifiedTime"},
|
||||
{106, C<&INpnsSystem::GetLastNotifiedTime>, "GetLastNotifiedTime"},
|
||||
{107, nullptr, "SetLastNotifiedTime"},
|
||||
{111, nullptr, "GetJid"},
|
||||
{112, nullptr, "CreateJid"},
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
{154, nullptr, "CreateTokenAsync"},
|
||||
{155, nullptr, "CreateTokenAsyncWithApplicationId"},
|
||||
{156, nullptr, "CreateTokenWithNameAsync"},
|
||||
{161, nullptr, "GetRequestChangeStateCancelEvent"},
|
||||
{161, C<&INpnsSystem::GetRequestChangeStateCancelEvent>, "GetRequestChangeStateCancelEvent"},
|
||||
{162, nullptr, "RequestChangeStateForceTimedWithCancelEvent"},
|
||||
{201, nullptr, "RequestChangeStateForceTimed"},
|
||||
{202, nullptr, "RequestChangeStateForceAsync"},
|
||||
@@ -105,10 +105,13 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
get_receive_event = service_context.CreateEvent("npns:s:GetReceiveEvent");
|
||||
get_request_change_state_cancel_event =
|
||||
service_context.CreateEvent("npns:s:GetRequestChangeStateCancelEvent");
|
||||
}
|
||||
|
||||
~INpnsSystem() override {
|
||||
service_context.CloseEvent(get_receive_event);
|
||||
service_context.CloseEvent(get_request_change_state_cancel_event);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -124,29 +127,54 @@ private:
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetState(Out<u32> out_state) {
|
||||
LOG_INFO(Service_NPNS, "called");
|
||||
*out_state = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetLastNotifiedTime(Out<s64> out_last_notified_time) {
|
||||
LOG_INFO(Service_NPNS, "called");
|
||||
*out_last_notified_time = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetRequestChangeStateCancelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_INFO(Service_NPNS, "called");
|
||||
*out_event = &get_request_change_state_cancel_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ListenToByName() {
|
||||
LOG_DEBUG(Service_NPNS, "(STUBBED) called.");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* get_receive_event;
|
||||
Kernel::KEvent* get_request_change_state_cancel_event;
|
||||
};
|
||||
|
||||
class INpnsUser final : public ServiceFramework<INpnsUser> {
|
||||
public:
|
||||
explicit INpnsUser(Core::System& system_) : ServiceFramework{system_, "npns:u"} {
|
||||
explicit INpnsUser(Core::System& system_)
|
||||
: ServiceFramework{system_, "npns:u"}, service_context{system, "npns:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "ListenAll"},
|
||||
{2, nullptr, "ListenTo"},
|
||||
{3, nullptr, "Receive"},
|
||||
{4, nullptr, "ReceiveRaw"},
|
||||
{5, nullptr, "GetReceiveEvent"},
|
||||
{5, C<&INpnsUser::GetReceiveEvent>, "GetReceiveEvent"},
|
||||
{7, nullptr, "GetStateChangeEvent"},
|
||||
{8, nullptr, "ListenToByName"},
|
||||
{8, C<&INpnsUser::ListenToByName>, "ListenToByName"},
|
||||
{21, nullptr, "CreateToken"},
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
{26, nullptr, "ListenToMyApplicationId"},
|
||||
{101, nullptr, "Suspend"},
|
||||
{102, nullptr, "Resume"},
|
||||
{103, nullptr, "GetState"},
|
||||
{103, C<&INpnsUser::GetState>, "GetState"},
|
||||
{104, nullptr, "GetStatistics"},
|
||||
{111, nullptr, "GetJid"},
|
||||
{120, nullptr, "CreateNotificationReceiver"},
|
||||
@@ -158,7 +186,37 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
get_receive_event = service_context.CreateEvent("npns:u:GetReceiveEvent");
|
||||
}
|
||||
|
||||
~INpnsUser() override {
|
||||
service_context.CloseEvent(get_receive_event);
|
||||
}
|
||||
|
||||
private:
|
||||
Result ListenToByName(InBuffer<BufferAttr_HipcMapAlias> name_buffer) {
|
||||
const std::string name(reinterpret_cast<const char*>(name_buffer.data()),
|
||||
name_buffer.size());
|
||||
LOG_DEBUG(Service_NPNS, "called, name={}", name);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetReceiveEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_NPNS, "called");
|
||||
|
||||
*out_event = &get_receive_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetState(Out<u32> out_state) {
|
||||
LOG_INFO(Service_NPNS, "called");
|
||||
*out_state = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* get_receive_event;
|
||||
};
|
||||
|
||||
class INotificationReceiver : public ServiceFramework<INotificationReceiver> {
|
||||
@@ -198,8 +256,7 @@ public:
|
||||
|
||||
class IFuture : public ServiceFramework<IFuture> {
|
||||
public:
|
||||
explicit IFuture(Core::System& system_, const char* name)
|
||||
: ServiceFramework{system_, name} {
|
||||
explicit IFuture(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
|
||||
// TODO: Implement functions based on documentation
|
||||
// Cmd 1: (No name)
|
||||
// Cmd 2: (No name)
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/ns/application_manager_interface.h"
|
||||
#include "core/hle/service/ns/content_management_interface.h"
|
||||
#include "core/hle/service/ns/ns_types.h"
|
||||
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
|
||||
|
||||
: ServiceFramework{system_, "IApplicationManagerInterface"},
|
||||
service_context{system, "IApplicationManagerInterface"},
|
||||
record_update_system_event{service_context}, sd_card_mount_status_event{service_context},
|
||||
@@ -20,7 +22,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"},
|
||||
{1, nullptr, "GenerateApplicationRecordCount"},
|
||||
{1, D<&IApplicationManagerInterface::GenerateApplicationRecordCount>, "GenerateApplicationRecordCount"},
|
||||
{2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
|
||||
{3, nullptr, "GetApplicationViewDeprecated"},
|
||||
{4, nullptr, "DeleteApplicationEntity"},
|
||||
@@ -123,8 +125,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{404, nullptr, "InvalidateApplicationControlCache"},
|
||||
{405, nullptr, "ListApplicationControlCacheEntryInfo"},
|
||||
{406, nullptr, "GetApplicationControlProperty"},
|
||||
{407, nullptr, "ListApplicationTitle"},
|
||||
{407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"},
|
||||
{408, nullptr, "ListApplicationIcon"},
|
||||
{419, D<&IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground>, "RequestDownloadApplicationControlDataInBackground"},
|
||||
{502, nullptr, "RequestCheckGameCardRegistration"},
|
||||
{503, nullptr, "RequestGameCardRegistrationGoldPoint"},
|
||||
{504, nullptr, "RequestRegisterGameCard"},
|
||||
@@ -134,15 +137,17 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{508, nullptr, "GetLastGameCardMountFailureResult"},
|
||||
{509, nullptr, "ListApplicationIdOnGameCard"},
|
||||
{510, nullptr, "GetGameCardPlatformRegion"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{600, D<&IApplicationManagerInterface::CountApplicationContentMeta>, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{602, nullptr, "ListAvailableAddOnContent"},
|
||||
{602, D<&IApplicationManagerInterface::ListAvailableAddOnContent>, "ListAvailableAddOnContent"},
|
||||
|
||||
{603, nullptr, "GetOwnedApplicationContentMetaStatus"},
|
||||
{604, nullptr, "RegisterContentsExternalKey"},
|
||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||
{606, nullptr, "GetContentMetaStorage"},
|
||||
{607, nullptr, "ListAvailableAddOnContent"},
|
||||
{607, D<&IApplicationManagerInterface::ListAvailableAddOnContent>, "ListAvailableAddOnContent"},
|
||||
{609, nullptr, "ListAvailabilityAssuredAddOnContent"},
|
||||
|
||||
{610, nullptr, "GetInstalledContentMetaStorage"},
|
||||
{611, nullptr, "PrepareAddOnContent"},
|
||||
{700, nullptr, "PushDownloadTaskList"},
|
||||
@@ -207,6 +212,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{1703, nullptr, "GetApplicationViewDownloadErrorContext"},
|
||||
{1704, D<&IApplicationManagerInterface::GetApplicationViewWithPromotionInfo>, "GetApplicationViewWithPromotionInfo"},
|
||||
{1705, nullptr, "IsPatchAutoDeletableApplication"},
|
||||
{1706, D<&IApplicationManagerInterface::GetApplicationViewDeprecated>, "GetApplicationView"},
|
||||
{1800, nullptr, "IsNotificationSetupCompleted"},
|
||||
{1801, nullptr, "GetLastNotificationInfoCount"},
|
||||
{1802, nullptr, "ListLastNotificationInfo"},
|
||||
@@ -338,7 +344,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{4039, nullptr, "Cmd4039"},
|
||||
{4040, nullptr, "Cmd4040"},
|
||||
{4041, nullptr, "Cmd4041"},
|
||||
{4042, nullptr, "Cmd4042"},
|
||||
{4041, nullptr, "Cmd4041"},
|
||||
{4042, D<&IApplicationManagerInterface::Cmd4042>, "Cmd4042"},
|
||||
{4043, nullptr, "Cmd4043"},
|
||||
{4043, nullptr, "Cmd4043"},
|
||||
{4044, nullptr, "Cmd4044"},
|
||||
{4045, nullptr, "Cmd4045"},
|
||||
@@ -347,7 +355,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{4050, nullptr, "Cmd4050"},
|
||||
{4051, nullptr, "Cmd4051"},
|
||||
{4052, nullptr, "Cmd4052"},
|
||||
{4053, nullptr, "Cmd4053"},
|
||||
{4053, D<&IApplicationManagerInterface::Cmd4053>, "Cmd4053"},
|
||||
{4054, nullptr, "Cmd4054"},
|
||||
{4055, nullptr, "Cmd4055"},
|
||||
{4056, nullptr, "Cmd4056"},
|
||||
@@ -403,58 +411,89 @@ IApplicationManagerInterface::~IApplicationManagerInterface() = default;
|
||||
Result IApplicationManagerInterface::GetApplicationControlData(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source, u64 application_id) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData(
|
||||
out_buffer, out_actual_size, application_control_source, application_id));
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetApplicationDesiredLanguage(
|
||||
Out<ApplicationLanguage> out_desired_language, u32 supported_languages) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationDesiredLanguage(
|
||||
out_desired_language, supported_languages));
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::ConvertApplicationLanguageToLanguageCode(
|
||||
Out<u64> out_language_code, ApplicationLanguage application_language) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
R_RETURN(
|
||||
IReadOnlyApplicationControlDataInterface(system).ConvertApplicationLanguageToLanguageCode(
|
||||
out_language_code, application_language));
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GenerateApplicationRecordCount(Out<s32> out_count) {
|
||||
const auto& cache = system.GetContentProviderUnion();
|
||||
const auto installed_games = cache.ListEntriesFilterOrigin(
|
||||
std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
|
||||
|
||||
s32 count = 0;
|
||||
for (const auto& [slot, game] : installed_games) {
|
||||
if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
|
||||
continue;
|
||||
}
|
||||
if ((game.title_id & 0xFFF) != 0) {
|
||||
continue; // skip sub-programs
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
LOG_INFO(Service_NS, "called, found {} application records", count);
|
||||
*out_count = count;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::ListApplicationRecord(
|
||||
OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count,
|
||||
s32 offset) {
|
||||
const auto limit = out_records.size();
|
||||
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
const auto& cache = system.GetContentProviderUnion();
|
||||
const auto installed_games = cache.ListEntriesFilterOrigin(
|
||||
std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
|
||||
|
||||
size_t i = 0;
|
||||
u8 ii = 24;
|
||||
std::vector<ApplicationRecord> records;
|
||||
records.reserve(installed_games.size());
|
||||
|
||||
for (const auto& [slot, game] : installed_games) {
|
||||
if (i >= limit) {
|
||||
break;
|
||||
}
|
||||
if (game.title_id == 0 || game.title_id < 0x0100000000001FFFull) {
|
||||
continue;
|
||||
}
|
||||
if (offset > 0) {
|
||||
offset--;
|
||||
continue;
|
||||
if ((game.title_id & 0xFFF) != 0) {
|
||||
continue; // skip sub-programs
|
||||
}
|
||||
|
||||
ApplicationRecord record{};
|
||||
record.application_id = game.title_id;
|
||||
record.type = ApplicationRecordType::Installed;
|
||||
record.unknown = 0; // 2 = needs update
|
||||
record.unknown2 = ii++;
|
||||
record.attributes = 0;
|
||||
record.last_updated = 0; // TODO: Implement launch timestamp tracking
|
||||
|
||||
out_records[i++] = record;
|
||||
records.push_back(record);
|
||||
}
|
||||
|
||||
LOG_INFO(Service_NS, "called, offset={} limit={} total_found={}", offset, limit,
|
||||
records.size());
|
||||
|
||||
// Sort by Title ID for now as a stable order
|
||||
std::sort(records.begin(), records.end(),
|
||||
[](const ApplicationRecord& lhs, const ApplicationRecord& rhs) {
|
||||
return lhs.application_id < rhs.application_id;
|
||||
});
|
||||
|
||||
size_t i = 0;
|
||||
const size_t start = static_cast<size_t>(std::max(0, offset));
|
||||
for (size_t j = start; j < records.size() && i < limit; ++j) {
|
||||
out_records[i++] = records[j];
|
||||
}
|
||||
|
||||
*out_count = static_cast<s32>(i);
|
||||
@@ -463,7 +502,7 @@ Result IApplicationManagerInterface::ListApplicationRecord(
|
||||
|
||||
Result IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
|
||||
record_update_system_event.Signal();
|
||||
*out_event = record_update_system_event.GetHandle();
|
||||
@@ -473,29 +512,52 @@ Result IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent(
|
||||
|
||||
Result IApplicationManagerInterface::GetGameCardMountFailureEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_event = gamecard_mount_failure_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||
Out<bool> out_is_any_application_entity_installed) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_is_any_application_entity_installed = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetApplicationView(
|
||||
OutArray<ApplicationViewV20, BufferAttr_HipcMapAlias> out_application_views,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||
const auto size = std::min(out_application_views.size(), application_ids.size());
|
||||
LOG_INFO(Service_NS, "called, size={}", application_ids.size());
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
ApplicationViewV20 view{};
|
||||
view.application_id = application_ids[i];
|
||||
view.version = 0; // TODO: Get actual version
|
||||
view.flags = 0x401f17; // Typical flags for installed app
|
||||
view.unk = 0;
|
||||
view.download_state = {0, 0, 0, 0, 0, {0, 0}, 0};
|
||||
view.download_progress = {0, 0, 0, 0, 0, {0, 0}, 0};
|
||||
|
||||
out_application_views[i] = view;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
|
||||
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||
const auto size = std::min(out_application_views.size(), application_ids.size());
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
|
||||
LOG_INFO(Service_NS, "called (deprecated v19), size={}", application_ids.size());
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
ApplicationView view{};
|
||||
view.application_id = application_ids[i];
|
||||
view.unk = 0x70000;
|
||||
view.version = 0;
|
||||
view.flags = 0x401f17;
|
||||
view.download_state = {0, 0, 0, 0, 0, {0, 0}, 0};
|
||||
view.download_progress = {0, 0, 0, 0, 0, {0, 0}, 0};
|
||||
|
||||
out_application_views[i] = view;
|
||||
}
|
||||
@@ -504,21 +566,39 @@ Result IApplicationManagerInterface::GetApplicationView(
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
|
||||
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_count,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||
const auto size = std::min(out_application_views.size(), application_ids.size());
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called, size={}", application_ids.size());
|
||||
const auto size = application_ids.size();
|
||||
LOG_INFO(Service_NS, "called, size={}", size);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
ApplicationViewWithPromotionInfo view{};
|
||||
view.view.application_id = application_ids[i];
|
||||
view.view.unk = 0x70000;
|
||||
view.view.flags = 0x401f17;
|
||||
view.promotion = {};
|
||||
// Using 0x78 per entry (V20 + PromotionInfo)
|
||||
constexpr size_t entry_size = 0x58 + 0x20;
|
||||
const size_t limit = out_buffer.size() / entry_size;
|
||||
const size_t actual_count = std::min(size, limit);
|
||||
|
||||
out_application_views[i] = view;
|
||||
for (size_t i = 0; i < actual_count; i++) {
|
||||
ApplicationViewV20 view{};
|
||||
view.application_id = application_ids[i];
|
||||
view.version = 0;
|
||||
view.flags = 0x401f17;
|
||||
view.unk = 0;
|
||||
|
||||
PromotionInfo promotion{};
|
||||
|
||||
const size_t offset = i * entry_size;
|
||||
std::memcpy(out_buffer.data() + offset, &view, sizeof(view));
|
||||
std::memcpy(out_buffer.data() + offset + sizeof(view), &promotion, sizeof(promotion));
|
||||
}
|
||||
|
||||
*out_count = static_cast<u32>(actual_count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground(
|
||||
u64 control_source, u64 application_id) {
|
||||
LOG_INFO(Service_NS, "called, control_source={}, application_id={:016X}", control_source,
|
||||
application_id);
|
||||
// Success allows QLaunch to continue even if we don't actually download anything
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -545,32 +625,32 @@ Result IApplicationManagerInterface::GetApplicationRightsOnClient(
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::CheckSdCardMountStatus() {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
R_RETURN(IContentManagementInterface(system).CheckSdCardMountStatus());
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetSdCardMountStatusChangedEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_event = sd_card_mount_status_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetFreeSpaceSize(Out<s64> out_free_space_size,
|
||||
FileSys::StorageId storage_id) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
R_RETURN(IContentManagementInterface(system).GetFreeSpaceSize(out_free_space_size, storage_id));
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetGameCardUpdateDetectionEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_event = gamecard_update_detection_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::ResumeAll() {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -624,4 +704,85 @@ Result IApplicationManagerInterface::Cmd4088(Out<u64> out_result) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::Cmd4042(Out<u64> out_result) {
|
||||
LOG_DEBUG(Service_NS, "(STUBBED) called [20.0.0+]");
|
||||
*out_result = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::CountApplicationContentMeta(Out<u32> out_count,
|
||||
u64 application_id) {
|
||||
LOG_DEBUG(Service_NS, "called, application_id={:016X}", application_id);
|
||||
|
||||
const auto& cache = system.GetContentProviderUnion();
|
||||
const auto installed_aocs = cache.ListEntriesFilterOrigin(std::nullopt, FileSys::TitleType::AOC,
|
||||
FileSys::ContentRecordType::Data);
|
||||
|
||||
u32 count = 0;
|
||||
for (const auto& [slot, aoc] : installed_aocs) {
|
||||
if (FileSys::GetBaseTitleID(aoc.title_id) == application_id) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NS, "CountApplicationContentMeta found {} AOCS for app {:016X}", count,
|
||||
application_id);
|
||||
|
||||
*out_count = count;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::ListAvailableAddOnContent(
|
||||
OutArray<u32, BufferAttr_HipcMapAlias> out_addons, Out<u32> out_count, u32 offset,
|
||||
u64 application_id) {
|
||||
const auto limit = out_addons.size();
|
||||
LOG_DEBUG(Service_NS, "called, offset={}, limit={}, application_id={:016X}", offset, limit,
|
||||
application_id);
|
||||
|
||||
const auto& cache = system.GetContentProviderUnion();
|
||||
const auto installed_aocs = cache.ListEntriesFilterOrigin(std::nullopt, FileSys::TitleType::AOC,
|
||||
FileSys::ContentRecordType::Data);
|
||||
|
||||
std::vector<u32> aocs;
|
||||
aocs.reserve(installed_aocs.size());
|
||||
|
||||
LOG_DEBUG(Service_NS, "Scanning {} installed AOCs for application {:016X}",
|
||||
installed_aocs.size(), application_id);
|
||||
|
||||
for (const auto& [slot, aoc] : installed_aocs) {
|
||||
const auto base_id = FileSys::GetBaseTitleID(aoc.title_id);
|
||||
if (base_id == application_id) {
|
||||
LOG_DEBUG(Service_NS, "Found match! AOC ID: {:016X}, Base ID: {:016X}", aoc.title_id,
|
||||
base_id);
|
||||
aocs.push_back(static_cast<u32>(FileSys::GetAOCID(aoc.title_id)));
|
||||
} else {
|
||||
// Uncomment for even more verbose logging if needed
|
||||
// LOG_DEBUG(Service_NS, "Skipping AOC ID: {:016X} (Base: {:016X})", aoc.title_id,
|
||||
// base_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(aocs.begin(), aocs.end());
|
||||
|
||||
size_t i = 0;
|
||||
const size_t start = static_cast<size_t>(std::max<u32>(0, offset));
|
||||
for (size_t j = start; j < aocs.size() && i < limit; ++j) {
|
||||
out_addons[i++] = aocs[j];
|
||||
}
|
||||
|
||||
*out_count = static_cast<u32>(i);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::Cmd4053() {
|
||||
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called.");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -24,17 +24,23 @@ public:
|
||||
u32 supported_languages);
|
||||
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
|
||||
ApplicationLanguage application_language);
|
||||
Result GenerateApplicationRecordCount(Out<s32> out_count);
|
||||
Result ListApplicationRecord(OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records,
|
||||
Out<s32> out_count, s32 offset);
|
||||
Result GetApplicationRecordUpdateSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
|
||||
Result GetApplicationView(
|
||||
OutArray<ApplicationViewV20, BufferAttr_HipcMapAlias> out_application_views,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||
Result GetApplicationViewDeprecated(
|
||||
OutArray<ApplicationView, BufferAttr_HipcMapAlias> out_application_views,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||
Result GetApplicationViewWithPromotionInfo(
|
||||
OutArray<ApplicationViewWithPromotionInfo, BufferAttr_HipcMapAlias> out_application_views,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_count,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||
Result RequestDownloadApplicationControlDataInBackground(u64 control_source,
|
||||
u64 application_id);
|
||||
Result GetApplicationRightsOnClient(
|
||||
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
|
||||
u32 flags, u64 application_id, Uid account_id);
|
||||
@@ -53,7 +59,15 @@ public:
|
||||
// [20.0.0+] Stub functions for QLaunch compatibility
|
||||
Result Cmd4022(Out<u64> out_result);
|
||||
Result Cmd4023(Out<u64> out_result);
|
||||
Result Cmd4042(Out<u64> out_result);
|
||||
Result Cmd4053();
|
||||
Result Cmd4088(Out<u64> out_result);
|
||||
// [1.0.0+]
|
||||
Result CountApplicationContentMeta(Out<u32> out_count, u64 application_id);
|
||||
Result ListAvailableAddOnContent(OutArray<u32, BufferAttr_HipcMapAlias> out_addons,
|
||||
Out<u32> out_count, u32 offset, u64 application_id);
|
||||
|
||||
void ListApplicationTitle(HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
@@ -29,31 +29,48 @@ enum class BackgroundNetworkUpdateState : u8 {
|
||||
Ready,
|
||||
};
|
||||
|
||||
/// ApplicationDownloadState
|
||||
struct ApplicationDownloadState {
|
||||
u64 downloaded_size;
|
||||
u64 total_size;
|
||||
u32 unk_x10;
|
||||
u8 state;
|
||||
u8 unk_x15;
|
||||
std::array<u8, 0x2> unk_x16;
|
||||
u64 unk_x18;
|
||||
};
|
||||
static_assert(sizeof(ApplicationDownloadState) == 0x20,
|
||||
"ApplicationDownloadState has incorrect size.");
|
||||
|
||||
struct ApplicationRecord {
|
||||
u64 application_id;
|
||||
ApplicationRecordType type;
|
||||
u8 unknown;
|
||||
u8 attributes;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x6);
|
||||
u8 unknown2;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x7);
|
||||
s64 last_updated;
|
||||
};
|
||||
static_assert(sizeof(ApplicationRecord) == 0x18, "ApplicationRecord has incorrect size.");
|
||||
|
||||
/// ApplicationView
|
||||
struct ApplicationView {
|
||||
u64 application_id; ///< ApplicationId.
|
||||
u32 unk; ///< Unknown.
|
||||
u32 flags; ///< Flags.
|
||||
std::array<u8, 0x10> unk_x10; ///< Unknown.
|
||||
u32 unk_x20; ///< Unknown.
|
||||
u16 unk_x24; ///< Unknown.
|
||||
std::array<u8, 0x2> unk_x26; ///< Unknown.
|
||||
std::array<u8, 0x8> unk_x28; ///< Unknown.
|
||||
std::array<u8, 0x10> unk_x30; ///< Unknown.
|
||||
u32 unk_x40; ///< Unknown.
|
||||
u8 unk_x44; ///< Unknown.
|
||||
std::array<u8, 0xb> unk_x45; ///< Unknown.
|
||||
struct ApplicationViewV19 {
|
||||
u64 application_id;
|
||||
u32 version;
|
||||
u32 flags;
|
||||
ApplicationDownloadState download_state;
|
||||
ApplicationDownloadState download_progress;
|
||||
};
|
||||
static_assert(sizeof(ApplicationViewV19) == 0x50, "ApplicationViewV19 has incorrect size.");
|
||||
|
||||
struct ApplicationViewV20 {
|
||||
u64 application_id;
|
||||
u32 version;
|
||||
u32 flags;
|
||||
u32 unk;
|
||||
ApplicationDownloadState download_state;
|
||||
ApplicationDownloadState download_progress;
|
||||
};
|
||||
static_assert(sizeof(ApplicationViewV20) == 0x58, "ApplicationViewV20 has incorrect size.");
|
||||
|
||||
using ApplicationView = ApplicationViewV19;
|
||||
static_assert(sizeof(ApplicationView) == 0x50, "ApplicationView has incorrect size.");
|
||||
|
||||
struct ApplicationRightsOnClient {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/query_service.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -26,12 +30,12 @@ IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_,
|
||||
{11, nullptr, "QueryAccountPlayEvent"},
|
||||
{12, nullptr, "GetAvailableAccountPlayEventRange"},
|
||||
{13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
|
||||
{14, nullptr, "QueryRecentlyPlayedApplication"},
|
||||
{14, D<&IQueryService::QueryRecentlyPlayedApplication>, "QueryRecentlyPlayedApplication"},
|
||||
{15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
|
||||
{16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
|
||||
{17, nullptr, "QueryLastPlayTime"},
|
||||
{18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
|
||||
{19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
|
||||
{18, D<&IQueryService::QueryApplicationPlayStatisticsForSystem>, "QueryApplicationPlayStatisticsForSystem"},
|
||||
{19, D<&IQueryService::QueryApplicationPlayStatisticsByUserAccountIdForSystem>, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -53,4 +57,66 @@ Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IQueryService::QueryRecentlyPlayedApplication(
|
||||
Out<s32> out_count, OutArray<u64, BufferAttr_HipcMapAlias> out_applications, Uid user_id) {
|
||||
const auto limit = out_applications.size();
|
||||
LOG_INFO(Service_NS, "called. user_id={}, limit={}", user_id.uuid.FormattedString(), limit);
|
||||
|
||||
const auto& cache = system.GetContentProviderUnion();
|
||||
auto installed_games =
|
||||
cache.ListEntriesFilter(std::nullopt, FileSys::ContentRecordType::Program, std::nullopt);
|
||||
|
||||
// Filter and sort
|
||||
std::vector<u64> applications;
|
||||
for (const auto& entry : installed_games) {
|
||||
if (entry.title_id == 0 || entry.title_id < 0x0100000000001FFFull) {
|
||||
continue;
|
||||
}
|
||||
applications.push_back(entry.title_id);
|
||||
}
|
||||
|
||||
// Sort by Title ID for now as a stable order
|
||||
std::sort(applications.begin(), applications.end());
|
||||
applications.erase(std::unique(applications.begin(), applications.end()), applications.end());
|
||||
|
||||
const auto count = std::min(limit, applications.size());
|
||||
LOG_INFO(Service_NS, "Returning {} applications out of {} found.", count, applications.size());
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
LOG_INFO(Service_NS, " [{}] TitleID: {:016X}", i, applications[i]);
|
||||
out_applications[i] = applications[i];
|
||||
}
|
||||
|
||||
*out_count = static_cast<s32>(count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IQueryService::QueryApplicationPlayStatisticsForSystem(
|
||||
Out<s32> out_entries, u8 flag,
|
||||
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||
const size_t count = std::min(out_stats.size(), application_ids.size());
|
||||
s32 written = 0;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const u64 app_id = application_ids[i];
|
||||
ApplicationPlayStatistics stats{};
|
||||
stats.application_id = app_id;
|
||||
stats.play_time_ns = 0; // TODO: Implement play time tracking
|
||||
stats.launch_count = 1; // Default to 1 for now
|
||||
out_stats[i] = stats;
|
||||
++written;
|
||||
}
|
||||
*out_entries = written;
|
||||
LOG_INFO(Service_NS, "called, entries={} flag={}", written, flag);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IQueryService::QueryApplicationPlayStatisticsByUserAccountIdForSystem(
|
||||
Out<s32> out_entries, u8 flag, Common::UUID user_id,
|
||||
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||
LOG_INFO(Service_NS, "called, user_id={}", user_id.FormattedString());
|
||||
return QueryApplicationPlayStatisticsForSystem(out_entries, flag, out_stats, application_ids);
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -23,6 +23,18 @@ struct PlayStatistics {
|
||||
};
|
||||
static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");
|
||||
|
||||
struct LastPlayTime {
|
||||
INSERT_PADDING_BYTES_NOINIT(0x8); // Likely needs padding for buffer alignment if used
|
||||
};
|
||||
|
||||
struct ApplicationPlayStatistics {
|
||||
u64 application_id{};
|
||||
u64 play_time_ns{};
|
||||
u64 launch_count{};
|
||||
};
|
||||
static_assert(sizeof(ApplicationPlayStatistics) == 0x18,
|
||||
"ApplicationPlayStatistics is an invalid size");
|
||||
|
||||
class IQueryService final : public ServiceFramework<IQueryService> {
|
||||
public:
|
||||
explicit IQueryService(Core::System& system_);
|
||||
@@ -31,6 +43,20 @@ public:
|
||||
private:
|
||||
Result QueryPlayStatisticsByApplicationIdAndUserAccountId(
|
||||
Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id);
|
||||
|
||||
Result QueryRecentlyPlayedApplication(Out<s32> out_count,
|
||||
OutArray<u64, BufferAttr_HipcMapAlias> out_applications,
|
||||
Uid user_id);
|
||||
|
||||
Result QueryApplicationPlayStatisticsForSystem(
|
||||
Out<s32> out_entries, u8 flag,
|
||||
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||
|
||||
Result QueryApplicationPlayStatisticsByUserAccountIdForSystem(
|
||||
Out<s32> out_entries, u8 flag, Common::UUID user_id,
|
||||
OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_stats,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
|
||||
};
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#define STB_IMAGE_RESIZE_STATIC
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/ns/ns_results.h"
|
||||
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
||||
@@ -13,6 +32,113 @@
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
namespace {
|
||||
|
||||
void JPGToMemory(void* context, void* data, int size) {
|
||||
auto* buffer = static_cast<std::vector<u8>*>(context);
|
||||
const auto* char_data = static_cast<const u8*>(data);
|
||||
buffer->insert(buffer->end(), char_data, char_data + size);
|
||||
}
|
||||
|
||||
void SanitizeJPEGImageSize(std::vector<u8>& image) {
|
||||
constexpr std::size_t max_jpeg_image_size = 0x20000;
|
||||
constexpr int profile_dimensions = 174; // for grid view thingy
|
||||
int original_width, original_height, color_channels;
|
||||
|
||||
auto* plain_image =
|
||||
stbi_load_from_memory(image.data(), static_cast<int>(image.size()), &original_width,
|
||||
&original_height, &color_channels, STBI_rgb);
|
||||
|
||||
if (plain_image == nullptr) {
|
||||
LOG_ERROR(Service_NS, "Failed to load JPEG for sanitization.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (original_width != profile_dimensions || original_height != profile_dimensions) {
|
||||
std::vector<u8> out_image(profile_dimensions * profile_dimensions * STBI_rgb);
|
||||
stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(),
|
||||
profile_dimensions, profile_dimensions, 0, STBI_rgb, 0,
|
||||
STBIR_FILTER_BOX);
|
||||
image.clear();
|
||||
if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions,
|
||||
STBI_rgb, out_image.data(), 90)) {
|
||||
LOG_ERROR(Service_NS, "Failed to resize the user provided image.");
|
||||
}
|
||||
}
|
||||
|
||||
stbi_image_free(plain_image);
|
||||
|
||||
if (image.size() > max_jpeg_image_size) {
|
||||
image.resize(max_jpeg_image_size);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// IAsyncValue implementation for ListApplicationTitle
|
||||
// https://switchbrew.org/wiki/NS_services#ListApplicationTitle
|
||||
class IAsyncValueForListApplicationTitle final
|
||||
: public ServiceFramework<IAsyncValueForListApplicationTitle> {
|
||||
public:
|
||||
explicit IAsyncValueForListApplicationTitle(Core::System& system_, s32 offset, s32 size)
|
||||
: ServiceFramework{system_, "IAsyncValue"}, service_context{system_, "IAsyncValue"},
|
||||
data_offset{offset}, data_size{size} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAsyncValueForListApplicationTitle::GetSize, "GetSize"},
|
||||
{1, &IAsyncValueForListApplicationTitle::Get, "Get"},
|
||||
{2, &IAsyncValueForListApplicationTitle::Cancel, "Cancel"},
|
||||
{3, &IAsyncValueForListApplicationTitle::GetErrorContext, "GetErrorContext"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
completion_event = service_context.CreateEvent("IAsyncValue:Completion");
|
||||
completion_event->GetReadableEvent().Signal();
|
||||
}
|
||||
|
||||
~IAsyncValueForListApplicationTitle() override {
|
||||
service_context.CloseEvent(completion_event);
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& ReadableEvent() const {
|
||||
return completion_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
void GetSize(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s64>(data_size);
|
||||
}
|
||||
|
||||
void Get(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
std::vector<u8> buffer(sizeof(s32));
|
||||
std::memcpy(buffer.data(), &data_offset, sizeof(s32));
|
||||
ctx.WriteBuffer(buffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetErrorContext(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* completion_event{};
|
||||
s32 data_offset;
|
||||
s32 data_size;
|
||||
};
|
||||
|
||||
IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
|
||||
Core::System& system_)
|
||||
: ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
|
||||
@@ -23,6 +149,9 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
|
||||
{2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
|
||||
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
|
||||
{4, nullptr, "SelectApplicationDesiredLanguage"},
|
||||
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
|
||||
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
|
||||
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -119,4 +248,191 @@ Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLan
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u64> out_total_size,
|
||||
ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
|
||||
LOG_INFO(Service_NS,
|
||||
"called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
|
||||
application_control_source, flag1, flag2, application_id);
|
||||
|
||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto size = out_buffer.size();
|
||||
|
||||
const auto nacp_size = sizeof(FileSys::RawNACP);
|
||||
|
||||
if (size < nacp_size) {
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})",
|
||||
size, nacp_size);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
if (control.first != nullptr) {
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
const auto copy_len =
|
||||
(std::min)(static_cast<size_t>(bytes.size()), static_cast<size_t>(nacp_size));
|
||||
std::memcpy(out_buffer.data(), bytes.data(), copy_len);
|
||||
if (copy_len < nacp_size) {
|
||||
std::memset(out_buffer.data() + copy_len, 0, nacp_size - copy_len);
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}", application_id);
|
||||
std::memset(out_buffer.data(), 0, nacp_size);
|
||||
}
|
||||
|
||||
const auto icon_area_size = size - nacp_size;
|
||||
std::vector<u8> final_icon_data;
|
||||
|
||||
if (control.second != nullptr) {
|
||||
size_t full_size = control.second->GetSize();
|
||||
if (full_size > 0) {
|
||||
final_icon_data.resize(full_size);
|
||||
control.second->Read(final_icon_data.data(), full_size);
|
||||
|
||||
if (flag1 == 1) {
|
||||
SanitizeJPEGImageSize(final_icon_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t available_icon_bytes = final_icon_data.size();
|
||||
|
||||
if (icon_area_size > 0) {
|
||||
const size_t to_copy = (std::min)(available_icon_bytes, icon_area_size);
|
||||
|
||||
if (to_copy > 0) {
|
||||
std::memcpy(out_buffer.data() + nacp_size, final_icon_data.data(), to_copy);
|
||||
}
|
||||
|
||||
if (to_copy < icon_area_size) {
|
||||
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 total_available = static_cast<u32>(nacp_size + available_icon_bytes);
|
||||
|
||||
*out_total_size = (static_cast<u64>(total_available) << 32) | static_cast<u64>(flag1);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
||||
|
||||
std::vector<u64> application_ids(app_count);
|
||||
if (app_count > 0) {
|
||||
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
|
||||
}
|
||||
|
||||
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||
|
||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||
const size_t total_data_size = app_count * title_entry_size;
|
||||
|
||||
constexpr s32 data_offset = 0;
|
||||
|
||||
if (t_mem != nullptr && app_count > 0) {
|
||||
auto& memory = system.ApplicationMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = application_ids[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
FileSys::LanguageEntry entry{};
|
||||
if (control.first != nullptr) {
|
||||
entry = control.first->GetLanguageEntry();
|
||||
}
|
||||
|
||||
const size_t offset = i * title_entry_size;
|
||||
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size);
|
||||
}
|
||||
}
|
||||
|
||||
auto async_value = std::make_shared<IAsyncValueForListApplicationTitle>(
|
||||
system, data_offset, static_cast<s32>(total_data_size));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(std::move(async_value));
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b,
|
||||
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1,
|
||||
u8 flag2, u64 application_id) {
|
||||
LOG_INFO(Service_NS,
|
||||
"called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
|
||||
application_control_source, flag1, flag2, application_id);
|
||||
|
||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
const auto size = out_buffer.size();
|
||||
|
||||
const auto nacp_size = sizeof(FileSys::RawNACP);
|
||||
|
||||
if (size < nacp_size) {
|
||||
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})",
|
||||
size, nacp_size);
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
if (control.first != nullptr) {
|
||||
const auto bytes = control.first->GetRawBytes();
|
||||
const auto copy_len =
|
||||
(std::min)(static_cast<size_t>(bytes.size()), static_cast<size_t>(nacp_size));
|
||||
std::memcpy(out_buffer.data(), bytes.data(), copy_len);
|
||||
if (copy_len < nacp_size) {
|
||||
std::memset(out_buffer.data() + copy_len, 0, nacp_size - copy_len);
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
|
||||
application_id);
|
||||
std::memset(out_buffer.data(), 0, nacp_size);
|
||||
}
|
||||
|
||||
const auto icon_area_size = size - nacp_size;
|
||||
std::vector<u8> final_icon_data;
|
||||
|
||||
if (control.second != nullptr) {
|
||||
size_t full_size = control.second->GetSize();
|
||||
if (full_size > 0) {
|
||||
final_icon_data.resize(full_size);
|
||||
control.second->Read(final_icon_data.data(), full_size);
|
||||
// TODO: Implement SanitizeJPEGImageSize(final_icon_data) if flag1 == 1
|
||||
}
|
||||
}
|
||||
|
||||
size_t available_icon_bytes = final_icon_data.size();
|
||||
|
||||
if (icon_area_size > 0) {
|
||||
const size_t to_copy = (std::min)(available_icon_bytes, icon_area_size);
|
||||
if (to_copy > 0) {
|
||||
std::memcpy(out_buffer.data() + nacp_size, final_icon_data.data(), to_copy);
|
||||
}
|
||||
if (to_copy < icon_area_size) {
|
||||
std::memset(out_buffer.data() + nacp_size + to_copy, 0, icon_area_size - to_copy);
|
||||
}
|
||||
} else {
|
||||
std::memset(out_buffer.data() + nacp_size, 0, icon_area_size);
|
||||
}
|
||||
|
||||
const u32 actual_total_size = static_cast<u32>(nacp_size + available_icon_bytes);
|
||||
|
||||
// Out 1: always 0x10001 (likely presents flags: Bit0=Icon, Bit16=NACP)
|
||||
// Out 2: reflects flag1 application (0 if flag1=0, 0x10001 if flag1=1)
|
||||
// Out 3: The actual size of data
|
||||
*out_flags_a = 0x10001;
|
||||
*out_flags_b = (flag1 == 1) ? 0x10001 : 0;
|
||||
*out_actual_size = actual_total_size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -16,7 +16,6 @@ public:
|
||||
explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
|
||||
~IReadOnlyApplicationControlDataInterface() override;
|
||||
|
||||
public:
|
||||
Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source,
|
||||
@@ -25,6 +24,16 @@ public:
|
||||
u32 supported_languages);
|
||||
Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
|
||||
ApplicationLanguage application_language);
|
||||
Result GetApplicationControlData2(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u64> out_total_size,
|
||||
ApplicationControlSource application_control_source, u8 flag1,
|
||||
u8 flag2, u64 application_id);
|
||||
void ListApplicationTitle(HLERequestContext& ctx);
|
||||
Result GetApplicationControlData3(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
|
||||
Out<u32> out_flags_a, Out<u32> out_flags_b,
|
||||
Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source, u8 flag1,
|
||||
u8 flag2, u64 application_id);
|
||||
};
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/application_manager_interface.h"
|
||||
#include "core/hle/service/ns/read_only_application_record_interface.h"
|
||||
|
||||
namespace Service::NS {
|
||||
@@ -13,6 +14,8 @@ IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::S
|
||||
{1, nullptr, "NotifyApplicationFailure"},
|
||||
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
|
||||
"IsDataCorruptedResult"},
|
||||
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>,
|
||||
"ListApplicationRecord"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -35,4 +38,16 @@ Result IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationRecordInterface::ListApplicationRecord(
|
||||
OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records, Out<s32> out_count,
|
||||
s32 entry_offset) {
|
||||
LOG_INFO(Service_NS,
|
||||
"delegating to IApplicationManagerInterface::ListApplicationRecord, offset={} "
|
||||
"limit={}",
|
||||
entry_offset, out_records.size());
|
||||
|
||||
R_RETURN(IApplicationManagerInterface(system).ListApplicationRecord(out_records, out_count,
|
||||
entry_offset));
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/ns/ns_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NS {
|
||||
@@ -17,6 +18,8 @@ public:
|
||||
private:
|
||||
Result HasApplicationRecord(Out<bool> out_has_application_record, u64 program_id);
|
||||
Result IsDataCorruptedResult(Out<bool> out_is_data_corrupted_result, Result result);
|
||||
Result ListApplicationRecord(OutArray<ApplicationRecord, BufferAttr_HipcMapAlias> out_records,
|
||||
Out<s32> out_count, s32 entry_offset);
|
||||
};
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -42,77 +42,77 @@ IServiceGetterInterface::~IServiceGetterInterface() = default;
|
||||
|
||||
Result IServiceGetterInterface::GetDynamicRightsInterface(
|
||||
Out<SharedPointer<IDynamicRightsInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IDynamicRightsInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface(
|
||||
Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IReadOnlyApplicationControlDataInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetReadOnlyApplicationRecordInterface(
|
||||
Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IReadOnlyApplicationRecordInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetECommerceInterface(
|
||||
Out<SharedPointer<IECommerceInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IECommerceInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetApplicationVersionInterface(
|
||||
Out<SharedPointer<IApplicationVersionInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IApplicationVersionInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetFactoryResetInterface(
|
||||
Out<SharedPointer<IFactoryResetInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IFactoryResetInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetAccountProxyInterface(
|
||||
Out<SharedPointer<IAccountProxyInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IAccountProxyInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetApplicationManagerInterface(
|
||||
Out<SharedPointer<IApplicationManagerInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IApplicationManagerInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetDownloadTaskInterface(
|
||||
Out<SharedPointer<IDownloadTaskInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IDownloadTaskInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetContentManagementInterface(
|
||||
Out<SharedPointer<IContentManagementInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IContentManagementInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IServiceGetterInterface::GetDocumentInterface(
|
||||
Out<SharedPointer<IDocumentInterface>> out_interface) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
LOG_INFO(Service_NS, "called");
|
||||
*out_interface = std::make_shared<IDocumentInterface>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
|
||||
Session::Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_)
|
||||
: id{id_}, process{process_}, asid{asid_}, has_preallocated_area{}, mapper{}, is_active{} {}
|
||||
Session::Session(SessionId id_, Kernel::KProcess* process_, u64 pid_, Core::Asid asid_)
|
||||
: id{id_}, process{process_}, pid{pid_}, asid{asid_}, has_preallocated_area{}, mapper{},
|
||||
is_active{} {}
|
||||
|
||||
Session::~Session() = default;
|
||||
|
||||
@@ -49,8 +50,39 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
continue;
|
||||
}
|
||||
if (session.process == process) {
|
||||
session.ref_count++;
|
||||
return session.id;
|
||||
if (session.pid != process->GetProcessId()) {
|
||||
LOG_WARNING(Service_NVDRV,
|
||||
"Container::OpenSession: Stale session detected! PID mismatch (old={}, "
|
||||
"new={}). Marking as inactive.",
|
||||
session.pid, process->GetProcessId());
|
||||
// Force close stale session logic
|
||||
// NOTE: We do NOT unmap handles or free memory here because it causes
|
||||
// KSynchronizationObject::UnlinkNode segfaults if threads are still waiting on
|
||||
// events associated with this session. We effectively leak the old session's
|
||||
// resources to ensure stability.
|
||||
// impl->file.UnmapAllHandles(session.id);
|
||||
|
||||
// auto& smmu_mgr = impl->host1x.MemoryManager();
|
||||
/*
|
||||
if (session.has_preallocated_area) {
|
||||
const DAddr region_start = session.mapper->GetRegionStart();
|
||||
const size_t region_size = session.mapper->GetRegionSize();
|
||||
session.mapper.reset();
|
||||
smmu_mgr.Free(region_start, region_size);
|
||||
session.has_preallocated_area = false;
|
||||
}
|
||||
*/
|
||||
session.is_active = false;
|
||||
session.ref_count = 0;
|
||||
// smmu_mgr.UnregisterProcess(session.asid);
|
||||
// impl->id_pool.emplace_front(session.id.id);
|
||||
// Continue searching to clean up other stale sessions if any?
|
||||
// Or proceed to create new one. Stale one is now inactive.
|
||||
continue;
|
||||
} else {
|
||||
session.ref_count++;
|
||||
return session.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t new_id{};
|
||||
@@ -60,10 +92,10 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
if (!impl->id_pool.empty()) {
|
||||
new_id = impl->id_pool.front();
|
||||
impl->id_pool.pop_front();
|
||||
impl->sessions[new_id] = Session{SessionId{new_id}, process, asid};
|
||||
impl->sessions[new_id] = Session{SessionId{new_id}, process, process->GetProcessId(), asid};
|
||||
} else {
|
||||
new_id = impl->new_ids++;
|
||||
impl->sessions.emplace_back(SessionId{new_id}, process, asid);
|
||||
impl->sessions.emplace_back(SessionId{new_id}, process, process->GetProcessId(), asid);
|
||||
}
|
||||
auto& session = impl->sessions[new_id];
|
||||
session.is_active = true;
|
||||
|
||||
@@ -32,7 +32,7 @@ struct SessionId {
|
||||
};
|
||||
|
||||
struct Session {
|
||||
Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_);
|
||||
Session(SessionId id_, Kernel::KProcess* process_, u64 pid_, Core::Asid asid_);
|
||||
~Session();
|
||||
|
||||
Session(const Session&) = delete;
|
||||
@@ -42,6 +42,7 @@ struct Session {
|
||||
|
||||
SessionId id;
|
||||
Kernel::KProcess* process;
|
||||
u64 pid;
|
||||
Core::Asid asid;
|
||||
bool has_preallocated_area{};
|
||||
std::unique_ptr<HeapMapper> mapper{};
|
||||
|
||||
@@ -160,7 +160,6 @@ void NVDRV::Initialize(HLERequestContext& ctx) {
|
||||
};
|
||||
|
||||
if (is_initialized) {
|
||||
// No need to initialize again
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ struct Layer {
|
||||
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
|
||||
s32 consumer_id_)
|
||||
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
|
||||
blending(LayerBlending::None), visible(true) {}
|
||||
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {}
|
||||
~Layer() {
|
||||
buffer_item_consumer->Abandon();
|
||||
}
|
||||
@@ -21,6 +21,8 @@ struct Layer {
|
||||
s32 consumer_id;
|
||||
LayerBlending blending;
|
||||
bool visible;
|
||||
s32 z_index;
|
||||
bool is_overlay;
|
||||
};
|
||||
|
||||
struct LayerStack {
|
||||
|
||||
@@ -83,7 +83,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
|
||||
.width = igbp_buffer.Width(),
|
||||
.height = igbp_buffer.Height(),
|
||||
.stride = igbp_buffer.Stride(),
|
||||
.z_index = 0,
|
||||
.z_index = layer->z_index,
|
||||
.blending = layer->blending,
|
||||
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
||||
.crop_rect = item.crop,
|
||||
|
||||
@@ -121,6 +121,13 @@ std::shared_ptr<Layer> SurfaceFlinger::FindLayer(s32 consumer_binder_id) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SurfaceFlinger::SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay) {
|
||||
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
||||
layer->is_overlay = is_overlay;
|
||||
LOG_DEBUG(Service_VI, "Layer {} marked as overlay: {}", consumer_binder_id, is_overlay);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceFlinger::CreateBufferQueue(s32* out_consumer_binder_id, s32* out_producer_binder_id) {
|
||||
auto& nvmap = nvdrv->GetContainer().GetNvMapFile();
|
||||
auto core = std::make_shared<android::BufferQueueCore>();
|
||||
|
||||
@@ -44,10 +44,12 @@ public:
|
||||
|
||||
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
|
||||
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
|
||||
void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay);
|
||||
|
||||
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
||||
|
||||
private:
|
||||
Display* FindDisplay(u64 display_id);
|
||||
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
||||
|
||||
public:
|
||||
// TODO: these don't belong here
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -12,14 +13,14 @@ IDaemonController::IDaemonController(Core::System& system_)
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IDaemonController::GetAutoTransferEnabledForAccountAndApplication>, "GetAutoTransferEnabledForAccountAndApplication"},
|
||||
{1, nullptr, "SetAutoTransferEnabledForAccountAndApplication"},
|
||||
{2, nullptr, "GetGlobalUploadEnabledForAccount"},
|
||||
{3, nullptr, "SetGlobalUploadEnabledForAccount"},
|
||||
{2, D<&IDaemonController::GetGlobalUploadEnabledForAccount>, "GetGlobalUploadEnabledForAccount"},
|
||||
{3, D<&IDaemonController::SetGlobalUploadEnabledForAccount>, "SetGlobalUploadEnabledForAccount"},
|
||||
{4, nullptr, "TouchAccount"},
|
||||
{5, nullptr, "GetGlobalDownloadEnabledForAccount"},
|
||||
{6, nullptr, "SetGlobalDownloadEnabledForAccount"},
|
||||
{5, D<&IDaemonController::GetGlobalDownloadEnabledForAccount>, "GetGlobalDownloadEnabledForAccount"},
|
||||
{6, D<&IDaemonController::SetGlobalDownloadEnabledForAccount>, "SetGlobalDownloadEnabledForAccount"},
|
||||
{10, nullptr, "GetForbiddenSaveDataIndication"},
|
||||
{11, nullptr, "GetStopperObject"},
|
||||
{12, nullptr, "GetState"},
|
||||
{12, D<&IDaemonController::GetAutonomyTaskStatus>, "GetAutonomyTaskStatus"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -37,4 +38,37 @@ Result IDaemonController::GetAutoTransferEnabledForAccountAndApplication(Out<boo
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::GetGlobalUploadEnabledForAccount(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={}", user_id.FormattedString());
|
||||
*out_is_enabled = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::SetGlobalUploadEnabledForAccount(bool is_enabled, Common::UUID user_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, is_enabled={} user_id={}", is_enabled,
|
||||
user_id.FormattedString());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::GetGlobalDownloadEnabledForAccount(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={}", user_id.FormattedString());
|
||||
*out_is_enabled = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::SetGlobalDownloadEnabledForAccount(bool is_enabled,
|
||||
Common::UUID user_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, is_enabled={} user_id={}", is_enabled,
|
||||
user_id.FormattedString());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IDaemonController::GetAutonomyTaskStatus(Out<u8> out_status, Common::UUID user_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={}", user_id.FormattedString());
|
||||
*out_status = 0; // Status: Idle
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::OLSC
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/uuid.h"
|
||||
@@ -15,6 +16,11 @@ public:
|
||||
private:
|
||||
Result GetAutoTransferEnabledForAccountAndApplication(Out<bool> out_is_enabled,
|
||||
Common::UUID user_id, u64 application_id);
|
||||
Result GetGlobalUploadEnabledForAccount(Out<bool> out_is_enabled, Common::UUID user_id);
|
||||
Result SetGlobalUploadEnabledForAccount(bool is_enabled, Common::UUID user_id);
|
||||
Result GetGlobalDownloadEnabledForAccount(Out<bool> out_is_enabled, Common::UUID user_id);
|
||||
Result SetGlobalDownloadEnabledForAccount(bool is_enabled, Common::UUID user_id);
|
||||
Result GetAutonomyTaskStatus(Out<u8> out_status, Common::UUID user_id);
|
||||
};
|
||||
|
||||
} // namespace Service::OLSC
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -21,11 +22,11 @@ IRemoteStorageController::IRemoteStorageController(Core::System& system_)
|
||||
{11, nullptr, "CreateDeleteDataTask"},
|
||||
{12, nullptr, "DeleteSeriesInfo"},
|
||||
{13, nullptr, "CreateRegisterNotificationTokenTask"},
|
||||
{14, nullptr, "UpdateSeriesInfo"},
|
||||
{14, D<&IRemoteStorageController::GetDataNewnessByApplicationId>, "GetDataNewnessByApplicationId"},
|
||||
{15, nullptr, "RegisterUploadSaveDataTransferTaskForAutonomyRegistration"},
|
||||
{16, nullptr, "CreateCleanupToDeleteSaveDataArchiveInfoTask"},
|
||||
{17, nullptr, "ListDataInfo"},
|
||||
{18, nullptr, "GetDataInfo"},
|
||||
{18, D<&IRemoteStorageController::GetDataInfo>, "GetDataInfo"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "CreateSaveDataArchiveInfoCacheForSaveDataBackupUpdationTask"},
|
||||
{21, nullptr, "ListSecondarySaves"},
|
||||
@@ -33,6 +34,7 @@ IRemoteStorageController::IRemoteStorageController(Core::System& system_)
|
||||
{23, nullptr, "TouchSecondarySave"},
|
||||
{24, nullptr, "GetSecondarySaveDataInfo"},
|
||||
{25, nullptr, "RegisterDownloadSaveDataTransferTaskForAutonomyRegistration"},
|
||||
{27, D<&IRemoteStorageController::GetDataInfo>, "GetDataInfoV2"}, // [20.0.0+]
|
||||
{28, D<&IRemoteStorageController::Unknown28>, "Unknown28"}, // [20.2.0+]
|
||||
{900, nullptr, "Unknown900"},
|
||||
{901, D<&IRemoteStorageController::Unknown901>, "Unknown901"}, // [20.2.0+]
|
||||
@@ -44,6 +46,20 @@ IRemoteStorageController::IRemoteStorageController(Core::System& system_)
|
||||
|
||||
IRemoteStorageController::~IRemoteStorageController() = default;
|
||||
|
||||
Result IRemoteStorageController::GetDataNewnessByApplicationId(Out<u8> out_newness,
|
||||
u64 application_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, application_id={:016X}", application_id);
|
||||
*out_newness = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IRemoteStorageController::GetDataInfo(Out<std::array<u8, 0x38>> out_data,
|
||||
u64 application_id) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, application_id={:016X}", application_id);
|
||||
out_data->fill(0);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IRemoteStorageController::GetSecondarySave(Out<bool> out_has_secondary_save,
|
||||
Out<std::array<u64, 3>> out_unknown,
|
||||
u64 application_id) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
@@ -12,9 +13,11 @@ public:
|
||||
~IRemoteStorageController() override;
|
||||
|
||||
private:
|
||||
Result GetDataNewnessByApplicationId(Out<u8> out_newness, u64 application_id);
|
||||
Result GetDataInfo(Out<std::array<u8, 0x38>> out_data, u64 application_id);
|
||||
Result GetSecondarySave(Out<bool> out_has_secondary_save, Out<std::array<u64, 3>> out_unknown,
|
||||
u64 application_id);
|
||||
Result Unknown28(); // [20.2.0+]
|
||||
Result Unknown28(); // [20.2.0+]
|
||||
Result Unknown901(); // [20.2.0+]
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/olsc/native_handle_holder.h"
|
||||
#include "core/hle/service/olsc/remote_storage_controller.h"
|
||||
#include "core/hle/service/olsc/transfer_task_list_controller.h"
|
||||
|
||||
namespace Service::OLSC {
|
||||
@@ -19,7 +21,7 @@ ITransferTaskListController::ITransferTaskListController(Core::System& system_)
|
||||
{5, D<&ITransferTaskListController::GetNativeHandleHolder>, "GetNativeHandleHolder"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "GetRemoteStorageController"},
|
||||
{8, D<&ITransferTaskListController::GetRemoteStorageController>, "GetRemoteStorageController"},
|
||||
{9, D<&ITransferTaskListController::GetNativeHandleHolder>, "GetNativeHandleHolder2"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
@@ -31,12 +33,12 @@ ITransferTaskListController::ITransferTaskListController(Core::System& system_)
|
||||
{17, nullptr, "Unknown17"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{20, D<&ITransferTaskListController::Unknown20>, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{24, D<&ITransferTaskListController::GetCurrentTransferTaskInfo>, "GetCurrentTransferTaskInfo"},
|
||||
{25, D<&ITransferTaskListController::FindTransferTaskInfo>, "FindTransferTaskInfo"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -52,4 +54,30 @@ Result ITransferTaskListController::GetNativeHandleHolder(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ITransferTaskListController::GetRemoteStorageController(
|
||||
Out<SharedPointer<IRemoteStorageController>> out_controller) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called");
|
||||
*out_controller = std::make_shared<IRemoteStorageController>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ITransferTaskListController::Unknown20() {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ITransferTaskListController::GetCurrentTransferTaskInfo(Out<std::array<u8, 0x30>> out_info,
|
||||
u8 unknown) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, unknown={:#x}", unknown);
|
||||
out_info->fill(0);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ITransferTaskListController::FindTransferTaskInfo(Out<std::array<u8, 0x30>> out_info,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> in) {
|
||||
LOG_WARNING(Service_OLSC, "(STUBBED) called, in_size={}", in.size());
|
||||
out_info->fill(0);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::OLSC
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
@@ -7,6 +8,7 @@
|
||||
namespace Service::OLSC {
|
||||
|
||||
class INativeHandleHolder;
|
||||
class IRemoteStorageController;
|
||||
|
||||
class ITransferTaskListController final : public ServiceFramework<ITransferTaskListController> {
|
||||
public:
|
||||
@@ -15,6 +17,11 @@ public:
|
||||
|
||||
private:
|
||||
Result GetNativeHandleHolder(Out<SharedPointer<INativeHandleHolder>> out_holder);
|
||||
Result GetRemoteStorageController(Out<SharedPointer<IRemoteStorageController>> out_controller);
|
||||
Result Unknown20();
|
||||
Result GetCurrentTransferTaskInfo(Out<std::array<u8, 0x30>> out_info, u8 unknown);
|
||||
Result FindTransferTaskInfo(Out<std::array<u8, 0x30>> out_info,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> in);
|
||||
};
|
||||
|
||||
} // namespace Service::OLSC
|
||||
|
||||
@@ -36,6 +36,7 @@ IParentalControlService::IParentalControlService(Core::System& system_, Capabili
|
||||
{1016, nullptr, "ConfirmShowNewsPermission"},
|
||||
{1017, D<&IParentalControlService::EndFreeCommunication>, "EndFreeCommunication"},
|
||||
{1018, D<&IParentalControlService::IsFreeCommunicationAvailable>, "IsFreeCommunicationAvailable"},
|
||||
{1019, D<&IParentalControlService::ConfirmLaunchApplicationPermission>, "ConfirmLaunchApplicationPermission"},
|
||||
{1031, D<&IParentalControlService::IsRestrictionEnabled>, "IsRestrictionEnabled"},
|
||||
{1032, D<&IParentalControlService::GetSafetyLevel>, "GetSafetyLevel"},
|
||||
{1033, nullptr, "SetSafetyLevel"},
|
||||
|
||||
@@ -18,4 +18,12 @@ union MessageFlags {
|
||||
};
|
||||
static_assert(sizeof(MessageFlags) == 0x8, "MessageFlags has incorrect size");
|
||||
|
||||
struct SourceName {
|
||||
char name[0x16];
|
||||
|
||||
const char* GetString() const {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Service::PSC
|
||||
|
||||
@@ -5,32 +5,113 @@
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/psc/ovln/receiver.h"
|
||||
|
||||
|
||||
namespace Service::PSC {
|
||||
|
||||
IReceiver::IReceiver(Core::System& system_)
|
||||
: ServiceFramework{system_, "IReceiver"}, service_context{system_, "IReceiver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "AddSource"},
|
||||
{1, nullptr, "RemoveSource"},
|
||||
{0, D<&IReceiver::AddSource>, "AddSource"},
|
||||
{1, D<&IReceiver::RemoveSource>, "RemoveSource"},
|
||||
{2, D<&IReceiver::GetReceiveEventHandle>, "GetReceiveEventHandle"},
|
||||
{3, nullptr, "Receive"},
|
||||
{4, nullptr, "ReceiveWithTick"},
|
||||
{3, D<&IReceiver::Receive>, "Receive"},
|
||||
{4, D<&IReceiver::ReceiveWithTick>, "ReceiveWithTick"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
event = service_context.CreateEvent("IReceiver:Event");
|
||||
|
||||
receive_event = service_context.CreateEvent("IReceiver::ReceiveEvent");
|
||||
}
|
||||
|
||||
IReceiver::~IReceiver() {
|
||||
service_context.CloseEvent(event);
|
||||
service_context.CloseEvent(receive_event);
|
||||
}
|
||||
|
||||
Result IReceiver::GetReceiveEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_PSC, "called");
|
||||
*out_event = &event->GetReadableEvent();
|
||||
Result IReceiver::AddSource(SourceName source_name) {
|
||||
const std::string name = source_name.GetString();
|
||||
LOG_INFO(Service_PSC, "called: source_name={}", name);
|
||||
|
||||
// Add source if it doesn't already exist
|
||||
if (message_sources.find(name) == message_sources.end()) {
|
||||
message_sources[name] = {};
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReceiver::RemoveSource(SourceName source_name) {
|
||||
const std::string name = source_name.GetString();
|
||||
LOG_INFO(Service_PSC, "called: source_name={}", name);
|
||||
|
||||
// Remove source if it exists
|
||||
message_sources.erase(name);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReceiver::GetReceiveEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_INFO(Service_PSC, "called");
|
||||
*out_event = &receive_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IReceiver::Receive(Out<OverlayNotification> out_notification, Out<MessageFlags> out_flags) {
|
||||
u64 tick;
|
||||
return ReceiveWithTick(out_notification, out_flags, Out<u64>(&tick));
|
||||
}
|
||||
|
||||
Result IReceiver::ReceiveWithTick(Out<OverlayNotification> out_notification,
|
||||
Out<MessageFlags> out_flags, Out<u64> out_tick) {
|
||||
LOG_DEBUG(Service_PSC, "called");
|
||||
|
||||
// Find the message with the lowest ID across all sources
|
||||
const std::string* target_source = nullptr;
|
||||
size_t target_index = 0;
|
||||
|
||||
for (const auto& [source_name, messages] : message_sources) {
|
||||
if (!messages.empty()) {
|
||||
if (target_source == nullptr) {
|
||||
target_source = &source_name;
|
||||
target_index = 0;
|
||||
}
|
||||
// Note: In the real implementation, we would track message IDs
|
||||
// For now, just use FIFO order
|
||||
}
|
||||
}
|
||||
|
||||
if (target_source != nullptr) {
|
||||
auto& messages = message_sources[*target_source];
|
||||
*out_notification = messages[target_index].first;
|
||||
*out_flags = messages[target_index].second;
|
||||
*out_tick = 0; // TODO: Implement tick tracking
|
||||
|
||||
// Remove the message
|
||||
messages.erase(messages.begin() + target_index);
|
||||
|
||||
// Clear event if no more messages
|
||||
bool has_messages = false;
|
||||
for (const auto& [_, msgs] : message_sources) {
|
||||
if (!msgs.empty()) {
|
||||
has_messages = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_messages) {
|
||||
receive_event->Clear();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// No messages available
|
||||
*out_notification = {};
|
||||
*out_flags = {};
|
||||
*out_tick = 0;
|
||||
|
||||
LOG_WARNING(Service_PSC, "No messages available");
|
||||
R_THROW(ResultUnknown); // TODO: Use proper OvlnResult::NoMessages when available
|
||||
}
|
||||
|
||||
} // namespace Service::PSC
|
||||
|
||||
@@ -3,12 +3,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/psc/ovln/ovln_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -20,10 +25,18 @@ public:
|
||||
~IReceiver() override;
|
||||
|
||||
private:
|
||||
Result AddSource(SourceName source_name);
|
||||
Result RemoveSource(SourceName source_name);
|
||||
Result GetReceiveEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result Receive(Out<OverlayNotification> out_notification, Out<MessageFlags> out_flags);
|
||||
Result ReceiveWithTick(Out<OverlayNotification> out_notification, Out<MessageFlags> out_flags,
|
||||
Out<u64> out_tick);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* event;
|
||||
Kernel::KEvent* receive_event;
|
||||
|
||||
std::map<std::string, std::vector<std::pair<OverlayNotification, MessageFlags>>>
|
||||
message_sources;
|
||||
};
|
||||
|
||||
} // namespace Service::PSC
|
||||
|
||||
@@ -431,6 +431,15 @@ static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is
|
||||
static_assert(std::is_trivial_v<FirmwareVersionFormat>,
|
||||
"FirmwareVersionFormat type must be trivially copyable.");
|
||||
|
||||
/// This is nn::settings::system::RebootlessSystemUpdateVersion
|
||||
struct RebootlessSystemUpdateVersion {
|
||||
u32 version;
|
||||
std::array<char, 0x18> display_version;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
};
|
||||
static_assert(sizeof(RebootlessSystemUpdateVersion) == 0x20,
|
||||
"RebootlessSystemUpdateVersion is an invalid size");
|
||||
|
||||
/// This is nn::settings::system::HomeMenuScheme
|
||||
struct HomeMenuScheme {
|
||||
u32 main;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -239,7 +240,7 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
|
||||
{146, nullptr, "SetConsoleSixAxisSensorAngularVelocityTimeBias"},
|
||||
{147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"},
|
||||
{148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"},
|
||||
{149, nullptr, "GetRebootlessSystemUpdateVersion"},
|
||||
{149, D<&ISystemSettingsServer::GetRebootlessSystemUpdateVersion>, "GetRebootlessSystemUpdateVersion"},
|
||||
{150, C<&ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime>, "GetDeviceTimeZoneLocationUpdatedTime"},
|
||||
{151, C<&ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime>, "SetDeviceTimeZoneLocationUpdatedTime"},
|
||||
{152, C<&ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime>, "GetUserSystemClockAutomaticCorrectionUpdatedTime"},
|
||||
@@ -852,6 +853,17 @@ Result ISystemSettingsServer::SetQuestFlag(QuestFlag quest_flag) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISystemSettingsServer::GetRebootlessSystemUpdateVersion(
|
||||
Out<RebootlessSystemUpdateVersion> out_version) {
|
||||
LOG_WARNING(Service_SET, "(STUBBED) called");
|
||||
|
||||
out_version->version = 0;
|
||||
std::memset(out_version->display_version.data(), 0, out_version->display_version.size());
|
||||
std::strcpy(out_version->display_version.data(), "0.0.0");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISystemSettingsServer::GetDeviceTimeZoneLocationName(
|
||||
Out<Service::PSC::Time::LocationName> out_name) {
|
||||
LOG_INFO(Service_SET, "called");
|
||||
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
Result SetSpeakerAutoMuteFlag(bool force_mute_on_headphone_removed);
|
||||
Result GetQuestFlag(Out<QuestFlag> out_quest_flag);
|
||||
Result SetQuestFlag(QuestFlag quest_flag);
|
||||
Result GetRebootlessSystemUpdateVersion(Out<RebootlessSystemUpdateVersion> out_version);
|
||||
Result GetDeviceTimeZoneLocationName(Out<Service::PSC::Time::LocationName> out_name);
|
||||
Result SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name);
|
||||
Result SetRegionCode(SystemRegionCode region_code);
|
||||
|
||||
@@ -131,6 +131,49 @@ Result Container::SetLayerBlending(u64 layer_id, bool enabled) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::SetLayerZIndex(u64 layer_id, s32 z_index) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) {
|
||||
LOG_DEBUG(Service_VI, "called, SetLayerZIndex layer_id={} z={} (cid={})", layer_id, z_index,
|
||||
layer->GetConsumerBinderId());
|
||||
layer_ref->z_index = z_index;
|
||||
} else {
|
||||
LOG_DEBUG(Service_VI,
|
||||
"called, SetLayerZIndex failed to find layer for layer_id={} (cid={})", layer_id,
|
||||
layer->GetConsumerBinderId());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) {
|
||||
*out_z_index = layer_ref->z_index;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
R_RETURN(VI::ResultNotFound);
|
||||
}
|
||||
|
||||
Result Container::SetLayerIsOverlay(u64 layer_id, bool is_overlay) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
m_surface_flinger->SetLayerIsOverlay(layer->GetConsumerBinderId(), is_overlay);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Container::LinkVsyncEvent(u64 display_id, Event* event) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_conductor->LinkVsyncEvent(display_id, event);
|
||||
|
||||
@@ -62,6 +62,9 @@ public:
|
||||
|
||||
Result SetLayerVisibility(u64 layer_id, bool visible);
|
||||
Result SetLayerBlending(u64 layer_id, bool enabled);
|
||||
Result SetLayerZIndex(u64 layer_id, s32 z_index);
|
||||
Result GetLayerZIndex(u64 layer_id, s32* out_z_index);
|
||||
Result SetLayerIsOverlay(u64 layer_id, bool is_overlay);
|
||||
|
||||
void LinkVsyncEvent(u64 display_id, Event* event);
|
||||
void UnlinkVsyncEvent(u64 display_id, Event* event);
|
||||
|
||||
@@ -116,6 +116,10 @@ Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) {
|
||||
R_RETURN(m_container->SetLayerBlending(layer_id, enabled));
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::SetLayerZIndex(s32 z_index, u64 layer_id) {
|
||||
R_RETURN(m_container->SetLayerZIndex(layer_id, z_index));
|
||||
}
|
||||
|
||||
Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
|
||||
AppletResourceUserId aruid) {
|
||||
LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid);
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
void DestroySharedLayerSession(Kernel::KProcess* owner_process);
|
||||
|
||||
Result SetLayerBlending(bool enabled, u64 layer_id);
|
||||
Result SetLayerZIndex(s32 z_index, u64 layer_id);
|
||||
|
||||
public:
|
||||
Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
|
||||
|
||||
Reference in New Issue
Block a user