Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c824b439b3 | |||
| d790af8b2b | |||
| 2697173d9f | |||
| 53a4189b0c | |||
| d4e34c3cd6 | |||
| 0ed849fd62 | |||
| a1b8e5d09a | |||
| c9ccdfbeac | |||
| 7979ccd956 | |||
| 8dd2e91427 | |||
| daf9cd9358 | |||
| 787b191abf | |||
| 038bcec111 | |||
| 2b514275ad | |||
| 9286976948 | |||
| ccd70819c2 | |||
| a49169e819 | |||
| d4d38dd44d | |||
| c182688ad6 | |||
| 2590b5a9ea | |||
| 918119ae1b | |||
| c6ff4a6f4d | |||
| faf628ad8d | |||
| ccaafaccfc | |||
| 77f9ecd32b | |||
| e018a48460 | |||
| b4164d295b | |||
| 4b91057688 | |||
| 1b04b72653 | |||
| 43af31836e | |||
| 721a92775d | |||
| e47b57a90f | |||
| 8abbc619a1 | |||
| 0a8e540681 | |||
| 08c0783d34 | |||
| 0084cceb20 | |||
| 02b36b0eb5 | |||
| 49c44e3fae | |||
| 62d772eaed | |||
| 06db4d94fd | |||
| 9d9fc8a675 | |||
| 8500ca797f | |||
| 256a50ad15 | |||
| b71bda45ae | |||
| 9bee885282 | |||
| 4dae5a52a8 | |||
| 3a1899d143 | |||
| 527b841c15 | |||
| 97b8c9d2c3 | |||
| 8fd266a7c4 | |||
| 183c445c30 | |||
| c7c8ffbc13 | |||
| 25383b9ff2 | |||
| c41365a56f | |||
| 9ad42fb0cf | |||
| b4db662053 | |||
| 934ce530f6 | |||
| b9fd1e2bed | |||
| 41836f3a17 | |||
| 01a4afee42 | |||
| c2f966dbc1 | |||
| bbe82d62b0 | |||
| 88d857499b | |||
| 4b81d19a1a | |||
| 50259d7bdc | |||
| b31880dc5e | |||
| 0526bf1895 | |||
| 2dd6411753 | |||
| 8d778c90e2 | |||
| 393cc3ef2f | |||
| b8b1747704 | |||
| 193bfefce4 | |||
| daae327e86 | |||
| 18fac59050 | |||
| ddfdeea3af | |||
| 3cc27e4dda | |||
| 01d96e1136 | |||
| 78d078e183 | |||
| 99e23bd0fd | |||
| 6b997c8f7f | |||
| 36abf67e79 | |||
| e60d281a01 | |||
| 78574746bd | |||
| 34b2c60f95 | |||
| c7ec7bc1f5 | |||
| 434d0922dc | |||
| d36a7a43c5 | |||
| 684b616f0d | |||
| 07a0242535 | |||
| 1487153e06 | |||
| 6f7b349461 | |||
| bfc5bacecd | |||
| 17a9b0178d | |||
| 1f43e5296f | |||
| 7228e22098 | |||
| 322d0200c8 | |||
| 80ec2feee8 | |||
| 954fc02fdd | |||
| 04cdecb7a1 | |||
| 6170337001 | |||
| 5edf24b510 | |||
| 2424eefad2 | |||
| 3a450c1395 | |||
| 2e5b5c2358 | |||
| 4ee9949639 | |||
| 03badbdd9b | |||
| 0f7b813d65 | |||
| 4de04eba39 | |||
| f17415d431 | |||
| d1abe8e92a | |||
| f763e23083 | |||
| b0da7e4262 | |||
| 7bbc98cfc3 | |||
| 5f309b88db | |||
| 77ef4fa907 | |||
| 701dedcfad | |||
| 42e1bb6d46 | |||
| 96cc9a9279 | |||
| 56c6f767ae | |||
| a43ee8d752 | |||
| 785c4946dd | |||
| 70485e690b | |||
| 3f695333cd | |||
| 471b2a4211 | |||
| 812fb30821 | |||
| 02560d6482 | |||
| 39f6d57c34 | |||
| b957a4862f | |||
| 1c75945dc4 |
@@ -6,9 +6,11 @@ yuzu emulator
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
|
||||
It is written in C++ with portability in mind, with builds actively maintained for Windows and Linux. The emulator is capable of running several commercial games.
|
||||
|
||||
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success.
|
||||
yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
|
||||
|
||||
Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
|
||||
|
||||
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
|
||||
|
||||
|
||||
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: d05c8df88d...fd568d51ed
+43
-9
@@ -14,8 +14,13 @@
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_core_manager.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
@@ -27,6 +32,7 @@
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/apm/controller.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
@@ -104,7 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
||||
}
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, cpu_core_manager{system}, applet_manager{system}, reporter{system} {}
|
||||
|
||||
Cpu& CurrentCpuCore() {
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
@@ -159,10 +166,6 @@ struct System::Impl {
|
||||
|
||||
LOG_DEBUG(Core, "Initialized OK");
|
||||
|
||||
// Reset counters and set time origin to current frame
|
||||
GetAndResetPerfStats();
|
||||
perf_stats.BeginSystemFrame();
|
||||
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -205,6 +208,25 @@ struct System::Impl {
|
||||
main_process->Run(load_parameters->main_thread_priority,
|
||||
load_parameters->main_thread_stack_size);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
} else if (!Settings::values.gamecard_path.empty()) {
|
||||
fs_controller.SetGameCard(
|
||||
GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path));
|
||||
}
|
||||
}
|
||||
|
||||
u64 title_id{0};
|
||||
if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
|
||||
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
|
||||
static_cast<u32>(load_result));
|
||||
}
|
||||
perf_stats = std::make_unique<PerfStats>(title_id);
|
||||
// Reset counters and set time origin to current frame
|
||||
GetAndResetPerfStats();
|
||||
perf_stats->BeginSystemFrame();
|
||||
|
||||
status = ResultStatus::Success;
|
||||
return status;
|
||||
}
|
||||
@@ -218,6 +240,8 @@ struct System::Impl {
|
||||
perf_results.game_fps);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
||||
perf_results.frametime * 1000.0);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
|
||||
perf_stats->GetMeanFrametime());
|
||||
|
||||
is_powered_on = false;
|
||||
|
||||
@@ -228,6 +252,7 @@ struct System::Impl {
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
perf_stats.reset();
|
||||
gpu_core.reset();
|
||||
|
||||
// Close all CPU/threading state
|
||||
@@ -285,7 +310,7 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
PerfStatsResults GetAndResetPerfStats() {
|
||||
return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
|
||||
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
Timing::CoreTiming core_timing;
|
||||
@@ -294,6 +319,7 @@ struct System::Impl {
|
||||
FileSys::VirtualFilesystem virtual_filesystem;
|
||||
/// ContentProviderUnion instance
|
||||
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
|
||||
Service::FileSystem::FileSystemController fs_controller;
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
@@ -326,7 +352,7 @@ struct System::Impl {
|
||||
ResultStatus status = ResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
Core::PerfStats perf_stats;
|
||||
std::unique_ptr<Core::PerfStats> perf_stats;
|
||||
Core::FrameLimiter frame_limiter;
|
||||
};
|
||||
|
||||
@@ -479,11 +505,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
|
||||
}
|
||||
|
||||
Core::PerfStats& System::GetPerfStats() {
|
||||
return impl->perf_stats;
|
||||
return *impl->perf_stats;
|
||||
}
|
||||
|
||||
const Core::PerfStats& System::GetPerfStats() const {
|
||||
return impl->perf_stats;
|
||||
return *impl->perf_stats;
|
||||
}
|
||||
|
||||
Core::FrameLimiter& System::FrameLimiter() {
|
||||
@@ -561,6 +587,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
|
||||
return *impl->content_provider;
|
||||
}
|
||||
|
||||
Service::FileSystem::FileSystemController& System::GetFileSystemController() {
|
||||
return impl->fs_controller;
|
||||
}
|
||||
|
||||
const Service::FileSystem::FileSystemController& System::GetFileSystemController() const {
|
||||
return impl->fs_controller;
|
||||
}
|
||||
|
||||
void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
|
||||
FileSys::ContentProvider* provider) {
|
||||
impl->content_provider->SetSlot(slot, provider);
|
||||
|
||||
@@ -47,6 +47,10 @@ namespace APM {
|
||||
class Controller;
|
||||
}
|
||||
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
} // namespace FileSystem
|
||||
|
||||
namespace Glue {
|
||||
class ARPManager;
|
||||
}
|
||||
@@ -299,6 +303,10 @@ public:
|
||||
|
||||
const FileSys::ContentProvider& GetContentProvider() const;
|
||||
|
||||
Service::FileSystem::FileSystemController& GetFileSystemController();
|
||||
|
||||
const Service::FileSystem::FileSystemController& GetFileSystemController() const;
|
||||
|
||||
void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
|
||||
FileSys::ContentProvider* provider);
|
||||
|
||||
|
||||
@@ -480,6 +480,10 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
|
||||
prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
|
||||
}
|
||||
|
||||
FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const {
|
||||
return prodinfo_decrypted;
|
||||
}
|
||||
|
||||
std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
|
||||
std::array<u8, 0x240> out{};
|
||||
if (prodinfo_decrypted != nullptr)
|
||||
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
bool HasProdInfo() const;
|
||||
FileSys::VirtualFile GetProdInfoRaw() const;
|
||||
void DecryptProdInfo(std::array<u8, 0x20> bis_key);
|
||||
FileSys::VirtualFile GetDecryptedProdInfo() const;
|
||||
std::array<u8, 0x240> GetETicketExtendedKek() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
|
||||
sysnand_cache(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
|
||||
usrnand_cache(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
|
||||
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))),
|
||||
sysnand_placeholder(std::make_unique<PlaceholderCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))),
|
||||
usrnand_placeholder(std::make_unique<PlaceholderCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {}
|
||||
|
||||
BISFactory::~BISFactory() = default;
|
||||
|
||||
VirtualDir BISFactory::GetSystemNANDContentDirectory() const {
|
||||
return GetOrCreateDirectoryRelative(nand_root, "/system/Contents");
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetUserNANDContentDirectory() const {
|
||||
return GetOrCreateDirectoryRelative(nand_root, "/user/Contents");
|
||||
}
|
||||
|
||||
RegisteredCache* BISFactory::GetSystemNANDContents() const {
|
||||
return sysnand_cache.get();
|
||||
}
|
||||
@@ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const {
|
||||
return usrnand_cache.get();
|
||||
}
|
||||
|
||||
PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const {
|
||||
return sysnand_placeholder.get();
|
||||
}
|
||||
|
||||
PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
|
||||
return usrnand_placeholder.get();
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
|
||||
// LayeredFS doesn't work on updates and title id-less homebrew
|
||||
if (title_id == 0 || (title_id & 0x800) > 0)
|
||||
if (title_id == 0 || (title_id & 0xFFF) == 0x800)
|
||||
return nullptr;
|
||||
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
@@ -39,4 +63,77 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
|
||||
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
|
||||
switch (id) {
|
||||
case BisPartitionId::CalibrationFile:
|
||||
return GetOrCreateDirectoryRelative(nand_root, "/prodinfof");
|
||||
case BisPartitionId::SafeMode:
|
||||
return GetOrCreateDirectoryRelative(nand_root, "/safe");
|
||||
case BisPartitionId::System:
|
||||
return GetOrCreateDirectoryRelative(nand_root, "/system");
|
||||
case BisPartitionId::User:
|
||||
return GetOrCreateDirectoryRelative(nand_root, "/user");
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
|
||||
Core::Crypto::KeyManager keys;
|
||||
Core::Crypto::PartitionDataManager pdm{
|
||||
Core::System::GetInstance().GetFilesystem()->OpenDirectory(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
|
||||
keys.PopulateFromPartitionData(pdm);
|
||||
|
||||
switch (id) {
|
||||
case BisPartitionId::CalibrationBinary:
|
||||
return pdm.GetDecryptedProdInfo();
|
||||
case BisPartitionId::BootConfigAndPackage2Part1:
|
||||
case BisPartitionId::BootConfigAndPackage2Part2:
|
||||
case BisPartitionId::BootConfigAndPackage2Part3:
|
||||
case BisPartitionId::BootConfigAndPackage2Part4:
|
||||
case BisPartitionId::BootConfigAndPackage2Part5:
|
||||
case BisPartitionId::BootConfigAndPackage2Part6: {
|
||||
const auto new_id = static_cast<u8>(id) -
|
||||
static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) +
|
||||
static_cast<u8>(Core::Crypto::Package2Type::NormalMain);
|
||||
return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id));
|
||||
}
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetImageDirectory() const {
|
||||
return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
|
||||
}
|
||||
|
||||
u64 BISFactory::GetSystemNANDFreeSpace() const {
|
||||
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
|
||||
if (sys_dir == nullptr)
|
||||
return 0;
|
||||
|
||||
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
|
||||
}
|
||||
|
||||
u64 BISFactory::GetSystemNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_system_size);
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDFreeSpace() const {
|
||||
const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
|
||||
if (usr_dir == nullptr)
|
||||
return 0;
|
||||
|
||||
return GetUserNANDTotalSpace() - usr_dir->GetSize();
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_user_size);
|
||||
}
|
||||
|
||||
u64 BISFactory::GetFullNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_total_size);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -10,7 +10,25 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class BisPartitionId : u32 {
|
||||
UserDataRoot = 20,
|
||||
CalibrationBinary = 27,
|
||||
CalibrationFile = 28,
|
||||
BootConfigAndPackage2Part1 = 21,
|
||||
BootConfigAndPackage2Part2 = 22,
|
||||
BootConfigAndPackage2Part3 = 23,
|
||||
BootConfigAndPackage2Part4 = 24,
|
||||
BootConfigAndPackage2Part5 = 25,
|
||||
BootConfigAndPackage2Part6 = 26,
|
||||
SafeMode = 29,
|
||||
System = 31,
|
||||
SystemProperEncryption = 32,
|
||||
SystemProperPartition = 33,
|
||||
User = 30,
|
||||
};
|
||||
|
||||
class RegisteredCache;
|
||||
class PlaceholderCache;
|
||||
|
||||
/// File system interface to the Built-In Storage
|
||||
/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
|
||||
@@ -20,12 +38,29 @@ public:
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
|
||||
~BISFactory();
|
||||
|
||||
VirtualDir GetSystemNANDContentDirectory() const;
|
||||
VirtualDir GetUserNANDContentDirectory() const;
|
||||
|
||||
RegisteredCache* GetSystemNANDContents() const;
|
||||
RegisteredCache* GetUserNANDContents() const;
|
||||
|
||||
PlaceholderCache* GetSystemNANDPlaceholder() const;
|
||||
PlaceholderCache* GetUserNANDPlaceholder() const;
|
||||
|
||||
VirtualDir GetModificationLoadRoot(u64 title_id) const;
|
||||
VirtualDir GetModificationDumpRoot(u64 title_id) const;
|
||||
|
||||
VirtualDir OpenPartition(BisPartitionId id) const;
|
||||
VirtualFile OpenPartitionStorage(BisPartitionId id) const;
|
||||
|
||||
VirtualDir GetImageDirectory() const;
|
||||
|
||||
u64 GetSystemNANDFreeSpace() const;
|
||||
u64 GetSystemNANDTotalSpace() const;
|
||||
u64 GetUserNANDFreeSpace() const;
|
||||
u64 GetUserNANDTotalSpace() const;
|
||||
u64 GetFullNANDTotalSpace() const;
|
||||
|
||||
private:
|
||||
VirtualDir nand_root;
|
||||
VirtualDir load_root;
|
||||
@@ -33,6 +68,9 @@ private:
|
||||
|
||||
std::unique_ptr<RegisteredCache> sysnand_cache;
|
||||
std::unique_ptr<RegisteredCache> usrnand_cache;
|
||||
|
||||
std::unique_ptr<PlaceholderCache> sysnand_placeholder;
|
||||
std::unique_ptr<PlaceholderCache> usrnand_placeholder;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -12,12 +12,16 @@
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000;
|
||||
constexpr std::array partition_names{
|
||||
"update",
|
||||
"normal",
|
||||
@@ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
VirtualDir XCI::ConcatenatedPseudoDirectory() {
|
||||
const auto out = std::make_shared<VectorVfsDirectory>();
|
||||
for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) {
|
||||
const auto& part = GetPartition(part_id);
|
||||
if (part == nullptr)
|
||||
continue;
|
||||
|
||||
for (const auto& file : part->GetFiles())
|
||||
out->AddFile(file);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, 0x200> XCI::GetCertificate() const {
|
||||
std::array<u8, 0x200> out;
|
||||
file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET);
|
||||
return out;
|
||||
}
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
const auto partition_index = static_cast<std::size_t>(part);
|
||||
const auto& partition = partitions[partition_index];
|
||||
|
||||
@@ -91,6 +91,8 @@ public:
|
||||
VirtualDir GetLogoPartition() const;
|
||||
|
||||
u64 GetProgramTitleID() const;
|
||||
u32 GetSystemUpdateVersion();
|
||||
u64 GetSystemUpdateTitleID() const;
|
||||
|
||||
bool HasProgramNCA() const;
|
||||
VirtualFile GetProgramNCAFile() const;
|
||||
@@ -106,6 +108,11 @@ public:
|
||||
|
||||
VirtualDir GetParentDirectory() const override;
|
||||
|
||||
// Creates a directory that contains all the NCAs in the gamecard
|
||||
VirtualDir ConcatenatedPseudoDirectory();
|
||||
|
||||
std::array<u8, 0x200> GetCertificate() const;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
|
||||
|
||||
@@ -120,6 +127,8 @@ private:
|
||||
std::shared_ptr<NCA> program;
|
||||
std::vector<std::shared_ptr<NCA>> ncas;
|
||||
|
||||
u64 update_normal_partition_end;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -528,6 +528,14 @@ u64 NCA::GetTitleId() const {
|
||||
return header.title_id;
|
||||
}
|
||||
|
||||
std::array<u8, 16> NCA::GetRightsId() const {
|
||||
return header.rights_id;
|
||||
}
|
||||
|
||||
u32 NCA::GetSDKVersion() const {
|
||||
return header.sdk_version;
|
||||
}
|
||||
|
||||
bool NCA::IsUpdate() const {
|
||||
return is_update;
|
||||
}
|
||||
|
||||
@@ -112,6 +112,8 @@ public:
|
||||
|
||||
NCAContentType GetType() const;
|
||||
u64 GetTitleId() const;
|
||||
std::array<u8, 0x10> GetRightsId() const;
|
||||
u32 GetSDKVersion() const;
|
||||
bool IsUpdate() const;
|
||||
|
||||
VirtualFile GetRomFS() const;
|
||||
|
||||
@@ -63,7 +63,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
|
||||
const auto dump_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
@@ -88,7 +89,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
|
||||
// LayeredExeFS
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
const auto load_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
|
||||
if (load_dir != nullptr && load_dir->GetSize() > 0) {
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(
|
||||
@@ -174,7 +176,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
if (Settings::values.dump_nso) {
|
||||
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
|
||||
title_id);
|
||||
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
|
||||
const auto dump_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
|
||||
const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
|
||||
@@ -186,7 +189,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
const auto load_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return nso;
|
||||
}
|
||||
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
@@ -224,7 +233,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
|
||||
|
||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
|
||||
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
const auto load_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
@@ -258,7 +273,13 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
|
||||
|
||||
std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
|
||||
const std::array<u8, 32>& build_id_) const {
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
const auto load_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
@@ -284,7 +305,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
|
||||
}
|
||||
|
||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
const auto load_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
|
||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
|
||||
load_dir == nullptr || load_dir->GetSize() <= 0) {
|
||||
return;
|
||||
@@ -393,6 +415,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
|
||||
|
||||
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
|
||||
VirtualFile update_raw) const {
|
||||
if (title_id == 0)
|
||||
return {};
|
||||
std::map<std::string, std::string, std::less<>> out;
|
||||
const auto& installed = Core::System::GetInstance().GetContentProvider();
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
@@ -423,7 +447,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
}
|
||||
|
||||
// General Mods (LayeredFS and IPS)
|
||||
const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
const auto mod_dir =
|
||||
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
|
||||
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
|
||||
for (const auto& mod : mod_dir->GetSubdirectories()) {
|
||||
std::string types;
|
||||
|
||||
@@ -48,6 +48,15 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors;
|
||||
}
|
||||
|
||||
acid_file_access_raw =
|
||||
file->ReadBytes(acid_header.fac_size, npdm_header.acid_offset + acid_header.fac_offset);
|
||||
aci0_file_access_raw =
|
||||
file->ReadBytes(aci_header.fah_size, npdm_header.aci_offset + aci_header.fah_offset);
|
||||
acid_service_access_raw =
|
||||
file->ReadBytes(acid_header.sac_size, npdm_header.acid_offset + acid_header.sac_offset);
|
||||
aci0_service_access_raw =
|
||||
file->ReadBytes(aci_header.sac_size, npdm_header.aci_offset + aci_header.sac_offset);
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -66,6 +75,22 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address
|
||||
aci_kernel_capabilities = std ::move(capabilities);
|
||||
}
|
||||
|
||||
std::vector<u8> ProgramMetadata::GetACIDFileAccessControl() const {
|
||||
return acid_file_access_raw;
|
||||
}
|
||||
|
||||
std::vector<u8> ProgramMetadata::GetACI0FileAccessControl() const {
|
||||
return aci0_file_access_raw;
|
||||
}
|
||||
|
||||
std::vector<u8> ProgramMetadata::GetACIDServiceAccessControl() const {
|
||||
return acid_service_access_raw;
|
||||
}
|
||||
|
||||
std::vector<u8> ProgramMetadata::GetACI0ServiceAccessControl() const {
|
||||
return aci0_service_access_raw;
|
||||
}
|
||||
|
||||
bool ProgramMetadata::Is64BitProgram() const {
|
||||
return npdm_header.has_64_bit_instructions;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ public:
|
||||
u32 GetSystemResourceSize() const;
|
||||
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||
|
||||
std::vector<u8> GetACIDFileAccessControl() const;
|
||||
std::vector<u8> GetACI0FileAccessControl() const;
|
||||
std::vector<u8> GetACIDServiceAccessControl() const;
|
||||
std::vector<u8> GetACI0ServiceAccessControl() const;
|
||||
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
@@ -164,6 +169,11 @@ private:
|
||||
FileAccessControl acid_file_access;
|
||||
FileAccessHeader aci_file_access;
|
||||
|
||||
std::vector<u8> acid_file_access_raw;
|
||||
std::vector<u8> aci0_file_access_raw;
|
||||
std::vector<u8> acid_service_access_raw;
|
||||
std::vector<u8> aci0_service_access_raw;
|
||||
|
||||
KernelCapabilityDescriptors aci_kernel_capabilities;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include "common/assert.h"
|
||||
@@ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
|
||||
static bool FollowsNcaIdFormat(std::string_view name) {
|
||||
static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
|
||||
std::regex_constants::icase);
|
||||
return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex);
|
||||
static const std::regex nca_id_cnmt_regex(
|
||||
"[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
|
||||
return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
|
||||
(name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
|
||||
}
|
||||
|
||||
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
|
||||
bool within_two_digit) {
|
||||
if (!within_two_digit) {
|
||||
return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
|
||||
}
|
||||
bool within_two_digit, bool cnmt_suffix) {
|
||||
if (!within_two_digit)
|
||||
return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
|
||||
Common::HexToString(nca_id, second_hex_upper));
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
return fmt::format("/000000{:02X}/{}.nca", hash[0],
|
||||
return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexToString(nca_id, second_hex_upper));
|
||||
}
|
||||
|
||||
@@ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
|
||||
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
||||
}
|
||||
|
||||
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
|
||||
|
||||
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
if (dir->GetFileRelative(path) != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
|
||||
if (dir2 == nullptr)
|
||||
return false;
|
||||
|
||||
const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
||||
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
return file->Resize(size);
|
||||
}
|
||||
|
||||
bool PlaceholderCache::Delete(const NcaID& id) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
if (dir->GetFileRelative(path) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
|
||||
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool PlaceholderCache::Exists(const NcaID& id) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
return dir->GetFileRelative(path) != nullptr;
|
||||
}
|
||||
|
||||
bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
return file->WriteBytes(data, offset) == data.size();
|
||||
}
|
||||
|
||||
bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
|
||||
const NcaID& install) const {
|
||||
const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
|
||||
|
||||
if (res != InstallResult::Success)
|
||||
return false;
|
||||
|
||||
return Delete(placeholder);
|
||||
}
|
||||
|
||||
bool PlaceholderCache::CleanAll() const {
|
||||
return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
|
||||
}
|
||||
|
||||
std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
|
||||
if (file == nullptr)
|
||||
return std::nullopt;
|
||||
|
||||
NCA nca{file};
|
||||
|
||||
if (nca.GetStatus() != Loader::ResultStatus::Success &&
|
||||
nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto rights_id = nca.GetRightsId();
|
||||
if (rights_id == NcaID{})
|
||||
return std::nullopt;
|
||||
|
||||
return rights_id;
|
||||
}
|
||||
|
||||
u64 PlaceholderCache::Size(const NcaID& id) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
|
||||
if (file == nullptr)
|
||||
return 0;
|
||||
|
||||
return file->GetSize();
|
||||
}
|
||||
|
||||
bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
return file->Resize(new_size);
|
||||
}
|
||||
|
||||
std::vector<NcaID> PlaceholderCache::List() const {
|
||||
std::vector<NcaID> out;
|
||||
for (const auto& sdir : dir->GetSubdirectories()) {
|
||||
for (const auto& file : sdir->GetFiles()) {
|
||||
const auto name = file->GetName();
|
||||
if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
|
||||
name[35] == 'a') {
|
||||
out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
NcaID PlaceholderCache::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
|
||||
NcaID out{};
|
||||
|
||||
const auto v1 = distribution(gen);
|
||||
const auto v2 = distribution(gen);
|
||||
std::memcpy(out.data(), &v1, sizeof(u64));
|
||||
std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
std::string_view path) const {
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
@@ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
|
||||
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
VirtualFile file;
|
||||
// Try all four modes of file storage:
|
||||
// (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
|
||||
// 00: /000000**/{:032X}.nca
|
||||
// 01: /{:032X}.nca
|
||||
// 10: /000000**/{:032x}.nca
|
||||
// 11: /{:032x}.nca
|
||||
for (u8 i = 0; i < 4; ++i) {
|
||||
const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
|
||||
// Try all five relevant modes of file storage:
|
||||
// (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
|
||||
// 000: /000000**/{:032X}.nca
|
||||
// 010: /{:032X}.nca
|
||||
// 100: /000000**/{:032x}.nca
|
||||
// 110: /{:032x}.nca
|
||||
// 111: /{:032x}.cnmt.nca
|
||||
for (u8 i = 0; i < 8; ++i) {
|
||||
if ((i % 2) == 1 && i != 7)
|
||||
continue;
|
||||
const auto path =
|
||||
GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
|
||||
file = OpenFileOrDirectoryConcat(dir, path);
|
||||
if (file != nullptr)
|
||||
return file;
|
||||
@@ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
|
||||
memcpy(id.data(), hash.data(), 16);
|
||||
}
|
||||
|
||||
std::string path = GetRelativePathFromNcaID(id, false, true);
|
||||
std::string path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
|
||||
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
|
||||
|
||||
@@ -25,6 +25,8 @@ enum class NCAContentType : u8;
|
||||
enum class TitleType : u8;
|
||||
|
||||
struct ContentRecord;
|
||||
struct MetaRecord;
|
||||
class RegisteredCache;
|
||||
|
||||
using NcaID = std::array<u8, 0x10>;
|
||||
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
|
||||
@@ -89,6 +91,27 @@ protected:
|
||||
Core::Crypto::KeyManager keys;
|
||||
};
|
||||
|
||||
class PlaceholderCache {
|
||||
public:
|
||||
explicit PlaceholderCache(VirtualDir dir);
|
||||
|
||||
bool Create(const NcaID& id, u64 size) const;
|
||||
bool Delete(const NcaID& id) const;
|
||||
bool Exists(const NcaID& id) const;
|
||||
bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
|
||||
bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
|
||||
bool CleanAll() const;
|
||||
std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
|
||||
u64 Size(const NcaID& id) const;
|
||||
bool SetSize(const NcaID& id, u64 new_size) const;
|
||||
std::vector<NcaID> List() const;
|
||||
|
||||
static NcaID Generate();
|
||||
|
||||
private:
|
||||
VirtualDir dir;
|
||||
};
|
||||
|
||||
/*
|
||||
* A class that catalogues NCAs in the registered directory structure.
|
||||
* Nintendo's registered format follows this structure:
|
||||
@@ -103,6 +126,8 @@ protected:
|
||||
* when 4GB splitting can be ignored.)
|
||||
*/
|
||||
class RegisteredCache : public ContentProvider {
|
||||
friend class PlaceholderCache;
|
||||
|
||||
public:
|
||||
// Parsing function defines the conversion from raw file to NCA. If there are other steps
|
||||
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@@ -34,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
|
||||
this->update_raw = std::move(update_raw);
|
||||
}
|
||||
|
||||
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
|
||||
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
|
||||
if (!updatable)
|
||||
return MakeResult<VirtualFile>(file);
|
||||
|
||||
@@ -43,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
|
||||
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
|
||||
}
|
||||
|
||||
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
|
||||
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
|
||||
ContentRecordType type) const {
|
||||
std::shared_ptr<NCA> res;
|
||||
|
||||
switch (storage) {
|
||||
@@ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
|
||||
res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
|
||||
break;
|
||||
case StorageId::NandSystem:
|
||||
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
|
||||
res =
|
||||
Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
|
||||
title_id, type);
|
||||
break;
|
||||
case StorageId::NandUser:
|
||||
res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
|
||||
res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
|
||||
title_id, type);
|
||||
break;
|
||||
case StorageId::SdCard:
|
||||
res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type);
|
||||
res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
|
||||
title_id, type);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
|
||||
|
||||
@@ -33,8 +33,8 @@ public:
|
||||
~RomFSFactory();
|
||||
|
||||
void SetPackedUpdate(VirtualFile update_raw);
|
||||
ResultVal<VirtualFile> OpenCurrentProcess();
|
||||
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
|
||||
ResultVal<VirtualFile> OpenCurrentProcess() const;
|
||||
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
|
||||
|
||||
private:
|
||||
VirtualFile file;
|
||||
|
||||
@@ -15,22 +15,8 @@ namespace FileSys {
|
||||
|
||||
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
|
||||
|
||||
std::string SaveDataDescriptor::DebugInfo() const {
|
||||
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
|
||||
"rank={}, index={}]",
|
||||
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
|
||||
static_cast<u8>(rank), index);
|
||||
}
|
||||
|
||||
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
|
||||
// Delete all temporary storages
|
||||
// On hardware, it is expected that temporary storage be empty at first use.
|
||||
dir->DeleteSubdirectoryRecursive("temp");
|
||||
}
|
||||
|
||||
SaveDataFactory::~SaveDataFactory() = default;
|
||||
|
||||
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
|
||||
namespace {
|
||||
void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
|
||||
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
|
||||
if (meta.zero_1 != 0) {
|
||||
LOG_WARNING(Service_FS,
|
||||
@@ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat
|
||||
"non-zero ({:016X}{:016X})",
|
||||
meta.user_id[1], meta.user_id[0]);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string save_directory =
|
||||
std::string SaveDataDescriptor::DebugInfo() const {
|
||||
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
|
||||
"save_id={:016X}, "
|
||||
"rank={}, index={}]",
|
||||
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
|
||||
static_cast<u8>(rank), index);
|
||||
}
|
||||
|
||||
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
|
||||
// Delete all temporary storages
|
||||
// On hardware, it is expected that temporary storage be empty at first use.
|
||||
dir->DeleteSubdirectoryRecursive("temp");
|
||||
}
|
||||
|
||||
SaveDataFactory::~SaveDataFactory() = default;
|
||||
|
||||
ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
|
||||
const SaveDataDescriptor& meta) const {
|
||||
PrintSaveDataDescriptorWarnings(meta);
|
||||
|
||||
const auto save_directory =
|
||||
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
|
||||
// TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
|
||||
// But, user_ids don't match so this works for now.
|
||||
auto out = dir->CreateDirectoryRelative(save_directory);
|
||||
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(DarkLordZach): Find out correct error code.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
}
|
||||
|
||||
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
|
||||
const SaveDataDescriptor& meta) const {
|
||||
|
||||
const auto save_directory =
|
||||
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
|
||||
|
||||
auto out = dir->GetDirectoryRelative(save_directory);
|
||||
|
||||
if (out == nullptr) {
|
||||
// TODO(bunnei): This is a work-around to always create a save data directory if it does not
|
||||
// already exist. This is a hack, as we do not understand yet how this works on hardware.
|
||||
// Without a save data directory, many games will assert on boot. This should not have any
|
||||
// bad side-effects.
|
||||
out = dir->CreateDirectoryRelative(save_directory);
|
||||
}
|
||||
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(Subv): Find out correct error code.
|
||||
@@ -152,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
|
||||
}
|
||||
|
||||
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||
SaveDataSize new_value) {
|
||||
SaveDataSize new_value) const {
|
||||
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
|
||||
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
|
||||
|
||||
|
||||
@@ -64,7 +64,8 @@ public:
|
||||
explicit SaveDataFactory(VirtualDir dir);
|
||||
~SaveDataFactory();
|
||||
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
|
||||
ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
|
||||
|
||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||
|
||||
@@ -73,7 +74,8 @@ public:
|
||||
u128 user_id, u64 save_id);
|
||||
|
||||
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
|
||||
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
|
||||
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
|
||||
SaveDataSize new_value) const;
|
||||
|
||||
private:
|
||||
VirtualDir dir;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/xts_archive.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
|
||||
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
|
||||
[](const VirtualFile& file, const NcaID& id) {
|
||||
return NAX{file, id}.GetDecrypted();
|
||||
})) {}
|
||||
})),
|
||||
placeholder(std::make_unique<PlaceholderCache>(
|
||||
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {}
|
||||
|
||||
SDMCFactory::~SDMCFactory() = default;
|
||||
|
||||
ResultVal<VirtualDir> SDMCFactory::Open() {
|
||||
ResultVal<VirtualDir> SDMCFactory::Open() const {
|
||||
return MakeResult<VirtualDir>(dir);
|
||||
}
|
||||
|
||||
VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
|
||||
return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents");
|
||||
}
|
||||
|
||||
RegisteredCache* SDMCFactory::GetSDMCContents() const {
|
||||
return contents.get();
|
||||
}
|
||||
|
||||
PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
|
||||
return placeholder.get();
|
||||
}
|
||||
|
||||
VirtualDir SDMCFactory::GetImageDirectory() const {
|
||||
return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
|
||||
}
|
||||
|
||||
u64 SDMCFactory::GetSDMCFreeSpace() const {
|
||||
return GetSDMCTotalSpace() - dir->GetSize();
|
||||
}
|
||||
|
||||
u64 SDMCFactory::GetSDMCTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.sdmc_size);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
namespace FileSys {
|
||||
|
||||
class RegisteredCache;
|
||||
class PlaceholderCache;
|
||||
|
||||
/// File system interface to the SDCard archive
|
||||
class SDMCFactory {
|
||||
@@ -18,13 +19,23 @@ public:
|
||||
explicit SDMCFactory(VirtualDir dir);
|
||||
~SDMCFactory();
|
||||
|
||||
ResultVal<VirtualDir> Open();
|
||||
ResultVal<VirtualDir> Open() const;
|
||||
|
||||
VirtualDir GetSDMCContentDirectory() const;
|
||||
|
||||
RegisteredCache* GetSDMCContents() const;
|
||||
PlaceholderCache* GetSDMCPlaceholder() const;
|
||||
|
||||
VirtualDir GetImageDirectory() const;
|
||||
|
||||
u64 GetSDMCFreeSpace() const;
|
||||
u64 GetSDMCTotalSpace() const;
|
||||
|
||||
private:
|
||||
VirtualDir dir;
|
||||
|
||||
std::unique_ptr<RegisteredCache> contents;
|
||||
std::unique_ptr<PlaceholderCache> placeholder;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@@ -78,6 +79,10 @@ Loader::ResultStatus NSP::GetStatus() const {
|
||||
}
|
||||
|
||||
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
|
||||
if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
const auto iter = program_status.find(title_id);
|
||||
if (iter == program_status.end())
|
||||
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
@@ -85,12 +90,29 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
|
||||
}
|
||||
|
||||
u64 NSP::GetFirstTitleID() const {
|
||||
if (IsExtractedType()) {
|
||||
return GetProgramTitleID();
|
||||
}
|
||||
|
||||
if (program_status.empty())
|
||||
return 0;
|
||||
return program_status.begin()->first;
|
||||
}
|
||||
|
||||
u64 NSP::GetProgramTitleID() const {
|
||||
if (IsExtractedType()) {
|
||||
if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProgramMetadata meta;
|
||||
if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
|
||||
return meta.GetTitleID();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const auto out = GetFirstTitleID();
|
||||
if ((out & 0x800) == 0)
|
||||
return out;
|
||||
@@ -102,6 +124,10 @@ u64 NSP::GetProgramTitleID() const {
|
||||
}
|
||||
|
||||
std::vector<u64> NSP::GetTitleIDs() const {
|
||||
if (IsExtractedType()) {
|
||||
return {GetProgramTitleID()};
|
||||
}
|
||||
|
||||
std::vector<u64> out;
|
||||
out.reserve(ncas.size());
|
||||
for (const auto& kv : ncas)
|
||||
@@ -222,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
|
||||
|
||||
void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
for (const auto& outer_file : files) {
|
||||
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
|
||||
if (outer_file->GetName().size() < 9 ||
|
||||
outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -166,6 +166,8 @@ ResultCode Process::ClearSignalState() {
|
||||
}
|
||||
|
||||
ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
program_metadata = std::make_unique<FileSys::ProgramMetadata>(metadata);
|
||||
|
||||
program_id = metadata.GetTitleID();
|
||||
ideal_core = metadata.GetMainThreadCore();
|
||||
is_64bit_process = metadata.Is64BitProgram();
|
||||
@@ -279,6 +281,8 @@ void Process::FreeTLSRegion(VAddr tls_address) {
|
||||
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory));
|
||||
|
||||
modules.insert_or_assign(base_addr, memory);
|
||||
|
||||
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
|
||||
MemoryState memory_state) {
|
||||
const auto vma = vm_manager
|
||||
|
||||
@@ -277,6 +277,14 @@ public:
|
||||
|
||||
void LoadModule(CodeSet module_, VAddr base_addr);
|
||||
|
||||
const std::map<VAddr, std::shared_ptr<std::vector<u8>>>& GetModules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
const FileSys::ProgramMetadata& GetProgramMetadata() const {
|
||||
return *program_metadata;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Thread-local storage management
|
||||
|
||||
@@ -380,6 +388,12 @@ private:
|
||||
|
||||
/// Name of this process
|
||||
std::string name;
|
||||
|
||||
/// List of loaded modules, keyed by base address
|
||||
std::map<VAddr, std::shared_ptr<std::vector<u8>>> modules;
|
||||
|
||||
/// Program Metadata
|
||||
std::unique_ptr<FileSys::ProgramMetadata> program_metadata;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -296,12 +296,6 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
|
||||
}
|
||||
|
||||
ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||
const auto end_addr = target + size;
|
||||
const auto last_addr = end_addr - 1;
|
||||
VAddr cur_addr = target;
|
||||
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
|
||||
// Check how much memory we've already mapped.
|
||||
const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size);
|
||||
if (mapped_size_result.Failed()) {
|
||||
@@ -324,13 +318,16 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||
|
||||
// Keep track of the memory regions we unmap.
|
||||
std::vector<std::pair<u64, u64>> mapped_regions;
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
|
||||
// Iterate, trying to map memory.
|
||||
{
|
||||
cur_addr = target;
|
||||
const auto end_addr = target + size;
|
||||
const auto last_addr = end_addr - 1;
|
||||
VAddr cur_addr = target;
|
||||
|
||||
auto iter = FindVMA(target);
|
||||
ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
|
||||
while (true) {
|
||||
const auto& vma = iter->second;
|
||||
@@ -342,7 +339,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||
const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
|
||||
if (vma.state == MemoryState::Unmapped) {
|
||||
const auto map_res =
|
||||
MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size, 0), 0,
|
||||
MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size), 0,
|
||||
map_size, MemoryState::Heap, VMAPermission::ReadWrite);
|
||||
result = map_res.Code();
|
||||
if (result.IsError()) {
|
||||
@@ -360,7 +357,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||
// Advance to the next block.
|
||||
cur_addr = vma_end;
|
||||
iter = FindVMA(cur_addr);
|
||||
ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +365,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||
if (result.IsError()) {
|
||||
for (const auto [unmap_address, unmap_size] : mapped_regions) {
|
||||
ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(),
|
||||
"MapPhysicalMemory un-map on error");
|
||||
"Failed to unmap memory range.");
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -381,12 +378,6 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||
}
|
||||
|
||||
ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
|
||||
const auto end_addr = target + size;
|
||||
const auto last_addr = end_addr - 1;
|
||||
VAddr cur_addr = target;
|
||||
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
|
||||
// Check how much memory is currently mapped.
|
||||
const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size);
|
||||
if (mapped_size_result.Failed()) {
|
||||
@@ -401,13 +392,16 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
|
||||
|
||||
// Keep track of the memory regions we unmap.
|
||||
std::vector<std::pair<u64, u64>> unmapped_regions;
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
|
||||
// Try to unmap regions.
|
||||
{
|
||||
cur_addr = target;
|
||||
const auto end_addr = target + size;
|
||||
const auto last_addr = end_addr - 1;
|
||||
VAddr cur_addr = target;
|
||||
|
||||
auto iter = FindVMA(target);
|
||||
ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
|
||||
while (true) {
|
||||
const auto& vma = iter->second;
|
||||
@@ -434,7 +428,7 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
|
||||
// Advance to the next block.
|
||||
cur_addr = vma_end;
|
||||
iter = FindVMA(cur_addr);
|
||||
ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,10 +437,12 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
|
||||
if (result.IsError()) {
|
||||
for (const auto [map_address, map_size] : unmapped_regions) {
|
||||
const auto remap_res =
|
||||
MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size, 0), 0,
|
||||
map_size, MemoryState::Heap, VMAPermission::None);
|
||||
ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error");
|
||||
MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size), 0, map_size,
|
||||
MemoryState::Heap, VMAPermission::None);
|
||||
ASSERT_MSG(remap_res.Succeeded(), "Failed to remap a memory block.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Update mapped amount
|
||||
@@ -757,20 +753,26 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre
|
||||
// Always merge allocated memory blocks, even when they don't share the same backing block.
|
||||
if (left.type == VMAType::AllocatedMemoryBlock &&
|
||||
(left.backing_block != right.backing_block || left.offset + left.size != right.offset)) {
|
||||
const auto right_begin = right.backing_block->begin() + right.offset;
|
||||
const auto right_end = right_begin + right.size;
|
||||
|
||||
// Check if we can save work.
|
||||
if (left.offset == 0 && left.size == left.backing_block->size()) {
|
||||
// Fast case: left is an entire backing block.
|
||||
left.backing_block->insert(left.backing_block->end(),
|
||||
right.backing_block->begin() + right.offset,
|
||||
right.backing_block->begin() + right.offset + right.size);
|
||||
left.backing_block->insert(left.backing_block->end(), right_begin, right_end);
|
||||
} else {
|
||||
// Slow case: make a new memory block for left and right.
|
||||
const auto left_begin = left.backing_block->begin() + left.offset;
|
||||
const auto left_end = left_begin + left.size;
|
||||
const auto left_size = static_cast<std::size_t>(std::distance(left_begin, left_end));
|
||||
const auto right_size = static_cast<std::size_t>(std::distance(right_begin, right_end));
|
||||
|
||||
auto new_memory = std::make_shared<PhysicalMemory>();
|
||||
new_memory->insert(new_memory->end(), left.backing_block->begin() + left.offset,
|
||||
left.backing_block->begin() + left.offset + left.size);
|
||||
new_memory->insert(new_memory->end(), right.backing_block->begin() + right.offset,
|
||||
right.backing_block->begin() + right.offset + right.size);
|
||||
left.backing_block = new_memory;
|
||||
new_memory->reserve(left_size + right_size);
|
||||
new_memory->insert(new_memory->end(), left_begin, left_end);
|
||||
new_memory->insert(new_memory->end(), right_begin, right_end);
|
||||
|
||||
left.backing_block = std::move(new_memory);
|
||||
left.offset = 0;
|
||||
}
|
||||
|
||||
@@ -965,7 +967,7 @@ ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
|
||||
|
||||
VAddr cur_addr = address;
|
||||
auto iter = FindVMA(cur_addr);
|
||||
ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
|
||||
while (true) {
|
||||
const auto& vma = iter->second;
|
||||
@@ -986,7 +988,7 @@ ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
|
||||
// Advance to the next block.
|
||||
cur_addr = vma_end;
|
||||
iter = std::next(iter);
|
||||
ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
}
|
||||
|
||||
return MakeResult(mapped_size);
|
||||
@@ -1000,7 +1002,7 @@ ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr ad
|
||||
|
||||
VAddr cur_addr = address;
|
||||
auto iter = FindVMA(cur_addr);
|
||||
ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
|
||||
while (true) {
|
||||
const auto& vma = iter->second;
|
||||
@@ -1029,7 +1031,7 @@ ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr ad
|
||||
// Advance to the next block.
|
||||
cur_addr = vma_end;
|
||||
iter = std::next(iter);
|
||||
ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
|
||||
ASSERT(iter != vma_map.end());
|
||||
}
|
||||
|
||||
return MakeResult(mapped_size);
|
||||
|
||||
@@ -454,8 +454,8 @@ public:
|
||||
|
||||
/// Maps memory at a given address.
|
||||
///
|
||||
/// @param addr The virtual address to map memory at.
|
||||
/// @param size The amount of memory to map.
|
||||
/// @param target The virtual address to map memory at.
|
||||
/// @param size The amount of memory to map.
|
||||
///
|
||||
/// @note The destination address must lie within the Map region.
|
||||
///
|
||||
@@ -468,8 +468,8 @@ public:
|
||||
|
||||
/// Unmaps memory at a given address.
|
||||
///
|
||||
/// @param addr The virtual address to unmap memory at.
|
||||
/// @param size The amount of memory to unmap.
|
||||
/// @param target The virtual address to unmap memory at.
|
||||
/// @param size The amount of memory to unmap.
|
||||
///
|
||||
/// @note The destination address must lie within the Map region.
|
||||
///
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
|
||||
constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
|
||||
|
||||
static std::string GetImagePath(Common::UUID uuid) {
|
||||
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
@@ -41,20 +44,31 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
|
||||
return static_cast<u32>(std::min(size, max_jpeg_image_size));
|
||||
}
|
||||
|
||||
class IProfile final : public ServiceFramework<IProfile> {
|
||||
class IProfileCommon : public ServiceFramework<IProfileCommon> {
|
||||
public:
|
||||
explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager)
|
||||
: ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) {
|
||||
explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id,
|
||||
ProfileManager& profile_manager)
|
||||
: ServiceFramework(name), profile_manager(profile_manager), user_id(user_id) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IProfile::Get, "Get"},
|
||||
{1, &IProfile::GetBase, "GetBase"},
|
||||
{10, &IProfile::GetImageSize, "GetImageSize"},
|
||||
{11, &IProfile::LoadImage, "LoadImage"},
|
||||
{0, &IProfileCommon::Get, "Get"},
|
||||
{1, &IProfileCommon::GetBase, "GetBase"},
|
||||
{10, &IProfileCommon::GetImageSize, "GetImageSize"},
|
||||
{11, &IProfileCommon::LoadImage, "LoadImage"},
|
||||
};
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
if (editor_commands) {
|
||||
static const FunctionInfo editor_functions[] = {
|
||||
{100, &IProfileCommon::Store, "Store"},
|
||||
{101, &IProfileCommon::StoreWithImage, "StoreWithImage"},
|
||||
};
|
||||
|
||||
RegisterHandlers(editor_functions);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
|
||||
ProfileBase profile_base{};
|
||||
@@ -127,10 +141,91 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
const ProfileManager& profile_manager;
|
||||
void Store(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto base = rp.PopRaw<ProfileBase>();
|
||||
|
||||
const auto user_data = ctx.ReadBuffer();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
|
||||
if (user_data.size() < sizeof(ProfileData)) {
|
||||
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_BUFFER_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileData data;
|
||||
std::memcpy(&data, user_data.data(), sizeof(ProfileData));
|
||||
|
||||
if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
|
||||
LOG_ERROR(Service_ACC, "Failed to update profile data and base!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_FAILED_SAVE_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void StoreWithImage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto base = rp.PopRaw<ProfileBase>();
|
||||
|
||||
const auto user_data = ctx.ReadBuffer();
|
||||
const auto image_data = ctx.ReadBuffer(1);
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
|
||||
if (user_data.size() < sizeof(ProfileData)) {
|
||||
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_BUFFER_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileData data;
|
||||
std::memcpy(&data, user_data.data(), sizeof(ProfileData));
|
||||
|
||||
FileUtil::IOFile image(GetImagePath(user_id), "wb");
|
||||
|
||||
if (!image.IsOpen() || !image.Resize(image_data.size()) ||
|
||||
image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
|
||||
!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
|
||||
LOG_ERROR(Service_ACC, "Failed to update profile data, base, and image!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_FAILED_SAVE_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
ProfileManager& profile_manager;
|
||||
Common::UUID user_id; ///< The user id this profile refers to.
|
||||
};
|
||||
|
||||
class IProfile final : public IProfileCommon {
|
||||
public:
|
||||
IProfile(Common::UUID user_id, ProfileManager& profile_manager)
|
||||
: IProfileCommon("IProfile", false, user_id, profile_manager) {}
|
||||
};
|
||||
|
||||
class IProfileEditor final : public IProfileCommon {
|
||||
public:
|
||||
IProfileEditor(Common::UUID user_id, ProfileManager& profile_manager)
|
||||
: IProfileCommon("IProfileEditor", true, user_id, profile_manager) {}
|
||||
};
|
||||
|
||||
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
|
||||
public:
|
||||
IManagerForApplication() : ServiceFramework("IManagerForApplication") {
|
||||
@@ -322,6 +417,17 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
|
||||
rb.Push(is_locked);
|
||||
}
|
||||
|
||||
void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IProfileEditor>(user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
|
||||
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
|
||||
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
|
||||
void GetProfileEditor(Kernel::HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
ResultCode InitializeApplicationInfoBase(u64 process_id);
|
||||
|
||||
@@ -41,7 +41,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{202, nullptr, "CancelUserRegistration"},
|
||||
{203, nullptr, "DeleteUser"},
|
||||
{204, nullptr, "SetUserPosition"},
|
||||
{205, nullptr, "GetProfileEditor"},
|
||||
{205, &ACC_SU::GetProfileEditor, "GetProfileEditor"},
|
||||
{206, nullptr, "CompleteUserRegistrationForcibly"},
|
||||
{210, nullptr, "CreateFloatingRegistrationRequest"},
|
||||
{230, nullptr, "AuthenticateServiceAsync"},
|
||||
|
||||
@@ -305,6 +305,17 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
|
||||
const ProfileData& data_new) {
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (index.has_value() && SetProfileBase(uuid, profile_new)) {
|
||||
profiles[*index].data = data_new;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProfileManager::ParseUserSaveFile() {
|
||||
FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
|
||||
|
||||
@@ -91,6 +91,8 @@ public:
|
||||
|
||||
bool RemoveUser(Common::UUID uuid);
|
||||
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
|
||||
bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
|
||||
const ProfileData& data_new);
|
||||
|
||||
private:
|
||||
void ParseUserSaveFile();
|
||||
|
||||
@@ -56,7 +56,8 @@ struct LaunchParameters {
|
||||
};
|
||||
static_assert(sizeof(LaunchParameters) == 0x88);
|
||||
|
||||
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
|
||||
IWindowController::IWindowController(Core::System& system_)
|
||||
: ServiceFramework("IWindowController"), system{system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateWindow"},
|
||||
@@ -75,7 +76,7 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
|
||||
IWindowController::~IWindowController() = default;
|
||||
|
||||
void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
|
||||
const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID();
|
||||
const u64 process_id = system.CurrentProcess()->GetProcessID();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
|
||||
|
||||
@@ -231,8 +232,9 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
|
||||
|
||||
IDebugFunctions::~IDebugFunctions() = default;
|
||||
|
||||
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
|
||||
ISelfController::ISelfController(Core::System& system_,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger_)
|
||||
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Exit"},
|
||||
@@ -280,7 +282,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& kernel = system_.Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"ISelfController:LaunchableEvent");
|
||||
|
||||
@@ -501,8 +503,7 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
|
||||
rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
|
||||
@@ -937,9 +938,8 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator(u64 current_process_title_id)
|
||||
: ServiceFramework("ILibraryAppletCreator"),
|
||||
current_process_title_id(current_process_title_id) {
|
||||
ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_)
|
||||
: ServiceFramework("ILibraryAppletCreator"), system{system_} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
@@ -961,8 +961,8 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
|
||||
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
|
||||
static_cast<u32>(applet_id), applet_mode);
|
||||
|
||||
const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
|
||||
const auto applet = applet_manager.GetApplet(applet_id, current_process_title_id);
|
||||
const auto& applet_manager{system.GetAppletManager()};
|
||||
const auto applet = applet_manager.GetApplet(applet_id);
|
||||
|
||||
if (applet == nullptr) {
|
||||
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
|
||||
@@ -999,8 +999,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
|
||||
const auto handle{rp.Pop<Kernel::Handle>()};
|
||||
|
||||
const auto transfer_mem =
|
||||
Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(
|
||||
handle);
|
||||
system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
|
||||
|
||||
if (transfer_mem == nullptr) {
|
||||
LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
|
||||
@@ -1018,7 +1017,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
|
||||
rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
|
||||
IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
: ServiceFramework("IApplicationFunctions"), system{system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
|
||||
@@ -1143,13 +1143,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
|
||||
|
||||
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u128 uid = rp.PopRaw<u128>(); // What does this do?
|
||||
LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
|
||||
u128 user_id = rp.PopRaw<u128>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
|
||||
|
||||
FileSys::SaveDataDescriptor descriptor{};
|
||||
descriptor.title_id = Core::CurrentProcess()->GetTitleID();
|
||||
descriptor.user_id = user_id;
|
||||
descriptor.type = FileSys::SaveDataType::SaveData;
|
||||
const auto res = system.GetFileSystemController().CreateSaveData(
|
||||
FileSys::SaveDataSpaceId::NandUser, descriptor);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(res.Code());
|
||||
rb.Push<u64>(0);
|
||||
} // namespace Service::AM
|
||||
}
|
||||
|
||||
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
|
||||
// Takes an input u32 Result, no output.
|
||||
@@ -1180,7 +1188,7 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
// Get supported languages from NACP, if possible
|
||||
// Default to 0 (all languages supported)
|
||||
u32 supported_languages = 0;
|
||||
FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()};
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
@@ -1188,8 +1196,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
// Call IApplicationManagerInterface implementation.
|
||||
auto& service_manager = Core::System::GetInstance().ServiceManager();
|
||||
auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2");
|
||||
auto& service_manager = system.ServiceManager();
|
||||
auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
|
||||
auto app_man = ns_am2->GetApplicationManagerInterface();
|
||||
|
||||
// Get desired application language
|
||||
@@ -1261,8 +1269,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
|
||||
"new_journal={:016X}",
|
||||
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
|
||||
|
||||
FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
|
||||
{new_normal_size, new_journal_size});
|
||||
system.GetFileSystemController().WriteSaveDataSize(
|
||||
type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -1281,8 +1289,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
|
||||
user_id[1], user_id[0]);
|
||||
|
||||
const auto size =
|
||||
FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
|
||||
const auto size = system.GetFileSystemController().ReadSaveDataSize(
|
||||
type, system.CurrentProcess()->GetTitleID(), user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -1300,9 +1308,9 @@ void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestCon
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
|
||||
// game boot
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());
|
||||
// Needed on game boot
|
||||
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
||||
|
||||
std::make_shared<AppletAE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
namespace AM {
|
||||
namespace Service::AM {
|
||||
|
||||
enum SystemLanguage {
|
||||
Japanese = 0,
|
||||
@@ -47,7 +50,7 @@ public:
|
||||
PerformanceModeChanged = 31,
|
||||
};
|
||||
|
||||
AppletMessageQueue();
|
||||
explicit AppletMessageQueue(Kernel::KernelCore& kernel);
|
||||
~AppletMessageQueue();
|
||||
|
||||
const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const;
|
||||
@@ -65,12 +68,14 @@ private:
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
public:
|
||||
IWindowController();
|
||||
explicit IWindowController(Core::System& system_);
|
||||
~IWindowController() override;
|
||||
|
||||
private:
|
||||
void GetAppletResourceUserId(Kernel::HLERequestContext& ctx);
|
||||
void AcquireForegroundRights(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class IAudioController final : public ServiceFramework<IAudioController> {
|
||||
@@ -113,7 +118,8 @@ public:
|
||||
|
||||
class ISelfController final : public ServiceFramework<ISelfController> {
|
||||
public:
|
||||
explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
explicit ISelfController(Core::System& system_,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger_);
|
||||
~ISelfController() override;
|
||||
|
||||
private:
|
||||
@@ -208,7 +214,7 @@ private:
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
public:
|
||||
ILibraryAppletCreator(u64 current_process_title_id);
|
||||
explicit ILibraryAppletCreator(Core::System& system_);
|
||||
~ILibraryAppletCreator() override;
|
||||
|
||||
private:
|
||||
@@ -216,12 +222,12 @@ private:
|
||||
void CreateStorage(Kernel::HLERequestContext& ctx);
|
||||
void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
|
||||
|
||||
u64 current_process_title_id;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
|
||||
public:
|
||||
IApplicationFunctions();
|
||||
explicit IApplicationFunctions(Core::System& system_);
|
||||
~IApplicationFunctions() override;
|
||||
|
||||
private:
|
||||
@@ -245,6 +251,7 @@ private:
|
||||
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Kernel::EventPair gpu_error_detected_event;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
@@ -278,5 +285,4 @@ public:
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system);
|
||||
|
||||
} // namespace AM
|
||||
} // namespace Service
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -50,7 +50,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISelfController>(nvflinger);
|
||||
rb.PushIpcInterface<ISelfController>(system, nvflinger);
|
||||
}
|
||||
|
||||
void GetWindowController(Kernel::HLERequestContext& ctx) {
|
||||
@@ -58,7 +58,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IWindowController>();
|
||||
rb.PushIpcInterface<IWindowController>(system);
|
||||
}
|
||||
|
||||
void GetAudioController(Kernel::HLERequestContext& ctx) {
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system);
|
||||
}
|
||||
|
||||
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
|
||||
@@ -106,7 +106,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationFunctions>();
|
||||
rb.PushIpcInterface<IApplicationFunctions>(system);
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
@@ -154,7 +154,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISelfController>(nvflinger);
|
||||
rb.PushIpcInterface<ISelfController>(system, nvflinger);
|
||||
}
|
||||
|
||||
void GetWindowController(Kernel::HLERequestContext& ctx) {
|
||||
@@ -162,7 +162,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IWindowController>();
|
||||
rb.PushIpcInterface<IWindowController>(system);
|
||||
}
|
||||
|
||||
void GetAudioController(Kernel::HLERequestContext& ctx) {
|
||||
@@ -194,7 +194,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system);
|
||||
}
|
||||
|
||||
void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
@@ -64,7 +63,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IWindowController>();
|
||||
rb.PushIpcInterface<IWindowController>(system);
|
||||
}
|
||||
|
||||
void GetSelfController(Kernel::HLERequestContext& ctx) {
|
||||
@@ -72,7 +71,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISelfController>(nvflinger);
|
||||
rb.PushIpcInterface<ISelfController>(system, nvflinger);
|
||||
}
|
||||
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
@@ -88,7 +87,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
|
||||
rb.PushIpcInterface<ILibraryAppletCreator>(system);
|
||||
}
|
||||
|
||||
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
|
||||
@@ -96,7 +95,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationFunctions>();
|
||||
rb.PushIpcInterface<IApplicationFunctions>(system);
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service {
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
AppletDataBroker::AppletDataBroker() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) {
|
||||
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
@@ -121,7 +120,7 @@ Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent(
|
||||
return state_changed_event.readable;
|
||||
}
|
||||
|
||||
Applet::Applet() = default;
|
||||
Applet::Applet(Kernel::KernelCore& kernel_) : broker{kernel_} {}
|
||||
|
||||
Applet::~Applet() = default;
|
||||
|
||||
@@ -154,7 +153,7 @@ AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default;
|
||||
|
||||
AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default;
|
||||
|
||||
AppletManager::AppletManager() = default;
|
||||
AppletManager::AppletManager(Core::System& system_) : system{system_} {}
|
||||
|
||||
AppletManager::~AppletManager() = default;
|
||||
|
||||
@@ -216,28 +215,28 @@ void AppletManager::ClearAll() {
|
||||
frontend = {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, u64 current_process_title_id) const {
|
||||
std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
|
||||
switch (id) {
|
||||
case AppletId::Auth:
|
||||
return std::make_shared<Auth>(*frontend.parental_controls);
|
||||
return std::make_shared<Auth>(system, *frontend.parental_controls);
|
||||
case AppletId::Error:
|
||||
return std::make_shared<Error>(*frontend.error);
|
||||
return std::make_shared<Error>(system, *frontend.error);
|
||||
case AppletId::ProfileSelect:
|
||||
return std::make_shared<ProfileSelect>(*frontend.profile_select);
|
||||
return std::make_shared<ProfileSelect>(system, *frontend.profile_select);
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard);
|
||||
return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard);
|
||||
case AppletId::PhotoViewer:
|
||||
return std::make_shared<PhotoViewer>(*frontend.photo_viewer);
|
||||
return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer);
|
||||
case AppletId::LibAppletShop:
|
||||
return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id,
|
||||
return std::make_shared<WebBrowser>(system, *frontend.web_browser,
|
||||
frontend.e_commerce.get());
|
||||
case AppletId::LibAppletOff:
|
||||
return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id);
|
||||
return std::make_shared<WebBrowser>(system, *frontend.web_browser);
|
||||
default:
|
||||
UNIMPLEMENTED_MSG(
|
||||
"No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
|
||||
static_cast<u8>(id));
|
||||
return std::make_shared<StubApplet>(id);
|
||||
return std::make_shared<StubApplet>(system, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
class ECommerceApplet;
|
||||
class ErrorApplet;
|
||||
@@ -22,6 +26,10 @@ class SoftwareKeyboardApplet;
|
||||
class WebBrowserApplet;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class IStorage;
|
||||
@@ -53,7 +61,7 @@ enum class AppletId : u32 {
|
||||
|
||||
class AppletDataBroker final {
|
||||
public:
|
||||
AppletDataBroker();
|
||||
explicit AppletDataBroker(Kernel::KernelCore& kernel_);
|
||||
~AppletDataBroker();
|
||||
|
||||
struct RawChannelData {
|
||||
@@ -108,7 +116,7 @@ private:
|
||||
|
||||
class Applet {
|
||||
public:
|
||||
Applet();
|
||||
explicit Applet(Kernel::KernelCore& kernel_);
|
||||
virtual ~Applet();
|
||||
|
||||
virtual void Initialize();
|
||||
@@ -179,7 +187,7 @@ struct AppletFrontendSet {
|
||||
|
||||
class AppletManager {
|
||||
public:
|
||||
AppletManager();
|
||||
explicit AppletManager(Core::System& system_);
|
||||
~AppletManager();
|
||||
|
||||
void SetAppletFrontendSet(AppletFrontendSet set);
|
||||
@@ -187,10 +195,11 @@ public:
|
||||
void SetDefaultAppletsIfMissing();
|
||||
void ClearAll();
|
||||
|
||||
std::shared_ptr<Applet> GetApplet(AppletId id, u64 current_process_title_id) const;
|
||||
std::shared_ptr<Applet> GetApplet(AppletId id) const;
|
||||
|
||||
private:
|
||||
AppletFrontendSet frontend;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Applets
|
||||
|
||||
@@ -85,7 +85,8 @@ ResultCode Decode64BitError(u64 error) {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {}
|
||||
Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_)
|
||||
: Applet{system_.Kernel()}, frontend(frontend_), system{system_} {}
|
||||
|
||||
Error::~Error() = default;
|
||||
|
||||
@@ -145,8 +146,8 @@ void Error::Execute() {
|
||||
}
|
||||
|
||||
const auto callback = [this] { DisplayCompleted(); };
|
||||
const auto title_id = Core::CurrentProcess()->GetTitleID();
|
||||
const auto& reporter{Core::System::GetInstance().GetReporter()};
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
const auto& reporter{system.GetReporter()};
|
||||
|
||||
switch (mode) {
|
||||
case ErrorAppletMode::ShowError:
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class ErrorAppletMode : u8 {
|
||||
@@ -21,7 +25,7 @@ enum class ErrorAppletMode : u8 {
|
||||
|
||||
class Error final : public Applet {
|
||||
public:
|
||||
explicit Error(const Core::Frontend::ErrorApplet& frontend);
|
||||
explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_);
|
||||
~Error() override;
|
||||
|
||||
void Initialize() override;
|
||||
@@ -42,6 +46,7 @@ private:
|
||||
std::unique_ptr<ErrorArguments> args;
|
||||
|
||||
bool complete = false;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -37,7 +37,8 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix)
|
||||
}
|
||||
}
|
||||
|
||||
Auth::Auth(Core::Frontend::ParentalControlsApplet& frontend) : frontend(frontend) {}
|
||||
Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_)
|
||||
: Applet{system_.Kernel()}, frontend(frontend_) {}
|
||||
|
||||
Auth::~Auth() = default;
|
||||
|
||||
@@ -151,7 +152,8 @@ void Auth::AuthFinished(bool successful) {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
|
||||
PhotoViewer::PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_)
|
||||
: Applet{system_.Kernel()}, frontend(frontend_), system{system_} {}
|
||||
|
||||
PhotoViewer::~PhotoViewer() = default;
|
||||
|
||||
@@ -185,7 +187,7 @@ void PhotoViewer::Execute() {
|
||||
const auto callback = [this] { ViewFinished(); };
|
||||
switch (mode) {
|
||||
case PhotoViewerAppletMode::CurrentApp:
|
||||
frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback);
|
||||
frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback);
|
||||
break;
|
||||
case PhotoViewerAppletMode::AllApps:
|
||||
frontend.ShowAllPhotos(callback);
|
||||
@@ -200,7 +202,8 @@ void PhotoViewer::ViewFinished() {
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
StubApplet::StubApplet(AppletId id) : id(id) {}
|
||||
StubApplet::StubApplet(Core::System& system_, AppletId id_)
|
||||
: Applet{system_.Kernel()}, id(id_), system{system_} {}
|
||||
|
||||
StubApplet::~StubApplet() = default;
|
||||
|
||||
@@ -209,7 +212,7 @@ void StubApplet::Initialize() {
|
||||
Applet::Initialize();
|
||||
|
||||
const auto data = broker.PeekDataToAppletForDebug();
|
||||
Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport(
|
||||
system.GetReporter().SaveUnimplementedAppletReport(
|
||||
static_cast<u32>(id), common_args.arguments_version, common_args.library_version,
|
||||
common_args.theme_color, common_args.play_startup_sound, common_args.system_tick,
|
||||
data.normal, data.interactive);
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class AuthAppletType : u32 {
|
||||
@@ -16,7 +20,7 @@ enum class AuthAppletType : u32 {
|
||||
|
||||
class Auth final : public Applet {
|
||||
public:
|
||||
explicit Auth(Core::Frontend::ParentalControlsApplet& frontend);
|
||||
explicit Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_);
|
||||
~Auth() override;
|
||||
|
||||
void Initialize() override;
|
||||
@@ -45,7 +49,7 @@ enum class PhotoViewerAppletMode : u8 {
|
||||
|
||||
class PhotoViewer final : public Applet {
|
||||
public:
|
||||
explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend);
|
||||
explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_);
|
||||
~PhotoViewer() override;
|
||||
|
||||
void Initialize() override;
|
||||
@@ -60,11 +64,12 @@ private:
|
||||
const Core::Frontend::PhotoViewerApplet& frontend;
|
||||
bool complete = false;
|
||||
PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class StubApplet final : public Applet {
|
||||
public:
|
||||
explicit StubApplet(AppletId id);
|
||||
explicit StubApplet(Core::System& system_, AppletId id_);
|
||||
~StubApplet() override;
|
||||
|
||||
void Initialize() override;
|
||||
@@ -76,6 +81,7 @@ public:
|
||||
|
||||
private:
|
||||
AppletId id;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace Service::AM::Applets {
|
||||
|
||||
constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
|
||||
|
||||
ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend)
|
||||
: frontend(frontend) {}
|
||||
ProfileSelect::ProfileSelect(Core::System& system_,
|
||||
const Core::Frontend::ProfileSelectApplet& frontend_)
|
||||
: Applet{system_.Kernel()}, frontend(frontend_) {}
|
||||
|
||||
ProfileSelect::~ProfileSelect() = default;
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
struct UserSelectionConfig {
|
||||
@@ -29,7 +33,8 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco
|
||||
|
||||
class ProfileSelect final : public Applet {
|
||||
public:
|
||||
explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend);
|
||||
explicit ProfileSelect(Core::System& system_,
|
||||
const Core::Frontend::ProfileSelectApplet& frontend_);
|
||||
~ProfileSelect() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
@@ -39,8 +39,9 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
|
||||
return params;
|
||||
}
|
||||
|
||||
SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend)
|
||||
: frontend(frontend) {}
|
||||
SoftwareKeyboard::SoftwareKeyboard(Core::System& system_,
|
||||
const Core::Frontend::SoftwareKeyboardApplet& frontend_)
|
||||
: Applet{system_.Kernel()}, frontend(frontend_) {}
|
||||
|
||||
SoftwareKeyboard::~SoftwareKeyboard() = default;
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class KeysetDisable : u32 {
|
||||
@@ -55,7 +59,8 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz
|
||||
|
||||
class SoftwareKeyboard final : public Applet {
|
||||
public:
|
||||
explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend);
|
||||
explicit SoftwareKeyboard(Core::System& system_,
|
||||
const Core::Frontend::SoftwareKeyboardApplet& frontend_);
|
||||
~SoftwareKeyboard() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
@@ -190,8 +190,9 @@ std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>&
|
||||
return out;
|
||||
}
|
||||
|
||||
FileSys::VirtualFile GetApplicationRomFS(u64 title_id, FileSys::ContentRecordType type) {
|
||||
const auto& installed{Core::System::GetInstance().GetContentProvider()};
|
||||
FileSys::VirtualFile GetApplicationRomFS(const Core::System& system, u64 title_id,
|
||||
FileSys::ContentRecordType type) {
|
||||
const auto& installed{system.GetContentProvider()};
|
||||
const auto res = installed.GetEntry(title_id, type);
|
||||
|
||||
if (res != nullptr) {
|
||||
@@ -207,10 +208,10 @@ FileSys::VirtualFile GetApplicationRomFS(u64 title_id, FileSys::ContentRecordTyp
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
|
||||
Core::Frontend::ECommerceApplet* frontend_e_commerce)
|
||||
: frontend(frontend), frontend_e_commerce(frontend_e_commerce),
|
||||
current_process_title_id(current_process_title_id) {}
|
||||
WebBrowser::WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_,
|
||||
Core::Frontend::ECommerceApplet* frontend_e_commerce_)
|
||||
: Applet{system_.Kernel()}, frontend(frontend_),
|
||||
frontend_e_commerce(frontend_e_commerce_), system{system_} {}
|
||||
|
||||
WebBrowser::~WebBrowser() = default;
|
||||
|
||||
@@ -266,7 +267,7 @@ void WebBrowser::UnpackRomFS() {
|
||||
ASSERT(offline_romfs != nullptr);
|
||||
const auto dir =
|
||||
FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
|
||||
const auto& vfs{Core::System::GetInstance().GetFilesystem()};
|
||||
const auto& vfs{system.GetFilesystem()};
|
||||
const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
|
||||
FileSys::VfsRawCopyD(dir, temp_dir);
|
||||
|
||||
@@ -470,10 +471,10 @@ void WebBrowser::InitializeOffline() {
|
||||
}
|
||||
|
||||
if (title_id == 0) {
|
||||
title_id = current_process_title_id;
|
||||
title_id = system.CurrentProcess()->GetTitleID();
|
||||
}
|
||||
|
||||
offline_romfs = GetApplicationRomFS(title_id, type);
|
||||
offline_romfs = GetApplicationRomFS(system, title_id, type);
|
||||
if (offline_romfs == nullptr) {
|
||||
status = ResultCode(-1);
|
||||
LOG_ERROR(Service_AM, "Failed to find offline data for request!");
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class ShimKind : u32;
|
||||
@@ -17,8 +21,8 @@ enum class WebArgTLVType : u16;
|
||||
|
||||
class WebBrowser final : public Applet {
|
||||
public:
|
||||
WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
|
||||
Core::Frontend::ECommerceApplet* frontend_e_commerce = nullptr);
|
||||
WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_,
|
||||
Core::Frontend::ECommerceApplet* frontend_e_commerce_ = nullptr);
|
||||
|
||||
~WebBrowser() override;
|
||||
|
||||
@@ -59,8 +63,6 @@ private:
|
||||
bool unpacked = false;
|
||||
ResultCode status = RESULT_SUCCESS;
|
||||
|
||||
u64 current_process_title_id;
|
||||
|
||||
ShimKind kind;
|
||||
std::map<WebArgTLVType, std::vector<u8>> args;
|
||||
|
||||
@@ -74,6 +76,8 @@ private:
|
||||
std::optional<u128> user_id;
|
||||
std::optional<bool> shop_full_display;
|
||||
std::string shop_extra_parameter;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
@@ -25,14 +26,10 @@
|
||||
#include "core/hle/service/filesystem/fsp_pr.h"
|
||||
#include "core/hle/service/filesystem/fsp_srv.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
// Size of emulated sd card free space, reported in bytes.
|
||||
// Just using 32GB because thats reasonable
|
||||
// TODO(DarkLordZach): Eventually make this configurable in settings.
|
||||
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
||||
|
||||
// A default size for normal/journal save data size if application control metadata cannot be found.
|
||||
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
|
||||
constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
|
||||
@@ -226,13 +223,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
|
||||
return MakeResult(dir);
|
||||
}
|
||||
|
||||
u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
|
||||
if (backing->IsWritable())
|
||||
return EMULATED_SD_REPORTED_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
@@ -251,44 +241,39 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of registered file systems, identified by type. Once an file system is registered here, it
|
||||
* is never removed until UnregisterFileSystems is called.
|
||||
*/
|
||||
static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
|
||||
static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
|
||||
static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
|
||||
static std::unique_ptr<FileSys::BISFactory> bis_factory;
|
||||
FileSystemController::FileSystemController() = default;
|
||||
|
||||
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
|
||||
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
|
||||
FileSystemController::~FileSystemController() = default;
|
||||
|
||||
ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
|
||||
romfs_factory = std::move(factory);
|
||||
LOG_DEBUG(Service_FS, "Registered RomFS");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
|
||||
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data");
|
||||
ResultCode FileSystemController::RegisterSaveData(
|
||||
std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
|
||||
ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
|
||||
save_data_factory = std::move(factory);
|
||||
LOG_DEBUG(Service_FS, "Registered save data");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
|
||||
ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
|
||||
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
|
||||
sdmc_factory = std::move(factory);
|
||||
LOG_DEBUG(Service_FS, "Registered SDMC");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
|
||||
ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
|
||||
ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
|
||||
bis_factory = std::move(factory);
|
||||
LOG_DEBUG(Service_FS, "Registered BIS");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void SetPackedUpdate(FileSys::VirtualFile update_raw) {
|
||||
void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
|
||||
LOG_TRACE(Service_FS, "Setting packed update for romfs");
|
||||
|
||||
if (romfs_factory == nullptr)
|
||||
@@ -297,7 +282,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) {
|
||||
romfs_factory->SetPackedUpdate(std::move(update_raw));
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
|
||||
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const {
|
||||
LOG_TRACE(Service_FS, "Opening RomFS for current process");
|
||||
|
||||
if (romfs_factory == nullptr) {
|
||||
@@ -308,8 +293,8 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
|
||||
return romfs_factory->OpenCurrentProcess();
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type) {
|
||||
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
|
||||
u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
|
||||
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
|
||||
title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
|
||||
|
||||
@@ -321,8 +306,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
|
||||
return romfs_factory->Open(title_id, storage_id, type);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
const FileSys::SaveDataDescriptor& descriptor) {
|
||||
ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
|
||||
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
|
||||
static_cast<u8>(space), save_struct.DebugInfo());
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return save_data_factory->Create(space, save_struct);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
|
||||
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
|
||||
static_cast<u8>(space), descriptor.DebugInfo());
|
||||
|
||||
@@ -333,7 +330,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
return save_data_factory->Open(space, descriptor);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
|
||||
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
|
||||
FileSys::SaveDataSpaceId space) const {
|
||||
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
@@ -343,7 +341,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
|
||||
return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC");
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
@@ -353,7 +351,92 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
return sdmc_factory->Open();
|
||||
}
|
||||
|
||||
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
|
||||
ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
|
||||
FileSys::BisPartitionId id) const {
|
||||
LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id));
|
||||
|
||||
if (bis_factory == nullptr) {
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
auto part = bis_factory->OpenPartition(id);
|
||||
if (part == nullptr) {
|
||||
return FileSys::ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return MakeResult<FileSys::VirtualDir>(std::move(part));
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
|
||||
FileSys::BisPartitionId id) const {
|
||||
LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id));
|
||||
|
||||
if (bis_factory == nullptr) {
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
auto part = bis_factory->OpenPartitionStorage(id);
|
||||
if (part == nullptr) {
|
||||
return FileSys::ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return MakeResult<FileSys::VirtualFile>(std::move(part));
|
||||
}
|
||||
|
||||
u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
|
||||
switch (id) {
|
||||
case FileSys::StorageId::None:
|
||||
case FileSys::StorageId::GameCard:
|
||||
return 0;
|
||||
case FileSys::StorageId::SdCard:
|
||||
if (sdmc_factory == nullptr)
|
||||
return 0;
|
||||
return sdmc_factory->GetSDMCFreeSpace();
|
||||
case FileSys::StorageId::Host:
|
||||
if (bis_factory == nullptr)
|
||||
return 0;
|
||||
return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace();
|
||||
case FileSys::StorageId::NandSystem:
|
||||
if (bis_factory == nullptr)
|
||||
return 0;
|
||||
return bis_factory->GetSystemNANDFreeSpace();
|
||||
case FileSys::StorageId::NandUser:
|
||||
if (bis_factory == nullptr)
|
||||
return 0;
|
||||
return bis_factory->GetUserNANDFreeSpace();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
|
||||
switch (id) {
|
||||
case FileSys::StorageId::None:
|
||||
case FileSys::StorageId::GameCard:
|
||||
return 0;
|
||||
case FileSys::StorageId::SdCard:
|
||||
if (sdmc_factory == nullptr)
|
||||
return 0;
|
||||
return sdmc_factory->GetSDMCTotalSpace();
|
||||
case FileSys::StorageId::Host:
|
||||
if (bis_factory == nullptr)
|
||||
return 0;
|
||||
return bis_factory->GetFullNANDTotalSpace();
|
||||
case FileSys::StorageId::NandSystem:
|
||||
if (bis_factory == nullptr)
|
||||
return 0;
|
||||
return bis_factory->GetSystemNANDTotalSpace();
|
||||
case FileSys::StorageId::NandUser:
|
||||
if (bis_factory == nullptr)
|
||||
return 0;
|
||||
return bis_factory->GetUserNANDTotalSpace();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
|
||||
u64 title_id, u128 user_id) const {
|
||||
if (save_data_factory == nullptr) {
|
||||
return {0, 0};
|
||||
}
|
||||
@@ -385,13 +468,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
|
||||
return value;
|
||||
}
|
||||
|
||||
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||
FileSys::SaveDataSize new_value) {
|
||||
void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||
FileSys::SaveDataSize new_value) const {
|
||||
if (save_data_factory != nullptr)
|
||||
save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents() {
|
||||
void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
|
||||
gamecard = std::make_unique<FileSys::XCI>(file);
|
||||
const auto dir = gamecard->ConcatenatedPseudoDirectory();
|
||||
gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir);
|
||||
gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
|
||||
}
|
||||
|
||||
FileSys::XCI* FileSystemController::GetGameCard() const {
|
||||
return gamecard.get();
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const {
|
||||
return gamecard_registered.get();
|
||||
}
|
||||
|
||||
FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const {
|
||||
return gamecard_placeholder.get();
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const {
|
||||
LOG_TRACE(Service_FS, "Opening System NAND Contents");
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
@@ -400,7 +502,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() {
|
||||
return bis_factory->GetSystemNANDContents();
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* GetUserNANDContents() {
|
||||
FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const {
|
||||
LOG_TRACE(Service_FS, "Opening User NAND Contents");
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
@@ -409,7 +511,7 @@ FileSys::RegisteredCache* GetUserNANDContents() {
|
||||
return bis_factory->GetUserNANDContents();
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* GetSDMCContents() {
|
||||
FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC Contents");
|
||||
|
||||
if (sdmc_factory == nullptr)
|
||||
@@ -418,7 +520,143 @@ FileSys::RegisteredCache* GetSDMCContents() {
|
||||
return sdmc_factory->GetSDMCContents();
|
||||
}
|
||||
|
||||
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
|
||||
FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
|
||||
LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetSystemNANDPlaceholder();
|
||||
}
|
||||
|
||||
FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const {
|
||||
LOG_TRACE(Service_FS, "Opening User NAND Placeholder");
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetUserNANDPlaceholder();
|
||||
}
|
||||
|
||||
FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC Placeholder");
|
||||
|
||||
if (sdmc_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return sdmc_factory->GetSDMCPlaceholder();
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
|
||||
FileSys::StorageId id) const {
|
||||
switch (id) {
|
||||
case FileSys::StorageId::None:
|
||||
case FileSys::StorageId::Host:
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
case FileSys::StorageId::GameCard:
|
||||
return GetGameCardContents();
|
||||
case FileSys::StorageId::NandSystem:
|
||||
return GetSystemNANDContents();
|
||||
case FileSys::StorageId::NandUser:
|
||||
return GetUserNANDContents();
|
||||
case FileSys::StorageId::SdCard:
|
||||
return GetSDMCContents();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
|
||||
FileSys::StorageId id) const {
|
||||
switch (id) {
|
||||
case FileSys::StorageId::None:
|
||||
case FileSys::StorageId::Host:
|
||||
UNIMPLEMENTED();
|
||||
return nullptr;
|
||||
case FileSys::StorageId::GameCard:
|
||||
return GetGameCardPlaceholder();
|
||||
case FileSys::StorageId::NandSystem:
|
||||
return GetSystemNANDPlaceholder();
|
||||
case FileSys::StorageId::NandUser:
|
||||
return GetUserNANDPlaceholder();
|
||||
case FileSys::StorageId::SdCard:
|
||||
return GetSDMCPlaceholder();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const {
|
||||
LOG_TRACE(Service_FS, "Opening system NAND content directory");
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetSystemNANDContentDirectory();
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const {
|
||||
LOG_TRACE(Service_FS, "Opening user NAND content directory");
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetUserNANDContentDirectory();
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC content directory");
|
||||
|
||||
if (sdmc_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return sdmc_factory->GetSDMCContentDirectory();
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const {
|
||||
LOG_TRACE(Service_FS, "Opening NAND image directory");
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetImageDirectory();
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC image directory");
|
||||
|
||||
if (sdmc_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return sdmc_factory->GetImageDirectory();
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const {
|
||||
switch (id) {
|
||||
case ContentStorageId::System:
|
||||
return GetSystemNANDContentDirectory();
|
||||
case ContentStorageId::User:
|
||||
return GetUserNANDContentDirectory();
|
||||
case ContentStorageId::SdCard:
|
||||
return GetSDMCContentDirectory();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const {
|
||||
switch (id) {
|
||||
case ImageDirectoryId::NAND:
|
||||
return GetNANDImageDirectory();
|
||||
case ImageDirectoryId::SdCard:
|
||||
return GetSDMCImageDirectory();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const {
|
||||
LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
@@ -427,7 +665,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
|
||||
return bis_factory->GetModificationLoadRoot(title_id);
|
||||
}
|
||||
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
|
||||
FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
|
||||
LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
@@ -436,7 +674,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
|
||||
return bis_factory->GetModificationDumpRoot(title_id);
|
||||
}
|
||||
|
||||
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
if (overwrite) {
|
||||
bis_factory = nullptr;
|
||||
save_data_factory = nullptr;
|
||||
@@ -473,11 +711,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
}
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
romfs_factory = nullptr;
|
||||
CreateFactories(*system.GetFilesystem(), false);
|
||||
std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
|
||||
->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -14,10 +14,13 @@ namespace FileSys {
|
||||
class BISFactory;
|
||||
class RegisteredCache;
|
||||
class RegisteredCacheUnion;
|
||||
class PlaceholderCache;
|
||||
class RomFSFactory;
|
||||
class SaveDataFactory;
|
||||
class SDMCFactory;
|
||||
class XCI;
|
||||
|
||||
enum class BisPartitionId : u32;
|
||||
enum class ContentRecordType : u8;
|
||||
enum class Mode : u32;
|
||||
enum class SaveDataSpaceId : u8;
|
||||
@@ -36,34 +39,91 @@ class ServiceManager;
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
|
||||
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
|
||||
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
|
||||
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
|
||||
enum class ContentStorageId : u32 {
|
||||
System,
|
||||
User,
|
||||
SdCard,
|
||||
};
|
||||
|
||||
void SetPackedUpdate(FileSys::VirtualFile update_raw);
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
const FileSys::SaveDataDescriptor& descriptor);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
enum class ImageDirectoryId : u32 {
|
||||
NAND,
|
||||
SdCard,
|
||||
};
|
||||
|
||||
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
|
||||
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||
FileSys::SaveDataSize new_value);
|
||||
class FileSystemController {
|
||||
public:
|
||||
FileSystemController();
|
||||
~FileSystemController();
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents();
|
||||
FileSys::RegisteredCache* GetUserNANDContents();
|
||||
FileSys::RegisteredCache* GetSDMCContents();
|
||||
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
|
||||
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
|
||||
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
|
||||
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
|
||||
|
||||
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
|
||||
void SetPackedUpdate(FileSys::VirtualFile update_raw);
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type) const;
|
||||
ResultVal<FileSys::VirtualDir> CreateSaveData(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(
|
||||
FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC() const;
|
||||
ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
|
||||
ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const;
|
||||
|
||||
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
|
||||
// above is called.
|
||||
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
|
||||
u64 GetFreeSpaceSize(FileSys::StorageId id) const;
|
||||
u64 GetTotalSpaceSize(FileSys::StorageId id) const;
|
||||
|
||||
FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
|
||||
u128 user_id) const;
|
||||
void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
|
||||
FileSys::SaveDataSize new_value) const;
|
||||
|
||||
void SetGameCard(FileSys::VirtualFile file);
|
||||
FileSys::XCI* GetGameCard() const;
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents() const;
|
||||
FileSys::RegisteredCache* GetUserNANDContents() const;
|
||||
FileSys::RegisteredCache* GetSDMCContents() const;
|
||||
FileSys::RegisteredCache* GetGameCardContents() const;
|
||||
|
||||
FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const;
|
||||
FileSys::PlaceholderCache* GetUserNANDPlaceholder() const;
|
||||
FileSys::PlaceholderCache* GetSDMCPlaceholder() const;
|
||||
FileSys::PlaceholderCache* GetGameCardPlaceholder() const;
|
||||
|
||||
FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const;
|
||||
FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const;
|
||||
|
||||
FileSys::VirtualDir GetSystemNANDContentDirectory() const;
|
||||
FileSys::VirtualDir GetUserNANDContentDirectory() const;
|
||||
FileSys::VirtualDir GetSDMCContentDirectory() const;
|
||||
|
||||
FileSys::VirtualDir GetNANDImageDirectory() const;
|
||||
FileSys::VirtualDir GetSDMCImageDirectory() const;
|
||||
|
||||
FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
|
||||
FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;
|
||||
|
||||
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
|
||||
|
||||
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
|
||||
// above is called.
|
||||
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
|
||||
std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
|
||||
std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
|
||||
std::unique_ptr<FileSys::BISFactory> bis_factory;
|
||||
|
||||
std::unique_ptr<FileSys::XCI> gamecard;
|
||||
std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
|
||||
std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
|
||||
};
|
||||
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
@@ -159,12 +219,6 @@ public:
|
||||
*/
|
||||
ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
|
||||
|
||||
/**
|
||||
* Get the free space
|
||||
* @return The number of free bytes in the archive
|
||||
*/
|
||||
u64 GetFreeSpaceSize() const;
|
||||
|
||||
/**
|
||||
* Get the type of the specified path
|
||||
* @return The type of the specified path or error code
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/system_archive/system_archive.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
@@ -30,6 +31,18 @@
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
struct SizeGetter {
|
||||
std::function<u64()> get_free_size;
|
||||
std::function<u64()> get_total_size;
|
||||
|
||||
static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
|
||||
return {
|
||||
[&fsc, id] { return fsc.GetFreeSpaceSize(id); },
|
||||
[&fsc, id] { return fsc.GetTotalSpaceSize(id); },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
enum class FileSystemType : u8 {
|
||||
Invalid0 = 0,
|
||||
Invalid1 = 1,
|
||||
@@ -289,8 +302,8 @@ private:
|
||||
|
||||
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
||||
public:
|
||||
explicit IFileSystem(FileSys::VirtualDir backend)
|
||||
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
|
||||
explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size)
|
||||
: ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
||||
{1, &IFileSystem::DeleteFile, "DeleteFile"},
|
||||
@@ -467,14 +480,31 @@ public:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(size.get_free_size());
|
||||
}
|
||||
|
||||
void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(size.get_total_size());
|
||||
}
|
||||
|
||||
private:
|
||||
VfsDirectoryServiceWrapper backend;
|
||||
SizeGetter size;
|
||||
};
|
||||
|
||||
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
|
||||
public:
|
||||
explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
|
||||
: ServiceFramework("ISaveDataInfoReader") {
|
||||
explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc)
|
||||
: ServiceFramework("ISaveDataInfoReader"), fsc(fsc) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
|
||||
};
|
||||
@@ -520,8 +550,13 @@ private:
|
||||
}
|
||||
|
||||
void FindAllSaves(FileSys::SaveDataSpaceId space) {
|
||||
const auto save_root = OpenSaveDataSpace(space);
|
||||
ASSERT(save_root.Succeeded());
|
||||
const auto save_root = fsc.OpenSaveDataSpace(space);
|
||||
|
||||
if (save_root.Failed() || *save_root == nullptr) {
|
||||
LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!",
|
||||
static_cast<u8>(space));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& type : (*save_root)->GetSubdirectories()) {
|
||||
if (type->GetName() == "save") {
|
||||
@@ -610,11 +645,13 @@ private:
|
||||
};
|
||||
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
|
||||
|
||||
FileSystemController& fsc;
|
||||
std::vector<SaveDataInfo> info;
|
||||
u64 next_entry_index = 0;
|
||||
};
|
||||
|
||||
FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) {
|
||||
FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
|
||||
: ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "OpenFileSystem"},
|
||||
@@ -754,7 +791,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IFileSystem filesystem(OpenSDMC().Unwrap());
|
||||
IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
|
||||
SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -768,8 +806,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
|
||||
u128 uid = rp.PopRaw<u128>();
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}",
|
||||
save_struct.DebugInfo(), uid[1], uid[0]);
|
||||
LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
|
||||
uid[1], uid[0]);
|
||||
|
||||
fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -786,14 +826,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
|
||||
auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
|
||||
if (dir.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
IFileSystem filesystem(std::move(dir.Unwrap()));
|
||||
FileSys::StorageId id;
|
||||
if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
|
||||
id = FileSys::StorageId::NandUser;
|
||||
} else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
|
||||
parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
|
||||
id = FileSys::StorageId::SdCard;
|
||||
} else {
|
||||
id = FileSys::StorageId::NandSystem;
|
||||
}
|
||||
|
||||
IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -812,7 +862,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
|
||||
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
|
||||
}
|
||||
|
||||
void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -836,7 +886,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
auto romfs = OpenRomFSCurrentProcess();
|
||||
auto romfs = fsc.OpenRomFSCurrentProcess();
|
||||
if (romfs.Failed()) {
|
||||
// TODO (bunnei): Find the right error code to use here
|
||||
LOG_CRITICAL(Service_FS, "no file system interface available!");
|
||||
@@ -861,7 +911,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
|
||||
static_cast<u8>(storage_id), unknown, title_id);
|
||||
|
||||
auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
|
||||
auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
|
||||
|
||||
if (data.Failed()) {
|
||||
const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
|
||||
|
||||
@@ -32,7 +32,7 @@ enum class LogMode : u32 {
|
||||
|
||||
class FSP_SRV final : public ServiceFramework<FSP_SRV> {
|
||||
public:
|
||||
explicit FSP_SRV(const Core::Reporter& reporter);
|
||||
explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
|
||||
~FSP_SRV() override;
|
||||
|
||||
private:
|
||||
@@ -51,6 +51,8 @@ private:
|
||||
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
|
||||
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSystemController& fsc;
|
||||
|
||||
FileSys::VirtualFile romfs;
|
||||
u64 current_process_id = 0;
|
||||
u32 access_log_program_index = 0;
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/ldr/ldr.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -16,6 +19,11 @@
|
||||
|
||||
namespace Service::LDR {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr ResultCode ERROR_ALREADY_REGISTERED{ErrorModule::Loader, 7};
|
||||
constexpr ResultCode ERROR_TITLE_NOT_FOUND{ErrorModule::Loader, 8};
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
|
||||
constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
|
||||
constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
|
||||
@@ -31,35 +39,241 @@ constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
|
||||
|
||||
constexpr u64 MAXIMUM_LOADED_RO = 0x40;
|
||||
|
||||
constexpr u64 NSO_BUILD_ID_OFFSET = 0x40;
|
||||
|
||||
enum class ApplicationType : u8 {
|
||||
Sysmodule = 0,
|
||||
Application = 1,
|
||||
Applet = 2,
|
||||
};
|
||||
|
||||
struct NSOInfo {
|
||||
std::array<u8, 0x20> build_id;
|
||||
VAddr map_address;
|
||||
u64 map_size;
|
||||
};
|
||||
static_assert(sizeof(NSOInfo) == 0x30, "NSOInfo has incorrect size.");
|
||||
|
||||
struct ProgramInfoHeader {
|
||||
u8 main_thread_priority;
|
||||
u8 default_cpu_id;
|
||||
ApplicationType application_type;
|
||||
INSERT_PADDING_BYTES(0x1);
|
||||
u32_le main_thread_stack_size;
|
||||
u64_le title_id;
|
||||
u32_le acid_sac_size;
|
||||
u32_le aci0_sac_size;
|
||||
u32_le acid_fac_size;
|
||||
u32_le aci0_fac_size;
|
||||
};
|
||||
static_assert(sizeof(ProgramInfoHeader) == 0x20, "ProgramInfoHeader has incorrect size.");
|
||||
|
||||
std::vector<NSOInfo> GetNSOInfo(const std::map<VAddr, std::shared_ptr<std::vector<u8>>>& modules) {
|
||||
std::vector<NSOInfo> out;
|
||||
|
||||
for (const auto& [base_addr, memory] : modules) {
|
||||
out.emplace_back();
|
||||
out.back().map_address = base_addr;
|
||||
out.back().map_size = memory->size();
|
||||
std::memcpy(out.back().build_id.data(), memory->data() + NSO_BUILD_ID_OFFSET,
|
||||
out.back().build_id.size());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<u8> GetProgramInfoFromProcess(const Kernel::Process& process) {
|
||||
const auto& meta = process.GetProgramMetadata();
|
||||
|
||||
ProgramInfoHeader header{};
|
||||
header.main_thread_priority = meta.GetMainThreadPriority();
|
||||
header.default_cpu_id = meta.GetMainThreadCore();
|
||||
header.application_type = ApplicationType::Application;
|
||||
header.main_thread_stack_size = meta.GetMainThreadStackSize();
|
||||
header.title_id = meta.GetTitleID();
|
||||
|
||||
const auto acid_file_access = meta.GetACIDFileAccessControl();
|
||||
const auto aci0_file_access = meta.GetACI0FileAccessControl();
|
||||
const auto acid_service_access = meta.GetACIDServiceAccessControl();
|
||||
const auto aci0_service_access = meta.GetACI0ServiceAccessControl();
|
||||
|
||||
header.acid_fac_size = acid_file_access.size();
|
||||
header.aci0_fac_size = aci0_file_access.size();
|
||||
header.acid_sac_size = acid_service_access.size();
|
||||
header.aci0_sac_size = aci0_service_access.size();
|
||||
|
||||
std::vector<u8> out(sizeof(ProgramInfoHeader) + acid_file_access.size() +
|
||||
aci0_file_access.size() + acid_service_access.size() +
|
||||
aci0_service_access.size());
|
||||
|
||||
u64 offset = 0;
|
||||
std::memcpy(out.data() + offset, &header, sizeof(ProgramInfoHeader));
|
||||
offset += sizeof(ProgramInfoHeader);
|
||||
std::memcpy(out.data() + offset, acid_service_access.data(), acid_service_access.size());
|
||||
offset += acid_service_access.size();
|
||||
std::memcpy(out.data() + offset, aci0_service_access.data(), aci0_service_access.size());
|
||||
offset += aci0_service_access.size();
|
||||
std::memcpy(out.data() + offset, acid_file_access.data(), acid_file_access.size());
|
||||
offset += acid_file_access.size();
|
||||
std::memcpy(out.data() + offset, aci0_file_access.data(), aci0_file_access.size());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
|
||||
public:
|
||||
explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
|
||||
explicit DebugMonitor(const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list)
|
||||
: ServiceFramework{"ldr:dmnt"}, process_list(process_list) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "AddProcessToDebugLaunchQueue"},
|
||||
{1, nullptr, "ClearDebugLaunchQueue"},
|
||||
{2, nullptr, "GetNsoInfos"},
|
||||
{2, &DebugMonitor::GetNsoInfos, "GetNsoInfos"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetNsoInfos(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
auto write_size = ctx.GetWriteBufferSize() / sizeof(NSOInfo);
|
||||
|
||||
LOG_DEBUG(Service_LDR, "called, process_id={:016X}, write_size={:08X}", process_id,
|
||||
write_size);
|
||||
|
||||
const auto iter = std::find_if(
|
||||
process_list.begin(), process_list.end(),
|
||||
[process_id](const auto& process) { return process->GetProcessID() == process_id; });
|
||||
|
||||
if (iter == process_list.end()) {
|
||||
LOG_ERROR(Service_LDR, "Cannot find process with given process ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_TITLE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& modules = (*iter)->GetModules();
|
||||
const auto nso_infos = GetNSOInfo(modules);
|
||||
|
||||
write_size = std::min<std::size_t>(write_size, nso_infos.size());
|
||||
|
||||
ctx.WriteBuffer(nso_infos.data(), write_size * sizeof(NSOInfo));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u32>(write_size));
|
||||
}
|
||||
|
||||
const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list;
|
||||
};
|
||||
|
||||
class ProcessManager final : public ServiceFramework<ProcessManager> {
|
||||
public:
|
||||
explicit ProcessManager() : ServiceFramework{"ldr:pm"} {
|
||||
explicit ProcessManager(const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list)
|
||||
: ServiceFramework{"ldr:pm"}, process_list(process_list) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateProcess"},
|
||||
{1, nullptr, "GetProgramInfo"},
|
||||
{2, nullptr, "RegisterTitle"},
|
||||
{3, nullptr, "UnregisterTitle"},
|
||||
{1, &ProcessManager::GetProgramInfo, "GetProgramInfo"},
|
||||
{2, &ProcessManager::RegisterTitle, "RegisterTitle"},
|
||||
{3, &ProcessManager::UnregisterTitle, "UnregisterTitle"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetProgramInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
|
||||
|
||||
LOG_DEBUG(Service_LDR, "called, title_id={:016X}, storage_id={:02X}", title_id,
|
||||
static_cast<u8>(storage_id));
|
||||
|
||||
const auto iter =
|
||||
std::find_if(process_list.begin(), process_list.end(), [title_id](const auto& process) {
|
||||
return process->GetTitleID() == title_id;
|
||||
});
|
||||
|
||||
if (iter == process_list.end()) {
|
||||
LOG_ERROR(Service_LDR, "Cannot find process with given title ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_TITLE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto info = GetProgramInfoFromProcess(**iter);
|
||||
|
||||
ctx.WriteBuffer(info);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void RegisterTitle(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
|
||||
|
||||
LOG_DEBUG(Service_LDR, "called, title_id={:016X}, storage_id={:02X}", title_id,
|
||||
static_cast<u8>(storage_id));
|
||||
|
||||
const auto iter =
|
||||
std::find_if(registry.begin(), registry.end(),
|
||||
[title_id](const auto& kv) { return kv.second.title_id == title_id; });
|
||||
|
||||
if (iter != registry.end()) {
|
||||
LOG_ERROR(Service_LDR, "Cannot register title with identical ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_ALREADY_REGISTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = next_register_id;
|
||||
registry.insert_or_assign(next_register_id++, TitleRegistration{title_id, storage_id});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u64>(index);
|
||||
}
|
||||
|
||||
void UnregisterTitle(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto register_id = rp.PopRaw<u64>();
|
||||
|
||||
LOG_DEBUG(Service_LDR, "called, register_id={:08X}", register_id);
|
||||
|
||||
const auto iter = registry.find(register_id);
|
||||
|
||||
if (iter == registry.end()) {
|
||||
LOG_ERROR(Service_LDR, "Cannot unregister nonexistant ID!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_TITLE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
registry.erase(iter);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
struct TitleRegistration {
|
||||
u64 title_id;
|
||||
FileSys::StorageId storage_id;
|
||||
};
|
||||
u64 next_register_id = 1;
|
||||
std::map<u64, TitleRegistration> registry;
|
||||
|
||||
const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list;
|
||||
};
|
||||
|
||||
class Shell final : public ServiceFramework<Shell> {
|
||||
@@ -78,7 +292,7 @@ public:
|
||||
|
||||
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
|
||||
public:
|
||||
explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
|
||||
explicit RelocatableObject(const char* name) : ServiceFramework{name} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &RelocatableObject::LoadNro, "LoadNro"},
|
||||
@@ -518,11 +732,14 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<DebugMonitor>()->InstallAsService(sm);
|
||||
std::make_shared<ProcessManager>()->InstallAsService(sm);
|
||||
std::make_shared<Shell>()->InstallAsService(sm);
|
||||
std::make_shared<RelocatableObject>()->InstallAsService(sm);
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
std::make_shared<DebugMonitor>(system.Kernel().GetProcessList())
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ProcessManager>(system.Kernel().GetProcessList())
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<Shell>()->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<RelocatableObject>("ldr:ro")->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<RelocatableObject>("ro:1")->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::LDR
|
||||
|
||||
@@ -11,6 +11,6 @@ class ServiceManager;
|
||||
namespace Service::LDR {
|
||||
|
||||
/// Registers all LDR services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& sm);
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::LDR
|
||||
|
||||
@@ -617,7 +617,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc) {
|
||||
std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
|
||||
std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
|
||||
std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
|
||||
@@ -628,7 +628,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<NS_SU>()->InstallAsService(service_manager);
|
||||
std::make_shared<NS_VM>()->InstallAsService(service_manager);
|
||||
|
||||
std::make_shared<PL_U>()->InstallAsService(service_manager);
|
||||
std::make_shared<PL_U>(fsc)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
|
||||
@@ -6,7 +6,13 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NS {
|
||||
namespace Service {
|
||||
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
} // namespace FileSystem
|
||||
|
||||
namespace NS {
|
||||
|
||||
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
|
||||
public:
|
||||
@@ -91,6 +97,8 @@ private:
|
||||
};
|
||||
|
||||
/// Registers all NS services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc);
|
||||
|
||||
} // namespace Service::NS
|
||||
} // namespace NS
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -150,7 +150,8 @@ struct PL_U::Impl {
|
||||
std::vector<FontRegion> shared_font_regions;
|
||||
};
|
||||
|
||||
PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
|
||||
PL_U::PL_U(FileSystem::FileSystemController& fsc)
|
||||
: ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &PL_U::RequestLoad, "RequestLoad"},
|
||||
{1, &PL_U::GetLoadState, "GetLoadState"},
|
||||
@@ -161,7 +162,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
// Attempt to load shared font data from disk
|
||||
const auto* nand = FileSystem::GetSystemNANDContents();
|
||||
const auto* nand = fsc.GetSystemNANDContents();
|
||||
std::size_t offset = 0;
|
||||
// Rebuild shared fonts from data ncas
|
||||
if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
|
||||
|
||||
@@ -7,11 +7,17 @@
|
||||
#include <memory>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NS {
|
||||
namespace Service {
|
||||
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
} // namespace FileSystem
|
||||
|
||||
namespace NS {
|
||||
|
||||
class PL_U final : public ServiceFramework<PL_U> {
|
||||
public:
|
||||
PL_U();
|
||||
PL_U(FileSystem::FileSystemController& fsc);
|
||||
~PL_U() override;
|
||||
|
||||
private:
|
||||
@@ -26,4 +32,6 @@ private:
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::NS
|
||||
} // namespace NS
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -146,8 +146,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
}
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
params.address, params.num_entries, params.flags.raw);
|
||||
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
|
||||
params.num_entries, params.flags.raw);
|
||||
|
||||
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader),
|
||||
@@ -179,8 +179,8 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
|
||||
}
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
params.address, params.num_entries, params.flags.raw);
|
||||
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
|
||||
params.num_entries, params.flags.raw);
|
||||
|
||||
Tegra::CommandList entries(params.num_entries);
|
||||
Memory::ReadBlock(params.address, entries.data(),
|
||||
|
||||
@@ -2,34 +2,31 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <json.hpp>
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/prepo/prepo.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/reporter.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::PlayReport {
|
||||
|
||||
class PlayReport final : public ServiceFramework<PlayReport> {
|
||||
public:
|
||||
explicit PlayReport(const char* name) : ServiceFramework{name} {
|
||||
explicit PlayReport(Core::System& system, const char* name)
|
||||
: ServiceFramework{name}, system(system) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10100, nullptr, "SaveReportOld"},
|
||||
{10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"},
|
||||
{10102, nullptr, "SaveReport"},
|
||||
{10103, nullptr, "SaveReportWithUser"},
|
||||
{10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"},
|
||||
{10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
|
||||
{10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
|
||||
{10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
|
||||
{10200, nullptr, "RequestImmediateTransmission"},
|
||||
{10300, nullptr, "GetTransmissionStatus"},
|
||||
{20100, nullptr, "SaveSystemReport"},
|
||||
{20101, nullptr, "SaveSystemReportWithUser"},
|
||||
{20100, &PlayReport::SaveSystemReport, "SaveSystemReport"},
|
||||
{20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"},
|
||||
{20200, nullptr, "SetOperationMode"},
|
||||
{30100, nullptr, "ClearStorage"},
|
||||
{30200, nullptr, "ClearStatistics"},
|
||||
@@ -47,7 +44,28 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
|
||||
template <Core::Reporter::PlayReportType Type>
|
||||
void SaveReport(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
static_cast<u8>(Type), process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
template <Core::Reporter::PlayReportType Type>
|
||||
void SaveReportWithUser(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
@@ -57,24 +75,65 @@ private:
|
||||
|
||||
LOG_DEBUG(
|
||||
Service_PREPO,
|
||||
"called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, "
|
||||
"data2_size={:016X}",
|
||||
static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{Core::System::GetInstance().GetReporter()};
|
||||
reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2},
|
||||
user_id);
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
|
||||
process_id, user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SaveSystemReport(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
|
||||
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
title_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2});
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SaveSystemReportWithUser(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
|
||||
"data2_size={:016X}",
|
||||
user_id[1], user_id[0], title_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2},
|
||||
std::nullopt, user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager);
|
||||
std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager);
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
std::make_shared<PlayReport>(system, "prepo:a")->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<PlayReport>(system, "prepo:a2")->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<PlayReport>(system, "prepo:m")->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<PlayReport>(system, "prepo:s")->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<PlayReport>(system, "prepo:u")->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::PlayReport
|
||||
|
||||
@@ -10,6 +10,6 @@ class ServiceManager;
|
||||
|
||||
namespace Service::PlayReport {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::PlayReport
|
||||
|
||||
@@ -199,6 +199,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
|
||||
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
|
||||
// here and pass it into the respective InstallInterfaces functions.
|
||||
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
|
||||
system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
|
||||
|
||||
SM::ServiceManager::InstallInterfaces(sm);
|
||||
|
||||
@@ -224,7 +225,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
|
||||
HID::InstallInterfaces(*sm);
|
||||
LBL::InstallInterfaces(*sm);
|
||||
LDN::InstallInterfaces(*sm);
|
||||
LDR::InstallInterfaces(*sm);
|
||||
LDR::InstallInterfaces(system);
|
||||
LM::InstallInterfaces(*sm);
|
||||
Migration::InstallInterfaces(*sm);
|
||||
Mii::InstallInterfaces(*sm);
|
||||
@@ -235,12 +236,12 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
|
||||
NIFM::InstallInterfaces(*sm);
|
||||
NIM::InstallInterfaces(*sm);
|
||||
NPNS::InstallInterfaces(*sm);
|
||||
NS::InstallInterfaces(*sm);
|
||||
NS::InstallInterfaces(*sm, system.GetFileSystemController());
|
||||
Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
|
||||
PCIe::InstallInterfaces(*sm);
|
||||
PCTL::InstallInterfaces(*sm);
|
||||
PCV::InstallInterfaces(*sm);
|
||||
PlayReport::InstallInterfaces(*sm);
|
||||
PlayReport::InstallInterfaces(system);
|
||||
PM::InstallInterfaces(system);
|
||||
PSC::InstallInterfaces(*sm);
|
||||
PSM::InstallInterfaces(*sm);
|
||||
|
||||
@@ -18,10 +18,6 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
class VfsFilesystem;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class ClientPort;
|
||||
class ServerPort;
|
||||
@@ -31,6 +27,10 @@ class HLERequestContext;
|
||||
|
||||
namespace Service {
|
||||
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
} // namespace FileSystem
|
||||
|
||||
namespace SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@@ -176,7 +177,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
// Register the RomFS if a ".romfs" file was found
|
||||
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
|
||||
romfs = *romfs_iter;
|
||||
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
|
||||
std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -57,7 +58,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
|
||||
}
|
||||
|
||||
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
|
||||
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
|
||||
std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
+12
-1
@@ -10,6 +10,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
@@ -214,7 +215,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
|
||||
}
|
||||
|
||||
if (romfs != nullptr) {
|
||||
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
|
||||
std::make_unique<FileSys::RomFSFactory>(*this));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
@@ -258,6 +260,15 @@ ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadControlData(FileSys::NACP& control) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
control = *nacp;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::IsRomFSUpdatable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
ResultStatus ReadControlData(FileSys::NACP& control) override;
|
||||
bool IsRomFSUpdatable() const override;
|
||||
|
||||
private:
|
||||
|
||||
+16
-15
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -26,20 +27,18 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
|
||||
|
||||
if (nsp->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
if (nsp->IsExtractedType())
|
||||
return;
|
||||
|
||||
const auto control_nca =
|
||||
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
|
||||
std::tie(nacp_file, icon_file) =
|
||||
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
|
||||
|
||||
if (nsp->IsExtractedType()) {
|
||||
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
|
||||
} else {
|
||||
const auto control_nca =
|
||||
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
|
||||
std::tie(nacp_file, icon_file) =
|
||||
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
|
||||
|
||||
if (title_id == 0)
|
||||
return;
|
||||
|
||||
@@ -56,11 +55,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
|
||||
if (nsp.GetStatus() == ResultStatus::Success) {
|
||||
// Extracted Type case
|
||||
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
|
||||
FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
|
||||
FileSys::IsDirectoryExeFS(nsp.GetExeFS())) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
|
||||
// Non-Ectracted Type case
|
||||
// Non-Extracted Type case
|
||||
if (!nsp.IsExtractedType() &&
|
||||
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
|
||||
@@ -77,7 +76,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
|
||||
return {ResultStatus::ErrorAlreadyLoaded, {}};
|
||||
}
|
||||
|
||||
if (title_id == 0) {
|
||||
if (!nsp->IsExtractedType() && title_id == 0) {
|
||||
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
|
||||
}
|
||||
|
||||
@@ -91,7 +90,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
|
||||
return {nsp_program_status, {}};
|
||||
}
|
||||
|
||||
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
|
||||
if (!nsp->IsExtractedType() &&
|
||||
nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
|
||||
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
|
||||
}
|
||||
@@ -106,7 +106,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
|
||||
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
|
||||
Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
|
||||
std::move(update_raw));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -72,7 +73,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
|
||||
|
||||
FileSys::VirtualFile update_raw;
|
||||
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
|
||||
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
|
||||
Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
|
||||
std::move(update_raw));
|
||||
}
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
+7
-2
@@ -43,8 +43,13 @@ static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* me
|
||||
|
||||
// During boot, current_page_table might not be set yet, in which case we need not flush
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
|
||||
size * PAGE_SIZE);
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
for (u64 i = 0; i < size; i++) {
|
||||
const auto page = base + i;
|
||||
if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
|
||||
gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VAddr end = base + size;
|
||||
|
||||
+46
-1
@@ -4,8 +4,14 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/format.h>
|
||||
#include "common/file_util.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
@@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
|
||||
// Purposefully ignore the first five frames, as there's a significant amount of overhead in
|
||||
// booting that we shouldn't account for
|
||||
constexpr std::size_t IgnoreFrames = 5;
|
||||
|
||||
namespace Core {
|
||||
|
||||
PerfStats::PerfStats(u64 title_id) : title_id(title_id) {}
|
||||
|
||||
PerfStats::~PerfStats() {
|
||||
if (!Settings::values.record_frame_times || title_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::time_t t = std::time(nullptr);
|
||||
std::ostringstream stream;
|
||||
std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
|
||||
std::ostream_iterator<double>(stream, "\n"));
|
||||
const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
|
||||
// %F Date format expanded is "%Y-%m-%d"
|
||||
const std::string filename =
|
||||
fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
|
||||
FileUtil::IOFile file(filename, "w");
|
||||
file.WriteString(stream.str());
|
||||
}
|
||||
|
||||
void PerfStats::BeginSystemFrame() {
|
||||
std::lock_guard lock{object_mutex};
|
||||
|
||||
@@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() {
|
||||
std::lock_guard lock{object_mutex};
|
||||
|
||||
auto frame_end = Clock::now();
|
||||
accumulated_frametime += frame_end - frame_begin;
|
||||
const auto frame_time = frame_end - frame_begin;
|
||||
if (current_index < perf_history.size()) {
|
||||
perf_history[current_index++] =
|
||||
std::chrono::duration<double, std::milli>(frame_time).count();
|
||||
}
|
||||
accumulated_frametime += frame_time;
|
||||
system_frames += 1;
|
||||
|
||||
previous_frame_length = frame_end - previous_frame_end;
|
||||
@@ -40,6 +74,17 @@ void PerfStats::EndGameFrame() {
|
||||
game_frames += 1;
|
||||
}
|
||||
|
||||
double PerfStats::GetMeanFrametime() {
|
||||
std::lock_guard lock{object_mutex};
|
||||
|
||||
if (current_index <= IgnoreFrames) {
|
||||
return 0;
|
||||
}
|
||||
const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
|
||||
perf_history.begin() + current_index, 0);
|
||||
return sum / (current_index - IgnoreFrames);
|
||||
}
|
||||
|
||||
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
|
||||
std::lock_guard lock{object_mutex};
|
||||
|
||||
|
||||
+20
-1
@@ -4,7 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -27,6 +29,10 @@ struct PerfStatsResults {
|
||||
*/
|
||||
class PerfStats {
|
||||
public:
|
||||
explicit PerfStats(u64 title_id);
|
||||
|
||||
~PerfStats();
|
||||
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
|
||||
void BeginSystemFrame();
|
||||
@@ -35,6 +41,11 @@ public:
|
||||
|
||||
PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
|
||||
|
||||
/**
|
||||
* Returns the Arthimetic Mean of all frametime values stored in the performance history.
|
||||
*/
|
||||
double GetMeanFrametime();
|
||||
|
||||
/**
|
||||
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
|
||||
* useful for scaling inputs or outputs moving between the two time domains.
|
||||
@@ -42,7 +53,15 @@ public:
|
||||
double GetLastFrameTimeScale();
|
||||
|
||||
private:
|
||||
std::mutex object_mutex;
|
||||
std::mutex object_mutex{};
|
||||
|
||||
/// Title ID for the game that is running. 0 if there is no game running yet
|
||||
u64 title_id{0};
|
||||
/// Current index for writing to the perf_history array
|
||||
std::size_t current_index{0};
|
||||
/// Stores an hour of historical frametime data useful for processing and tracking performance
|
||||
/// regressions with code changes.
|
||||
std::array<double, 216000> perf_history = {};
|
||||
|
||||
/// Point when the cumulative counters were reset
|
||||
Clock::time_point reset_point = Clock::now();
|
||||
|
||||
@@ -304,8 +304,8 @@ void Reporter::SaveUnimplementedAppletReport(
|
||||
SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp));
|
||||
}
|
||||
|
||||
void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
|
||||
std::optional<u128> user_id) const {
|
||||
void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
|
||||
std::optional<u64> process_id, std::optional<u128> user_id) const {
|
||||
if (!IsReportingEnabled()) {
|
||||
return;
|
||||
}
|
||||
@@ -321,7 +321,11 @@ void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vec
|
||||
data_out.push_back(Common::HexToString(d));
|
||||
}
|
||||
|
||||
out["play_report_process_id"] = fmt::format("{:016X}", process_id);
|
||||
if (process_id.has_value()) {
|
||||
out["play_report_process_id"] = fmt::format("{:016X}", *process_id);
|
||||
}
|
||||
|
||||
out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type));
|
||||
out["play_report_data"] = std::move(data_out);
|
||||
|
||||
SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
|
||||
|
||||
+8
-2
@@ -46,8 +46,14 @@ public:
|
||||
std::vector<std::vector<u8>> normal_channel,
|
||||
std::vector<std::vector<u8>> interactive_channel) const;
|
||||
|
||||
void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
|
||||
std::optional<u128> user_id = {}) const;
|
||||
enum class PlayReportType {
|
||||
Old,
|
||||
New,
|
||||
System,
|
||||
};
|
||||
|
||||
void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
|
||||
std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
|
||||
|
||||
void SaveErrorReport(u64 title_id, ResultCode result,
|
||||
std::optional<std::string> custom_text_main = {},
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
@@ -97,8 +98,8 @@ void LogSettings() {
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
||||
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
|
||||
LogSetting("DataStorage_NandDir", Settings::values.nand_dir);
|
||||
LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir);
|
||||
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
|
||||
LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
|
||||
LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
|
||||
LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
|
||||
|
||||
+33
-2
@@ -346,6 +346,31 @@ struct TouchscreenInput {
|
||||
u32 rotation_angle;
|
||||
};
|
||||
|
||||
enum class NANDTotalSize : u64 {
|
||||
S29_1GB = 0x747C00000ULL,
|
||||
};
|
||||
|
||||
enum class NANDUserSize : u64 {
|
||||
S26GB = 0x680000000ULL,
|
||||
};
|
||||
|
||||
enum class NANDSystemSize : u64 {
|
||||
S2_5GB = 0xA0000000,
|
||||
};
|
||||
|
||||
enum class SDMCSize : u64 {
|
||||
S1GB = 0x40000000,
|
||||
S2GB = 0x80000000,
|
||||
S4GB = 0x100000000ULL,
|
||||
S8GB = 0x200000000ULL,
|
||||
S16GB = 0x400000000ULL,
|
||||
S32GB = 0x800000000ULL,
|
||||
S64GB = 0x1000000000ULL,
|
||||
S128GB = 0x2000000000ULL,
|
||||
S256GB = 0x4000000000ULL,
|
||||
S1TB = 0x10000000000ULL,
|
||||
};
|
||||
|
||||
struct Values {
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
@@ -382,8 +407,13 @@ struct Values {
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
std::string nand_dir;
|
||||
std::string sdmc_dir;
|
||||
bool gamecard_inserted;
|
||||
bool gamecard_current_game;
|
||||
std::string gamecard_path;
|
||||
NANDTotalSize nand_total_size;
|
||||
NANDSystemSize nand_system_size;
|
||||
NANDUserSize nand_user_size;
|
||||
SDMCSize sdmc_size;
|
||||
|
||||
// Renderer
|
||||
float resolution_factor;
|
||||
@@ -409,6 +439,7 @@ struct Values {
|
||||
float volume;
|
||||
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
bool use_gdbstub;
|
||||
u16 gdbstub_port;
|
||||
std::string program_args;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <bitset>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
@@ -49,6 +50,33 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
|
||||
}
|
||||
}
|
||||
|
||||
Tegra::Texture::FullTextureInfo KeplerCompute::GetTexture(std::size_t offset) const {
|
||||
const std::bitset<8> cbuf_mask = launch_description.const_buffer_enable_mask.Value();
|
||||
ASSERT(cbuf_mask[regs.tex_cb_index]);
|
||||
|
||||
const auto& texinfo = launch_description.const_buffer_config[regs.tex_cb_index];
|
||||
ASSERT(texinfo.Address() != 0);
|
||||
|
||||
const GPUVAddr address = texinfo.Address() + offset * sizeof(Texture::TextureHandle);
|
||||
ASSERT(address < texinfo.Address() + texinfo.size);
|
||||
|
||||
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(address)};
|
||||
return GetTextureInfo(tex_handle, offset);
|
||||
}
|
||||
|
||||
Texture::FullTextureInfo KeplerCompute::GetTextureInfo(const Texture::TextureHandle tex_handle,
|
||||
std::size_t offset) const {
|
||||
return Texture::FullTextureInfo{static_cast<u32>(offset), GetTICEntry(tex_handle.tic_id),
|
||||
GetTSCEntry(tex_handle.tsc_id)};
|
||||
}
|
||||
|
||||
u32 KeplerCompute::AccessConstBuffer32(u64 const_buffer, u64 offset) const {
|
||||
const auto& buffer = launch_description.const_buffer_config[const_buffer];
|
||||
u32 result;
|
||||
std::memcpy(&result, memory_manager.GetPointer(buffer.Address() + offset), sizeof(u32));
|
||||
return result;
|
||||
}
|
||||
|
||||
void KeplerCompute::ProcessLaunch() {
|
||||
const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address();
|
||||
memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description,
|
||||
@@ -60,4 +88,29 @@ void KeplerCompute::ProcessLaunch() {
|
||||
rasterizer.DispatchCompute(code_addr);
|
||||
}
|
||||
|
||||
Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
|
||||
const GPUVAddr tic_address_gpu{regs.tic.Address() + tic_index * sizeof(Texture::TICEntry)};
|
||||
|
||||
Texture::TICEntry tic_entry;
|
||||
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
|
||||
const auto r_type{tic_entry.r_type.Value()};
|
||||
const auto g_type{tic_entry.g_type.Value()};
|
||||
const auto b_type{tic_entry.b_type.Value()};
|
||||
const auto a_type{tic_entry.a_type.Value()};
|
||||
|
||||
// TODO(Subv): Different data types for separate components are not supported
|
||||
DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||
|
||||
return tic_entry;
|
||||
}
|
||||
|
||||
Texture::TSCEntry KeplerCompute::GetTSCEntry(u32 tsc_index) const {
|
||||
const GPUVAddr tsc_address_gpu{regs.tsc.Address() + tsc_index * sizeof(Texture::TSCEntry)};
|
||||
|
||||
Texture::TSCEntry tsc_entry;
|
||||
memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
|
||||
return tsc_entry;
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/engine_upload.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -111,7 +112,7 @@ public:
|
||||
|
||||
INSERT_PADDING_WORDS(0x3FE);
|
||||
|
||||
u32 texture_const_buffer_index;
|
||||
u32 tex_cb_index;
|
||||
|
||||
INSERT_PADDING_WORDS(0x374);
|
||||
};
|
||||
@@ -149,7 +150,7 @@ public:
|
||||
union {
|
||||
BitField<0, 8, u32> const_buffer_enable_mask;
|
||||
BitField<29, 2, u32> cache_layout;
|
||||
} memory_config;
|
||||
};
|
||||
|
||||
INSERT_PADDING_WORDS(0x8);
|
||||
|
||||
@@ -194,6 +195,14 @@ public:
|
||||
/// Write the value to the register identified by method.
|
||||
void CallMethod(const GPU::MethodCall& method_call);
|
||||
|
||||
Tegra::Texture::FullTextureInfo GetTexture(std::size_t offset) const;
|
||||
|
||||
/// Given a Texture Handle, returns the TSC and TIC entries.
|
||||
Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle,
|
||||
std::size_t offset) const;
|
||||
|
||||
u32 AccessConstBuffer32(u64 const_buffer, u64 offset) const;
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
@@ -201,6 +210,12 @@ private:
|
||||
Upload::State upload_state;
|
||||
|
||||
void ProcessLaunch();
|
||||
|
||||
/// Retrieves information about a specific TIC entry from the TIC buffer.
|
||||
Texture::TICEntry GetTICEntry(u32 tic_index) const;
|
||||
|
||||
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
||||
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
||||
};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
@@ -218,12 +233,12 @@ ASSERT_REG_POSITION(launch, 0xAF);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(code_loc, 0x582);
|
||||
ASSERT_REG_POSITION(texture_const_buffer_index, 0x982);
|
||||
ASSERT_REG_POSITION(tex_cb_index, 0x982);
|
||||
ASSERT_LAUNCH_PARAM_POSITION(program_start, 0x8);
|
||||
ASSERT_LAUNCH_PARAM_POSITION(grid_dim_x, 0xC);
|
||||
ASSERT_LAUNCH_PARAM_POSITION(shared_alloc, 0x11);
|
||||
ASSERT_LAUNCH_PARAM_POSITION(block_dim_x, 0x12);
|
||||
ASSERT_LAUNCH_PARAM_POSITION(memory_config, 0x14);
|
||||
ASSERT_LAUNCH_PARAM_POSITION(const_buffer_enable_mask, 0x14);
|
||||
ASSERT_LAUNCH_PARAM_POSITION(const_buffer_config, 0x1D);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
|
||||
@@ -89,6 +89,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
|
||||
// Commercial games seem to assume this value is enabled and nouveau sets this value manually.
|
||||
regs.rt_separate_frag_data = 1;
|
||||
|
||||
// Some games (like Super Mario Odyssey) assume that SRGB is enabled.
|
||||
regs.framebuffer_srgb = 1;
|
||||
}
|
||||
|
||||
#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name))
|
||||
@@ -244,7 +247,7 @@ void Maxwell3D::InitDirtySettings() {
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg;
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) {
|
||||
// Reset the current macro.
|
||||
executing_macro = 0;
|
||||
|
||||
@@ -252,7 +255,7 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
const u32 entry = ((method - MacroRegistersStart) >> 1) % macro_positions.size();
|
||||
|
||||
// Execute the current macro.
|
||||
macro_interpreter.Execute(macro_positions[entry], std::move(parameters));
|
||||
macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters);
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
@@ -289,7 +292,8 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
|
||||
// Call the macro when there are no more parameters in the command buffer
|
||||
if (method_call.IsLastCall()) {
|
||||
CallMacroMethod(executing_macro, std::move(macro_params));
|
||||
CallMacroMethod(executing_macro, macro_params.size(), macro_params.data());
|
||||
macro_params.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -328,6 +332,10 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ProcessMacroBind(method_call.argument);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(firmware[4]): {
|
||||
ProcessFirmwareCall4();
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
|
||||
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
|
||||
@@ -418,6 +426,14 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
|
||||
macro_positions[regs.macros.entry++] = data;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessFirmwareCall4() {
|
||||
LOG_WARNING(HW_GPU, "(STUBBED) called");
|
||||
|
||||
// Firmware call 4 is a blob that changes some registers depending on its parameters.
|
||||
// These registers don't affect emulation and so are stubbed by setting 0xd00 to 1.
|
||||
regs.reg_array[0xd00] = 1;
|
||||
}
|
||||
|
||||
void Maxwell3D::ProcessQueryGet() {
|
||||
const GPUVAddr sequence_address{regs.query.QueryAddress()};
|
||||
// Since the sequence address is given as a GPU VAddr, we have to convert it to an application
|
||||
@@ -525,7 +541,7 @@ void Maxwell3D::ProcessSyncPoint() {
|
||||
}
|
||||
|
||||
void Maxwell3D::DrawArrays() {
|
||||
LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
|
||||
LOG_TRACE(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
|
||||
regs.vertex_buffer.count);
|
||||
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
static constexpr std::size_t NumVertexAttributes = 32;
|
||||
static constexpr std::size_t NumVaryings = 31;
|
||||
static constexpr std::size_t NumTextureSamplers = 32;
|
||||
static constexpr std::size_t NumImages = 8; // TODO(Rodrigo): Investigate this number
|
||||
static constexpr std::size_t NumClipDistances = 8;
|
||||
static constexpr std::size_t MaxShaderProgram = 6;
|
||||
static constexpr std::size_t MaxShaderStage = 5;
|
||||
@@ -1088,7 +1089,9 @@ public:
|
||||
INSERT_PADDING_WORDS(14);
|
||||
} shader_config[MaxShaderProgram];
|
||||
|
||||
INSERT_PADDING_WORDS(0x80);
|
||||
INSERT_PADDING_WORDS(0x60);
|
||||
|
||||
u32 firmware[0x20];
|
||||
|
||||
struct {
|
||||
u32 cb_size;
|
||||
@@ -1307,9 +1310,10 @@ private:
|
||||
/**
|
||||
* Call a macro on this engine.
|
||||
* @param method Method to call
|
||||
* @param num_parameters Number of arguments
|
||||
* @param parameters Arguments to the method call
|
||||
*/
|
||||
void CallMacroMethod(u32 method, std::vector<u32> parameters);
|
||||
void CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters);
|
||||
|
||||
/// Handles writes to the macro uploading register.
|
||||
void ProcessMacroUpload(u32 data);
|
||||
@@ -1317,6 +1321,9 @@ private:
|
||||
/// Handles writes to the macro bind register.
|
||||
void ProcessMacroBind(u32 data);
|
||||
|
||||
/// Handles firmware blob 4
|
||||
void ProcessFirmwareCall4();
|
||||
|
||||
/// Handles a write to the CLEAR_BUFFERS register.
|
||||
void ProcessClearBuffers();
|
||||
|
||||
@@ -1429,6 +1436,7 @@ ASSERT_REG_POSITION(vertex_array[0], 0x700);
|
||||
ASSERT_REG_POSITION(independent_blend, 0x780);
|
||||
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
|
||||
ASSERT_REG_POSITION(shader_config[0], 0x800);
|
||||
ASSERT_REG_POSITION(firmware, 0x8C0);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
ASSERT_REG_POSITION(cb_bind[0], 0x904);
|
||||
ASSERT_REG_POSITION(tex_cb_index, 0x982);
|
||||
|
||||
@@ -544,6 +544,35 @@ enum class VoteOperation : u64 {
|
||||
Eq = 2, // allThreadsEqualNV
|
||||
};
|
||||
|
||||
enum class ImageAtomicSize : u64 {
|
||||
U32 = 0,
|
||||
S32 = 1,
|
||||
U64 = 2,
|
||||
F32 = 3,
|
||||
S64 = 5,
|
||||
SD32 = 6,
|
||||
SD64 = 7,
|
||||
};
|
||||
|
||||
enum class ImageAtomicOperation : u64 {
|
||||
Add = 0,
|
||||
Min = 1,
|
||||
Max = 2,
|
||||
Inc = 3,
|
||||
Dec = 4,
|
||||
And = 5,
|
||||
Or = 6,
|
||||
Xor = 7,
|
||||
Exch = 8,
|
||||
};
|
||||
|
||||
enum class ShuffleOperation : u64 {
|
||||
Idx = 0, // shuffleNV
|
||||
Up = 1, // shuffleUpNV
|
||||
Down = 2, // shuffleDownNV
|
||||
Bfly = 3, // shuffleXorNV
|
||||
};
|
||||
|
||||
union Instruction {
|
||||
Instruction& operator=(const Instruction& instr) {
|
||||
value = instr.value;
|
||||
@@ -577,6 +606,15 @@ union Instruction {
|
||||
BitField<42, 1, u64> negate_value;
|
||||
} vote;
|
||||
|
||||
union {
|
||||
BitField<30, 2, ShuffleOperation> operation;
|
||||
BitField<48, 3, u64> pred48;
|
||||
BitField<28, 1, u64> is_index_imm;
|
||||
BitField<29, 1, u64> is_mask_imm;
|
||||
BitField<20, 5, u64> index_imm;
|
||||
BitField<34, 13, u64> mask_imm;
|
||||
} shfl;
|
||||
|
||||
union {
|
||||
BitField<8, 8, Register> gpr;
|
||||
BitField<20, 24, s64> offset;
|
||||
@@ -674,6 +712,10 @@ union Instruction {
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} shift;
|
||||
|
||||
union {
|
||||
BitField<39, 1, u64> wrap;
|
||||
} shr;
|
||||
|
||||
union {
|
||||
BitField<39, 5, u64> shift_amount;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
@@ -907,6 +949,11 @@ union Instruction {
|
||||
BitField<49, 3, PredCondition> cond;
|
||||
} isetp;
|
||||
|
||||
union {
|
||||
BitField<48, 1, u64> is_signed;
|
||||
BitField<49, 3, PredCondition> cond;
|
||||
} icmp;
|
||||
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
@@ -1387,6 +1434,14 @@ union Instruction {
|
||||
}
|
||||
} sust;
|
||||
|
||||
union {
|
||||
BitField<28, 1, u64> is_ba;
|
||||
BitField<51, 3, ImageAtomicSize> size;
|
||||
BitField<33, 3, ImageType> image_type;
|
||||
BitField<29, 4, ImageAtomicOperation> operation;
|
||||
BitField<49, 2, OutOfBoundsStore> out_of_bounds_store;
|
||||
} suatom_d;
|
||||
|
||||
union {
|
||||
BitField<20, 24, u64> target;
|
||||
BitField<5, 1, u64> constant_buffer;
|
||||
@@ -1508,6 +1563,7 @@ public:
|
||||
BRK,
|
||||
DEPBAR,
|
||||
VOTE,
|
||||
SHFL,
|
||||
BFE_C,
|
||||
BFE_R,
|
||||
BFE_IMM,
|
||||
@@ -1539,6 +1595,7 @@ public:
|
||||
TMML_B, // Texture Mip Map Level
|
||||
TMML, // Texture Mip Map Level
|
||||
SUST, // Surface Store
|
||||
SUATOM, // Surface Atomic Operation
|
||||
EXIT,
|
||||
NOP,
|
||||
IPA,
|
||||
@@ -1593,6 +1650,10 @@ public:
|
||||
SEL_C,
|
||||
SEL_R,
|
||||
SEL_IMM,
|
||||
ICMP_RC,
|
||||
ICMP_R,
|
||||
ICMP_CR,
|
||||
ICMP_IMM,
|
||||
MUFU, // Multi-Function Operator
|
||||
RRO_C, // Range Reduction Operator
|
||||
RRO_R,
|
||||
@@ -1798,6 +1859,7 @@ private:
|
||||
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
|
||||
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
|
||||
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
|
||||
INST("1110111100010---", Id::SHFL, Type::Warp, "SHFL"),
|
||||
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
|
||||
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
|
||||
INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
|
||||
@@ -1822,6 +1884,7 @@ private:
|
||||
INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
|
||||
INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
|
||||
INST("11101011001-----", Id::SUST, Type::Image, "SUST"),
|
||||
INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"),
|
||||
INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"),
|
||||
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
|
||||
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
|
||||
@@ -1856,6 +1919,10 @@ private:
|
||||
INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
|
||||
INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
|
||||
INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
|
||||
INST("010100110100----", Id::ICMP_RC, Type::ArithmeticInteger, "ICMP_RC"),
|
||||
INST("010110110100----", Id::ICMP_R, Type::ArithmeticInteger, "ICMP_R"),
|
||||
INST("010010110100----", Id::ICMP_CR, Type::ArithmeticInteger, "ICMP_CR"),
|
||||
INST("0011011-0100----", Id::ICMP_IMM, Type::ArithmeticInteger, "ICMP_IMM"),
|
||||
INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"),
|
||||
INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"),
|
||||
INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
|
||||
|
||||
@@ -14,11 +14,18 @@ namespace Tegra {
|
||||
|
||||
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
|
||||
|
||||
void MacroInterpreter::Execute(u32 offset, std::vector<u32> parameters) {
|
||||
void MacroInterpreter::Execute(u32 offset, std::size_t num_parameters, const u32* parameters) {
|
||||
MICROPROFILE_SCOPE(MacroInterp);
|
||||
Reset();
|
||||
|
||||
registers[1] = parameters[0];
|
||||
this->parameters = std::move(parameters);
|
||||
|
||||
if (num_parameters > parameters_capacity) {
|
||||
parameters_capacity = num_parameters;
|
||||
this->parameters = std::make_unique<u32[]>(num_parameters);
|
||||
}
|
||||
std::memcpy(this->parameters.get(), parameters, num_parameters * sizeof(u32));
|
||||
this->num_parameters = num_parameters;
|
||||
|
||||
// Execute the code until we hit an exit condition.
|
||||
bool keep_executing = true;
|
||||
@@ -27,7 +34,7 @@ void MacroInterpreter::Execute(u32 offset, std::vector<u32> parameters) {
|
||||
}
|
||||
|
||||
// Assert the the macro used all the input parameters
|
||||
ASSERT(next_parameter_index == this->parameters.size());
|
||||
ASSERT(next_parameter_index == num_parameters);
|
||||
}
|
||||
|
||||
void MacroInterpreter::Reset() {
|
||||
@@ -35,7 +42,7 @@ void MacroInterpreter::Reset() {
|
||||
pc = 0;
|
||||
delayed_pc = {};
|
||||
method_address.raw = 0;
|
||||
parameters.clear();
|
||||
num_parameters = 0;
|
||||
// The next parameter index starts at 1, because $r1 already has the value of the first
|
||||
// parameter.
|
||||
next_parameter_index = 1;
|
||||
@@ -124,9 +131,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
|
||||
|
||||
// An instruction with the Exit flag will not actually
|
||||
// cause an exit if it's executed inside a delay slot.
|
||||
// TODO(Blinkhawk): Reversed to always exit. The behavior explained above requires further
|
||||
// testing on the MME code.
|
||||
if (opcode.is_exit) {
|
||||
if (opcode.is_exit && !is_delay_slot) {
|
||||
// Exit has a delay slot, execute the next instruction
|
||||
Step(offset, true);
|
||||
return false;
|
||||
@@ -229,7 +234,8 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res
|
||||
}
|
||||
|
||||
u32 MacroInterpreter::FetchParameter() {
|
||||
return parameters.at(next_parameter_index++);
|
||||
ASSERT(next_parameter_index < num_parameters);
|
||||
return parameters[next_parameter_index++];
|
||||
}
|
||||
|
||||
u32 MacroInterpreter::GetRegister(u32 register_id) const {
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
* @param offset Offset to start execution at.
|
||||
* @param parameters The parameters of the macro.
|
||||
*/
|
||||
void Execute(u32 offset, std::vector<u32> parameters);
|
||||
void Execute(u32 offset, std::size_t num_parameters, const u32* parameters);
|
||||
|
||||
private:
|
||||
enum class Operation : u32 {
|
||||
@@ -162,10 +162,12 @@ private:
|
||||
MethodAddress method_address = {};
|
||||
|
||||
/// Input parameters of the current macro.
|
||||
std::vector<u32> parameters;
|
||||
std::unique_ptr<u32[]> parameters;
|
||||
std::size_t num_parameters = 0;
|
||||
std::size_t parameters_capacity = 0;
|
||||
/// Index of the next parameter that will be fetched by the 'parm' instruction.
|
||||
u32 next_parameter_index = 0;
|
||||
|
||||
bool carry_flag{};
|
||||
bool carry_flag = false;
|
||||
};
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -331,7 +331,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
|
||||
SetupDrawConstBuffers(stage_enum, shader);
|
||||
SetupDrawGlobalMemory(stage_enum, shader);
|
||||
const auto texture_buffer_usage{SetupTextures(stage_enum, shader, base_bindings)};
|
||||
const auto texture_buffer_usage{SetupDrawTextures(stage_enum, shader, base_bindings)};
|
||||
|
||||
const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage};
|
||||
const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
|
||||
@@ -489,9 +489,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
|
||||
// Assume that a surface will be written to if it is used as a framebuffer, even if
|
||||
// the shader doesn't actually write to it.
|
||||
texture_cache.MarkColorBufferInUse(*single_color_target);
|
||||
// Workaround for and issue in nvidia drivers
|
||||
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
|
||||
state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
|
||||
}
|
||||
|
||||
fbkey.is_single_buffer = true;
|
||||
@@ -512,11 +509,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
|
||||
// Assume that a surface will be written to if it is used as a framebuffer, even
|
||||
// if the shader doesn't actually write to it.
|
||||
texture_cache.MarkColorBufferInUse(index);
|
||||
// Enable sRGB only for supported formats
|
||||
// Workaround for and issue in nvidia drivers
|
||||
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
|
||||
state.framebuffer_srgb.enabled |=
|
||||
color_surface->GetSurfaceParams().srgb_conversion;
|
||||
}
|
||||
|
||||
fbkey.color_attachments[index] =
|
||||
@@ -537,8 +529,7 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
|
||||
texture_cache.MarkDepthBufferInUse();
|
||||
|
||||
fbkey.zeta = depth_surface;
|
||||
fbkey.stencil_enable = regs.stencil_enable &&
|
||||
depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
|
||||
fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
|
||||
}
|
||||
|
||||
texture_cache.GuardRenderTargets(false);
|
||||
@@ -577,16 +568,15 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, boo
|
||||
if (depth_surface) {
|
||||
const auto& params = depth_surface->GetSurfaceParams();
|
||||
switch (params.type) {
|
||||
case VideoCore::Surface::SurfaceType::Depth: {
|
||||
case VideoCore::Surface::SurfaceType::Depth:
|
||||
depth_surface->Attach(GL_DEPTH_ATTACHMENT, GL_DRAW_FRAMEBUFFER);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
break;
|
||||
}
|
||||
case VideoCore::Surface::SurfaceType::DepthStencil: {
|
||||
depth_surface->Attach(GL_DEPTH_ATTACHMENT, GL_DRAW_FRAMEBUFFER);
|
||||
case VideoCore::Surface::SurfaceType::DepthStencil:
|
||||
depth_surface->Attach(GL_DEPTH_STENCIL_ATTACHMENT, GL_DRAW_FRAMEBUFFER);
|
||||
break;
|
||||
}
|
||||
default: { UNIMPLEMENTED(); }
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
@@ -639,6 +629,7 @@ void RasterizerOpenGL::Clear() {
|
||||
ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
|
||||
use_stencil = true;
|
||||
clear_state.stencil.test_enabled = true;
|
||||
|
||||
if (regs.clear_flags.stencil) {
|
||||
// Stencil affects the clear so fill it with the used masks
|
||||
clear_state.stencil.front.test_func = GL_ALWAYS;
|
||||
@@ -802,7 +793,11 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
|
||||
}
|
||||
|
||||
auto kernel = shader_cache.GetComputeKernel(code_addr);
|
||||
const auto [program, next_bindings] = kernel->GetProgramHandle({});
|
||||
ProgramVariant variant;
|
||||
variant.texture_buffer_usage = SetupComputeTextures(kernel);
|
||||
SetupComputeImages(kernel);
|
||||
|
||||
const auto [program, next_bindings] = kernel->GetProgramHandle(variant);
|
||||
state.draw.shader_program = program;
|
||||
state.draw.program_pipeline = 0;
|
||||
|
||||
@@ -817,13 +812,13 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
|
||||
SetupComputeConstBuffers(kernel);
|
||||
SetupComputeGlobalMemory(kernel);
|
||||
|
||||
// TODO(Rodrigo): Bind images and samplers
|
||||
|
||||
buffer_cache.Unmap();
|
||||
|
||||
bind_ubo_pushbuffer.Bind();
|
||||
bind_ssbo_pushbuffer.Bind();
|
||||
|
||||
state.ApplyTextures();
|
||||
state.ApplyImages();
|
||||
state.ApplyShaderProgram();
|
||||
state.ApplyProgramPipeline();
|
||||
|
||||
@@ -903,6 +898,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
|
||||
}
|
||||
|
||||
screen_info.display_texture = surface->GetTexture();
|
||||
screen_info.display_srgb = surface->GetSurfaceParams().srgb_conversion;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -923,7 +919,7 @@ void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) {
|
||||
const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
|
||||
for (const auto& entry : kernel->GetShaderEntries().const_buffers) {
|
||||
const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
|
||||
const std::bitset<8> mask = launch_desc.memory_config.const_buffer_enable_mask.Value();
|
||||
const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
|
||||
Tegra::Engines::ConstBufferInfo buffer;
|
||||
buffer.address = config.Address();
|
||||
buffer.size = config.size;
|
||||
@@ -982,53 +978,125 @@ void RasterizerOpenGL::SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entr
|
||||
bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size));
|
||||
}
|
||||
|
||||
TextureBufferUsage RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
|
||||
BaseBindings base_bindings) {
|
||||
TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage,
|
||||
const Shader& shader,
|
||||
BaseBindings base_bindings) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||
const auto& gpu = system.GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
const auto& entries = shader->GetShaderEntries().samplers;
|
||||
|
||||
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.texture_units),
|
||||
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.textures),
|
||||
"Exceeded the number of active textures.");
|
||||
|
||||
TextureBufferUsage texture_buffer_usage{0};
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
Tegra::Texture::FullTextureInfo texture;
|
||||
if (entry.IsBindless()) {
|
||||
const auto texture = [&]() {
|
||||
if (!entry.IsBindless()) {
|
||||
return maxwell3d.GetStageTexture(stage, entry.GetOffset());
|
||||
}
|
||||
const auto cbuf = entry.GetBindlessCBuf();
|
||||
Tegra::Texture::TextureHandle tex_handle;
|
||||
tex_handle.raw = maxwell3d.AccessConstBuffer32(stage, cbuf.first, cbuf.second);
|
||||
texture = maxwell3d.GetTextureInfo(tex_handle, entry.GetOffset());
|
||||
} else {
|
||||
texture = maxwell3d.GetStageTexture(stage, entry.GetOffset());
|
||||
}
|
||||
const u32 current_bindpoint = base_bindings.sampler + bindpoint;
|
||||
return maxwell3d.GetTextureInfo(tex_handle, entry.GetOffset());
|
||||
}();
|
||||
|
||||
auto& unit{state.texture_units[current_bindpoint]};
|
||||
unit.sampler = sampler_cache.GetSampler(texture.tsc);
|
||||
|
||||
if (const auto view{texture_cache.GetTextureSurface(texture, entry)}; view) {
|
||||
if (view->GetSurfaceParams().IsBuffer()) {
|
||||
// Record that this texture is a texture buffer.
|
||||
texture_buffer_usage.set(bindpoint);
|
||||
} else {
|
||||
// Apply swizzle to textures that are not buffers.
|
||||
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
|
||||
texture.tic.w_source);
|
||||
}
|
||||
state.texture_units[current_bindpoint].texture = view->GetTexture();
|
||||
} else {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
unit.texture = 0;
|
||||
if (SetupTexture(base_bindings.sampler + bindpoint, texture, entry)) {
|
||||
texture_buffer_usage.set(bindpoint);
|
||||
}
|
||||
}
|
||||
|
||||
return texture_buffer_usage;
|
||||
}
|
||||
|
||||
TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||
const auto& compute = system.GPU().KeplerCompute();
|
||||
const auto& entries = kernel->GetShaderEntries().samplers;
|
||||
|
||||
ASSERT_MSG(entries.size() <= std::size(state.textures),
|
||||
"Exceeded the number of active textures.");
|
||||
|
||||
TextureBufferUsage texture_buffer_usage{0};
|
||||
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
const auto texture = [&]() {
|
||||
if (!entry.IsBindless()) {
|
||||
return compute.GetTexture(entry.GetOffset());
|
||||
}
|
||||
const auto cbuf = entry.GetBindlessCBuf();
|
||||
Tegra::Texture::TextureHandle tex_handle;
|
||||
tex_handle.raw = compute.AccessConstBuffer32(cbuf.first, cbuf.second);
|
||||
return compute.GetTextureInfo(tex_handle, entry.GetOffset());
|
||||
}();
|
||||
|
||||
if (SetupTexture(bindpoint, texture, entry)) {
|
||||
texture_buffer_usage.set(bindpoint);
|
||||
}
|
||||
}
|
||||
|
||||
return texture_buffer_usage;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
||||
const GLShader::SamplerEntry& entry) {
|
||||
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
|
||||
|
||||
const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
|
||||
if (!view) {
|
||||
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||
state.textures[binding] = 0;
|
||||
return false;
|
||||
}
|
||||
state.textures[binding] = view->GetTexture();
|
||||
|
||||
if (view->GetSurfaceParams().IsBuffer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply swizzle to textures that are not buffers.
|
||||
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
|
||||
texture.tic.w_source);
|
||||
return false;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
|
||||
const auto& compute = system.GPU().KeplerCompute();
|
||||
const auto& entries = shader->GetShaderEntries().images;
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& entry = entries[bindpoint];
|
||||
const auto tic = [&]() {
|
||||
if (!entry.IsBindless()) {
|
||||
return compute.GetTexture(entry.GetOffset()).tic;
|
||||
}
|
||||
const auto cbuf = entry.GetBindlessCBuf();
|
||||
Tegra::Texture::TextureHandle tex_handle;
|
||||
tex_handle.raw = compute.AccessConstBuffer32(cbuf.first, cbuf.second);
|
||||
return compute.GetTextureInfo(tex_handle, entry.GetOffset()).tic;
|
||||
}();
|
||||
SetupImage(bindpoint, tic, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
|
||||
const GLShader::ImageEntry& entry) {
|
||||
const auto view = texture_cache.GetImageSurface(tic, entry);
|
||||
if (!view) {
|
||||
state.images[binding] = 0;
|
||||
return;
|
||||
}
|
||||
if (!tic.IsBuffer()) {
|
||||
view->ApplySwizzle(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
|
||||
}
|
||||
if (entry.IsWritten()) {
|
||||
view->MarkAsModified(texture_cache.Tick());
|
||||
}
|
||||
state.images[binding] = view->GetTexture();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
const bool geometry_shaders_enabled =
|
||||
@@ -1119,9 +1187,12 @@ void RasterizerOpenGL::SyncStencilTestState() {
|
||||
if (!maxwell3d.dirty.stencil_test) {
|
||||
return;
|
||||
}
|
||||
const auto& regs = maxwell3d.regs;
|
||||
maxwell3d.dirty.stencil_test = false;
|
||||
|
||||
const auto& regs = maxwell3d.regs;
|
||||
state.stencil.test_enabled = regs.stencil_enable != 0;
|
||||
state.MarkDirtyStencilState();
|
||||
|
||||
if (!regs.stencil_enable) {
|
||||
return;
|
||||
}
|
||||
@@ -1150,8 +1221,6 @@ void RasterizerOpenGL::SyncStencilTestState() {
|
||||
state.stencil.back.action_depth_fail = GL_KEEP;
|
||||
state.stencil.back.action_depth_pass = GL_KEEP;
|
||||
}
|
||||
state.MarkDirtyStencilState();
|
||||
maxwell3d.dirty.stencil_test = false;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncColorMask() {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -137,8 +138,22 @@ private:
|
||||
|
||||
/// Configures the current textures to use for the draw command. Returns shaders texture buffer
|
||||
/// usage.
|
||||
TextureBufferUsage SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, BaseBindings base_bindings);
|
||||
TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, BaseBindings base_bindings);
|
||||
|
||||
/// Configures the textures used in a compute shader. Returns texture buffer usage.
|
||||
TextureBufferUsage SetupComputeTextures(const Shader& kernel);
|
||||
|
||||
/// Configures a texture. Returns true when the texture is a texture buffer.
|
||||
bool SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
|
||||
const GLShader::SamplerEntry& entry);
|
||||
|
||||
/// Configures images in a compute shader.
|
||||
void SetupComputeImages(const Shader& shader);
|
||||
|
||||
/// Configures an image.
|
||||
void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
|
||||
const GLShader::ImageEntry& entry);
|
||||
|
||||
/// Syncs the viewport and depth range to match the guest state
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
|
||||
@@ -214,7 +214,8 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn
|
||||
std::string source = "#version 430 core\n"
|
||||
"#extension GL_ARB_separate_shader_objects : enable\n"
|
||||
"#extension GL_NV_gpu_shader5 : enable\n"
|
||||
"#extension GL_NV_shader_thread_group : enable\n";
|
||||
"#extension GL_NV_shader_thread_group : enable\n"
|
||||
"#extension GL_NV_shader_thread_shuffle : enable\n";
|
||||
if (entries.shader_viewport_layer_array) {
|
||||
source += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
|
||||
}
|
||||
@@ -348,23 +349,16 @@ Shader CachedShader::CreateKernelFromCache(const ShaderParameters& params,
|
||||
}
|
||||
|
||||
std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) {
|
||||
GLuint handle{};
|
||||
if (program_type == ProgramType::Geometry) {
|
||||
handle = GetGeometryShader(variant);
|
||||
} else {
|
||||
const auto [entry, is_cache_miss] = programs.try_emplace(variant);
|
||||
auto& program = entry->second;
|
||||
if (is_cache_miss) {
|
||||
program = TryLoadProgram(variant);
|
||||
if (!program) {
|
||||
program = SpecializeShader(code, entries, program_type, variant);
|
||||
disk_cache.SaveUsage(GetUsage(variant));
|
||||
}
|
||||
|
||||
LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
|
||||
const auto [entry, is_cache_miss] = programs.try_emplace(variant);
|
||||
auto& program = entry->second;
|
||||
if (is_cache_miss) {
|
||||
program = TryLoadProgram(variant);
|
||||
if (!program) {
|
||||
program = SpecializeShader(code, entries, program_type, variant);
|
||||
disk_cache.SaveUsage(GetUsage(variant));
|
||||
}
|
||||
|
||||
handle = program->handle;
|
||||
LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
|
||||
}
|
||||
|
||||
auto base_bindings = variant.base_bindings;
|
||||
@@ -375,52 +369,9 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVar
|
||||
base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size());
|
||||
base_bindings.sampler += static_cast<u32>(entries.samplers.size());
|
||||
|
||||
return {handle, base_bindings};
|
||||
return {program->handle, base_bindings};
|
||||
}
|
||||
|
||||
GLuint CachedShader::GetGeometryShader(const ProgramVariant& variant) {
|
||||
const auto [entry, is_cache_miss] = geometry_programs.try_emplace(variant);
|
||||
auto& programs = entry->second;
|
||||
|
||||
switch (variant.primitive_mode) {
|
||||
case GL_POINTS:
|
||||
return LazyGeometryProgram(programs.points, variant);
|
||||
case GL_LINES:
|
||||
case GL_LINE_STRIP:
|
||||
return LazyGeometryProgram(programs.lines, variant);
|
||||
case GL_LINES_ADJACENCY:
|
||||
case GL_LINE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(programs.lines_adjacency, variant);
|
||||
case GL_TRIANGLES:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
case GL_TRIANGLE_FAN:
|
||||
return LazyGeometryProgram(programs.triangles, variant);
|
||||
case GL_TRIANGLES_ADJACENCY:
|
||||
case GL_TRIANGLE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(programs.triangles_adjacency, variant);
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown primitive mode.");
|
||||
return LazyGeometryProgram(programs.points, variant);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program,
|
||||
const ProgramVariant& variant) {
|
||||
if (target_program) {
|
||||
return target_program->handle;
|
||||
}
|
||||
const auto [glsl_name, debug_name, vertices] = GetPrimitiveDescription(variant.primitive_mode);
|
||||
target_program = TryLoadProgram(variant);
|
||||
if (!target_program) {
|
||||
target_program = SpecializeShader(code, entries, program_type, variant);
|
||||
disk_cache.SaveUsage(GetUsage(variant));
|
||||
}
|
||||
|
||||
LabelGLObject(GL_PROGRAM, target_program->handle, cpu_addr, debug_name);
|
||||
|
||||
return target_program->handle;
|
||||
};
|
||||
|
||||
CachedProgram CachedShader::TryLoadProgram(const ProgramVariant& variant) const {
|
||||
const auto found = precompiled_programs.find(GetUsage(variant));
|
||||
if (found == precompiled_programs.end()) {
|
||||
|
||||
@@ -86,22 +86,6 @@ private:
|
||||
explicit CachedShader(const ShaderParameters& params, ProgramType program_type,
|
||||
GLShader::ProgramResult result);
|
||||
|
||||
// Geometry programs. These are needed because GLSL needs an input topology but it's not
|
||||
// declared by the hardware. Workaround this issue by generating a different shader per input
|
||||
// topology class.
|
||||
struct GeometryPrograms {
|
||||
CachedProgram points;
|
||||
CachedProgram lines;
|
||||
CachedProgram lines_adjacency;
|
||||
CachedProgram triangles;
|
||||
CachedProgram triangles_adjacency;
|
||||
};
|
||||
|
||||
GLuint GetGeometryShader(const ProgramVariant& variant);
|
||||
|
||||
/// Generates a geometry shader or returns one that already exists.
|
||||
GLuint LazyGeometryProgram(CachedProgram& target_program, const ProgramVariant& variant);
|
||||
|
||||
CachedProgram TryLoadProgram(const ProgramVariant& variant) const;
|
||||
|
||||
ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant) const;
|
||||
@@ -117,11 +101,6 @@ private:
|
||||
std::size_t shader_length{};
|
||||
|
||||
std::unordered_map<ProgramVariant, CachedProgram> programs;
|
||||
std::unordered_map<ProgramVariant, GeometryPrograms> geometry_programs;
|
||||
|
||||
std::unordered_map<u32, GLuint> cbuf_resource_cache;
|
||||
std::unordered_map<u32, GLuint> gmem_resource_cache;
|
||||
std::unordered_map<u32, GLint> uniform_cache;
|
||||
};
|
||||
|
||||
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
|
||||
|
||||
@@ -325,6 +325,7 @@ public:
|
||||
DeclareRegisters();
|
||||
DeclarePredicates();
|
||||
DeclareLocalMemory();
|
||||
DeclareSharedMemory();
|
||||
DeclareInternalFlags();
|
||||
DeclareInputAttributes();
|
||||
DeclareOutputAttributes();
|
||||
@@ -389,11 +390,10 @@ public:
|
||||
for (const auto& sampler : ir.GetSamplers()) {
|
||||
entries.samplers.emplace_back(sampler);
|
||||
}
|
||||
for (const auto& image : ir.GetImages()) {
|
||||
for (const auto& [offset, image] : ir.GetImages()) {
|
||||
entries.images.emplace_back(image);
|
||||
}
|
||||
for (const auto& gmem_pair : ir.GetGlobalMemory()) {
|
||||
const auto& [base, usage] = gmem_pair;
|
||||
for (const auto& [base, usage] : ir.GetGlobalMemory()) {
|
||||
entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset,
|
||||
usage.is_read, usage.is_written);
|
||||
}
|
||||
@@ -500,6 +500,13 @@ private:
|
||||
code.AddNewLine();
|
||||
}
|
||||
|
||||
void DeclareSharedMemory() {
|
||||
if (stage != ProgramType::Compute) {
|
||||
return;
|
||||
}
|
||||
code.AddLine("shared uint {}[];", GetSharedMemory());
|
||||
}
|
||||
|
||||
void DeclareInternalFlags() {
|
||||
for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
|
||||
const auto flag_code = static_cast<InternalFlag>(flag);
|
||||
@@ -706,8 +713,8 @@ private:
|
||||
|
||||
void DeclareImages() {
|
||||
const auto& images{ir.GetImages()};
|
||||
for (const auto& image : images) {
|
||||
const std::string image_type = [&]() {
|
||||
for (const auto& [offset, image] : images) {
|
||||
const char* image_type = [&] {
|
||||
switch (image.GetType()) {
|
||||
case Tegra::Shader::ImageType::Texture1D:
|
||||
return "image1D";
|
||||
@@ -726,9 +733,33 @@ private:
|
||||
return "image1D";
|
||||
}
|
||||
}();
|
||||
code.AddLine("layout (binding = IMAGE_BINDING_{}) coherent volatile writeonly uniform "
|
||||
|
||||
const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
|
||||
if (!image.IsSizeKnown()) {
|
||||
return {"", ""};
|
||||
}
|
||||
switch (image.GetSize()) {
|
||||
case Tegra::Shader::ImageAtomicSize::U32:
|
||||
return {"u", "r32ui, "};
|
||||
case Tegra::Shader::ImageAtomicSize::S32:
|
||||
return {"i", "r32i, "};
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
|
||||
static_cast<u32>(image.GetSize()));
|
||||
return {"", ""};
|
||||
}
|
||||
}();
|
||||
|
||||
std::string qualifier = "coherent volatile";
|
||||
if (image.IsRead() && !image.IsWritten()) {
|
||||
qualifier += " readonly";
|
||||
} else if (image.IsWritten() && !image.IsRead()) {
|
||||
qualifier += " writeonly";
|
||||
}
|
||||
|
||||
code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform "
|
||||
"{} {};",
|
||||
image.GetIndex(), image_type, GetImage(image));
|
||||
image.GetIndex(), qualifier, image_type, GetImage(image));
|
||||
}
|
||||
if (!images.empty()) {
|
||||
code.AddNewLine();
|
||||
@@ -858,6 +889,12 @@ private:
|
||||
Type::Uint};
|
||||
}
|
||||
|
||||
if (const auto smem = std::get_if<SmemNode>(&*node)) {
|
||||
return {
|
||||
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
|
||||
Type::Uint};
|
||||
}
|
||||
|
||||
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
||||
return {GetInternalFlag(internal_flag->GetFlag()), Type::Bool};
|
||||
}
|
||||
@@ -984,10 +1021,10 @@ private:
|
||||
return {std::move(temporary), value.GetType()};
|
||||
}
|
||||
|
||||
Expression GetOutputAttribute(const AbufNode* abuf) {
|
||||
std::optional<Expression> GetOutputAttribute(const AbufNode* abuf) {
|
||||
switch (const auto attribute = abuf->GetIndex()) {
|
||||
case Attribute::Index::Position:
|
||||
return {"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float};
|
||||
return {{"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float}};
|
||||
case Attribute::Index::LayerViewportPointSize:
|
||||
switch (abuf->GetElement()) {
|
||||
case 0:
|
||||
@@ -997,25 +1034,25 @@ private:
|
||||
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
|
||||
return {};
|
||||
}
|
||||
return {"gl_Layer", Type::Int};
|
||||
return {{"gl_Layer", Type::Int}};
|
||||
case 2:
|
||||
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
|
||||
return {};
|
||||
}
|
||||
return {"gl_ViewportIndex", Type::Int};
|
||||
return {{"gl_ViewportIndex", Type::Int}};
|
||||
case 3:
|
||||
UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader");
|
||||
return {"gl_PointSize", Type::Float};
|
||||
return {{"gl_PointSize", Type::Float}};
|
||||
}
|
||||
return {};
|
||||
case Attribute::Index::ClipDistances0123:
|
||||
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float};
|
||||
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float}};
|
||||
case Attribute::Index::ClipDistances4567:
|
||||
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float};
|
||||
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}};
|
||||
default:
|
||||
if (IsGenericAttribute(attribute)) {
|
||||
return {GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()),
|
||||
Type::Float};
|
||||
return {
|
||||
{GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()), Type::Float}};
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
|
||||
return {};
|
||||
@@ -1174,6 +1211,74 @@ private:
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string BuildIntegerCoordinates(Operation operation) {
|
||||
constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
|
||||
const std::size_t coords_count{operation.GetOperandsCount()};
|
||||
std::string expr = constructors.at(coords_count - 1);
|
||||
for (std::size_t i = 0; i < coords_count; ++i) {
|
||||
expr += VisitOperand(operation, i).AsInt();
|
||||
if (i + 1 < coords_count) {
|
||||
expr += ", ";
|
||||
}
|
||||
}
|
||||
expr += ')';
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string BuildImageValues(Operation operation) {
|
||||
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
|
||||
constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
|
||||
if (!meta.image.IsSizeKnown()) {
|
||||
return {float_constructors, Type::Float};
|
||||
}
|
||||
switch (meta.image.GetSize()) {
|
||||
case Tegra::Shader::ImageAtomicSize::U32:
|
||||
return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
|
||||
case Tegra::Shader::ImageAtomicSize::S32:
|
||||
return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented image size={}",
|
||||
static_cast<u32>(meta.image.GetSize()));
|
||||
return {float_constructors, Type::Float};
|
||||
}
|
||||
}();
|
||||
|
||||
const std::size_t values_count{meta.values.size()};
|
||||
std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
|
||||
for (std::size_t i = 0; i < values_count; ++i) {
|
||||
expr += Visit(meta.values.at(i)).As(type);
|
||||
if (i + 1 < values_count) {
|
||||
expr += ", ";
|
||||
}
|
||||
}
|
||||
expr += ')';
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expression AtomicImage(Operation operation, const char* opname) {
|
||||
constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
|
||||
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
ASSERT(meta.values.size() == 1);
|
||||
ASSERT(meta.image.IsSizeKnown());
|
||||
|
||||
const auto type = [&]() {
|
||||
switch (const auto size = meta.image.GetSize()) {
|
||||
case Tegra::Shader::ImageAtomicSize::U32:
|
||||
return Type::Uint;
|
||||
case Tegra::Shader::ImageAtomicSize::S32:
|
||||
return Type::Int;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
|
||||
return Type::Uint;
|
||||
}
|
||||
}();
|
||||
|
||||
return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
|
||||
BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
|
||||
type};
|
||||
}
|
||||
|
||||
Expression Assign(Operation operation) {
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
@@ -1187,7 +1292,11 @@ private:
|
||||
target = {GetRegister(gpr->GetIndex()), Type::Float};
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
|
||||
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
|
||||
target = GetOutputAttribute(abuf);
|
||||
auto output = GetOutputAttribute(abuf);
|
||||
if (!output) {
|
||||
return {};
|
||||
}
|
||||
target = std::move(*output);
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
|
||||
if (stage == ProgramType::Compute) {
|
||||
LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
|
||||
@@ -1195,6 +1304,11 @@ private:
|
||||
target = {
|
||||
fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
|
||||
Type::Uint};
|
||||
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
|
||||
ASSERT(stage == ProgramType::Compute);
|
||||
target = {
|
||||
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
|
||||
Type::Uint};
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
||||
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
|
||||
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
|
||||
@@ -1688,38 +1802,39 @@ private:
|
||||
}
|
||||
|
||||
Expression ImageStore(Operation operation) {
|
||||
constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
|
||||
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
|
||||
std::string expr = "imageStore(";
|
||||
expr += GetImage(meta.image);
|
||||
expr += ", ";
|
||||
|
||||
const std::size_t coords_count{operation.GetOperandsCount()};
|
||||
expr += constructors.at(coords_count - 1);
|
||||
for (std::size_t i = 0; i < coords_count; ++i) {
|
||||
expr += VisitOperand(operation, i).AsInt();
|
||||
if (i + 1 < coords_count) {
|
||||
expr += ", ";
|
||||
}
|
||||
}
|
||||
expr += "), ";
|
||||
|
||||
const std::size_t values_count{meta.values.size()};
|
||||
UNIMPLEMENTED_IF(values_count != 4);
|
||||
expr += "vec4(";
|
||||
for (std::size_t i = 0; i < values_count; ++i) {
|
||||
expr += Visit(meta.values.at(i)).AsFloat();
|
||||
if (i + 1 < values_count) {
|
||||
expr += ", ";
|
||||
}
|
||||
}
|
||||
expr += "));";
|
||||
|
||||
code.AddLine(expr);
|
||||
code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
|
||||
BuildIntegerCoordinates(operation), BuildImageValues(operation));
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression AtomicImageAdd(Operation operation) {
|
||||
return AtomicImage(operation, "imageAtomicAdd");
|
||||
}
|
||||
|
||||
Expression AtomicImageMin(Operation operation) {
|
||||
return AtomicImage(operation, "imageAtomicMin");
|
||||
}
|
||||
|
||||
Expression AtomicImageMax(Operation operation) {
|
||||
return AtomicImage(operation, "imageAtomicMax");
|
||||
}
|
||||
Expression AtomicImageAnd(Operation operation) {
|
||||
return AtomicImage(operation, "imageAtomicAnd");
|
||||
}
|
||||
|
||||
Expression AtomicImageOr(Operation operation) {
|
||||
return AtomicImage(operation, "imageAtomicOr");
|
||||
}
|
||||
|
||||
Expression AtomicImageXor(Operation operation) {
|
||||
return AtomicImage(operation, "imageAtomicXor");
|
||||
}
|
||||
|
||||
Expression AtomicImageExchange(Operation operation) {
|
||||
return AtomicImage(operation, "imageAtomicExchange");
|
||||
}
|
||||
|
||||
Expression Branch(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
@@ -1842,8 +1957,7 @@ private:
|
||||
Expression BallotThread(Operation operation) {
|
||||
const std::string value = VisitOperand(operation, 0).AsBool();
|
||||
if (!device.HasWarpIntrinsics()) {
|
||||
LOG_ERROR(Render_OpenGL,
|
||||
"Nvidia warp intrinsics are not available and its required by a shader");
|
||||
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
|
||||
// Stub on non-Nvidia devices by simulating all threads voting the same as the active
|
||||
// one.
|
||||
return {fmt::format("({} ? 0xFFFFFFFFU : 0U)", value), Type::Uint};
|
||||
@@ -1854,8 +1968,7 @@ private:
|
||||
Expression Vote(Operation operation, const char* func) {
|
||||
const std::string value = VisitOperand(operation, 0).AsBool();
|
||||
if (!device.HasWarpIntrinsics()) {
|
||||
LOG_ERROR(Render_OpenGL,
|
||||
"Nvidia vote intrinsics are not available and its required by a shader");
|
||||
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
|
||||
// Stub with a warp size of one.
|
||||
return {value, Type::Bool};
|
||||
}
|
||||
@@ -1872,15 +1985,54 @@ private:
|
||||
|
||||
Expression VoteEqual(Operation operation) {
|
||||
if (!device.HasWarpIntrinsics()) {
|
||||
LOG_ERROR(Render_OpenGL,
|
||||
"Nvidia vote intrinsics are not available and its required by a shader");
|
||||
// We must return true here since a stub for a theoretical warp size of 1 will always
|
||||
// return an equal result for all its votes.
|
||||
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
|
||||
// We must return true here since a stub for a theoretical warp size of 1.
|
||||
// This will always return an equal result across all votes.
|
||||
return {"true", Type::Bool};
|
||||
}
|
||||
return Vote(operation, "allThreadsEqualNV");
|
||||
}
|
||||
|
||||
template <const std::string_view& func>
|
||||
Expression Shuffle(Operation operation) {
|
||||
const std::string value = VisitOperand(operation, 0).AsFloat();
|
||||
if (!device.HasWarpIntrinsics()) {
|
||||
LOG_ERROR(Render_OpenGL, "Nvidia shuffle intrinsics are required by this shader");
|
||||
// On a "single-thread" device we are either on the same thread or out of bounds. Both
|
||||
// cases return the passed value.
|
||||
return {value, Type::Float};
|
||||
}
|
||||
|
||||
const std::string index = VisitOperand(operation, 1).AsUint();
|
||||
const std::string width = VisitOperand(operation, 2).AsUint();
|
||||
return {fmt::format("{}({}, {}, {})", func, value, index, width), Type::Float};
|
||||
}
|
||||
|
||||
template <const std::string_view& func>
|
||||
Expression InRangeShuffle(Operation operation) {
|
||||
const std::string index = VisitOperand(operation, 0).AsUint();
|
||||
const std::string width = VisitOperand(operation, 1).AsUint();
|
||||
if (!device.HasWarpIntrinsics()) {
|
||||
// On a "single-thread" device we are only in bounds when the requested index is 0.
|
||||
return {fmt::format("({} == 0U)", index), Type::Bool};
|
||||
}
|
||||
|
||||
const std::string in_range = code.GenerateTemporary();
|
||||
code.AddLine("bool {};", in_range);
|
||||
code.AddLine("{}(0U, {}, {}, {});", func, index, width, in_range);
|
||||
return {in_range, Type::Bool};
|
||||
}
|
||||
|
||||
struct Func final {
|
||||
Func() = delete;
|
||||
~Func() = delete;
|
||||
|
||||
static constexpr std::string_view ShuffleIndexed = "shuffleNV";
|
||||
static constexpr std::string_view ShuffleUp = "shuffleUpNV";
|
||||
static constexpr std::string_view ShuffleDown = "shuffleDownNV";
|
||||
static constexpr std::string_view ShuffleButterfly = "shuffleXorNV";
|
||||
};
|
||||
|
||||
static constexpr std::array operation_decompilers = {
|
||||
&GLSLDecompiler::Assign,
|
||||
|
||||
@@ -2013,6 +2165,13 @@ private:
|
||||
&GLSLDecompiler::TexelFetch,
|
||||
|
||||
&GLSLDecompiler::ImageStore,
|
||||
&GLSLDecompiler::AtomicImageAdd,
|
||||
&GLSLDecompiler::AtomicImageMin,
|
||||
&GLSLDecompiler::AtomicImageMax,
|
||||
&GLSLDecompiler::AtomicImageAnd,
|
||||
&GLSLDecompiler::AtomicImageOr,
|
||||
&GLSLDecompiler::AtomicImageXor,
|
||||
&GLSLDecompiler::AtomicImageExchange,
|
||||
|
||||
&GLSLDecompiler::Branch,
|
||||
&GLSLDecompiler::BranchIndirect,
|
||||
@@ -2036,6 +2195,16 @@ private:
|
||||
&GLSLDecompiler::VoteAll,
|
||||
&GLSLDecompiler::VoteAny,
|
||||
&GLSLDecompiler::VoteEqual,
|
||||
|
||||
&GLSLDecompiler::Shuffle<Func::ShuffleIndexed>,
|
||||
&GLSLDecompiler::Shuffle<Func::ShuffleUp>,
|
||||
&GLSLDecompiler::Shuffle<Func::ShuffleDown>,
|
||||
&GLSLDecompiler::Shuffle<Func::ShuffleButterfly>,
|
||||
|
||||
&GLSLDecompiler::InRangeShuffle<Func::ShuffleIndexed>,
|
||||
&GLSLDecompiler::InRangeShuffle<Func::ShuffleUp>,
|
||||
&GLSLDecompiler::InRangeShuffle<Func::ShuffleDown>,
|
||||
&GLSLDecompiler::InRangeShuffle<Func::ShuffleButterfly>,
|
||||
};
|
||||
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
|
||||
|
||||
@@ -2076,6 +2245,10 @@ private:
|
||||
return "lmem_" + suffix;
|
||||
}
|
||||
|
||||
std::string GetSharedMemory() const {
|
||||
return fmt::format("smem_{}", suffix);
|
||||
}
|
||||
|
||||
std::string GetInternalFlag(InternalFlag flag) const {
|
||||
constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
|
||||
"overflow_flag"};
|
||||
|
||||
@@ -341,13 +341,22 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
u64 index{};
|
||||
u32 type{};
|
||||
u8 is_bindless{};
|
||||
u8 is_written{};
|
||||
u8 is_read{};
|
||||
u8 is_size_known{};
|
||||
u32 size{};
|
||||
if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
|
||||
!LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless)) {
|
||||
!LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
|
||||
!LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) ||
|
||||
!LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) {
|
||||
return {};
|
||||
}
|
||||
entry.entries.images.emplace_back(
|
||||
static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
|
||||
static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0);
|
||||
static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0,
|
||||
is_read != 0,
|
||||
is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size))
|
||||
: std::nullopt);
|
||||
}
|
||||
|
||||
u32 global_memory_count{};
|
||||
@@ -426,10 +435,14 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
|
||||
return false;
|
||||
}
|
||||
for (const auto& image : entries.images) {
|
||||
const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U;
|
||||
if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0))) {
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
|
||||
!SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace OpenGL {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
OpenGLState OpenGLState::cur_state;
|
||||
bool OpenGLState::s_rgb_used;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -34,6 +33,25 @@ bool UpdateTie(T1 current_value, const T2 new_value) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::pair<GLuint, GLsizei>> UpdateArray(T& current_values, const T& new_values) {
|
||||
std::optional<std::size_t> first;
|
||||
std::size_t last;
|
||||
for (std::size_t i = 0; i < std::size(current_values); ++i) {
|
||||
if (!UpdateValue(current_values[i], new_values[i])) {
|
||||
continue;
|
||||
}
|
||||
if (!first) {
|
||||
first = i;
|
||||
}
|
||||
last = i;
|
||||
}
|
||||
if (!first) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_pair(static_cast<GLuint>(*first), static_cast<GLsizei>(last - *first + 1));
|
||||
}
|
||||
|
||||
void Enable(GLenum cap, bool enable) {
|
||||
if (enable) {
|
||||
glEnable(cap);
|
||||
@@ -134,10 +152,6 @@ OpenGLState::OpenGLState() {
|
||||
logic_op.enabled = false;
|
||||
logic_op.operation = GL_COPY;
|
||||
|
||||
for (auto& texture_unit : texture_units) {
|
||||
texture_unit.Reset();
|
||||
}
|
||||
|
||||
draw.read_framebuffer = 0;
|
||||
draw.draw_framebuffer = 0;
|
||||
draw.vertex_array = 0;
|
||||
@@ -267,8 +281,6 @@ void OpenGLState::ApplySRgb() const {
|
||||
return;
|
||||
cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled;
|
||||
if (framebuffer_srgb.enabled) {
|
||||
// Track if sRGB is used
|
||||
s_rgb_used = true;
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
} else {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
@@ -496,52 +508,20 @@ void OpenGLState::ApplyAlphaTest() const {
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTextures() const {
|
||||
bool has_delta{};
|
||||
std::size_t first{};
|
||||
std::size_t last{};
|
||||
std::array<GLuint, Maxwell::NumTextureSamplers> textures;
|
||||
|
||||
for (std::size_t i = 0; i < std::size(texture_units); ++i) {
|
||||
const auto& texture_unit = texture_units[i];
|
||||
auto& cur_state_texture_unit = cur_state.texture_units[i];
|
||||
textures[i] = texture_unit.texture;
|
||||
if (cur_state_texture_unit.texture == textures[i]) {
|
||||
continue;
|
||||
}
|
||||
cur_state_texture_unit.texture = textures[i];
|
||||
if (!has_delta) {
|
||||
first = i;
|
||||
has_delta = true;
|
||||
}
|
||||
last = i;
|
||||
}
|
||||
if (has_delta) {
|
||||
glBindTextures(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
|
||||
textures.data() + first);
|
||||
if (const auto update = UpdateArray(cur_state.textures, textures)) {
|
||||
glBindTextures(update->first, update->second, textures.data() + update->first);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplySamplers() const {
|
||||
bool has_delta{};
|
||||
std::size_t first{};
|
||||
std::size_t last{};
|
||||
std::array<GLuint, Maxwell::NumTextureSamplers> samplers;
|
||||
|
||||
for (std::size_t i = 0; i < std::size(samplers); ++i) {
|
||||
samplers[i] = texture_units[i].sampler;
|
||||
if (cur_state.texture_units[i].sampler == texture_units[i].sampler) {
|
||||
continue;
|
||||
}
|
||||
cur_state.texture_units[i].sampler = texture_units[i].sampler;
|
||||
if (!has_delta) {
|
||||
first = i;
|
||||
has_delta = true;
|
||||
}
|
||||
last = i;
|
||||
if (const auto update = UpdateArray(cur_state.samplers, samplers)) {
|
||||
glBindSamplers(update->first, update->second, samplers.data() + update->first);
|
||||
}
|
||||
if (has_delta) {
|
||||
glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
|
||||
samplers.data() + first);
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyImages() const {
|
||||
if (const auto update = UpdateArray(cur_state.images, images)) {
|
||||
glBindImageTextures(update->first, update->second, images.data() + update->first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,6 +556,7 @@ void OpenGLState::Apply() {
|
||||
ApplyLogicOp();
|
||||
ApplyTextures();
|
||||
ApplySamplers();
|
||||
ApplyImages();
|
||||
if (dirty.polygon_offset) {
|
||||
ApplyPolygonOffset();
|
||||
dirty.polygon_offset = false;
|
||||
@@ -606,18 +587,18 @@ void OpenGLState::EmulateViewportWithScissor() {
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
if (unit.texture == handle) {
|
||||
unit.Unbind();
|
||||
for (auto& texture : textures) {
|
||||
if (texture == handle) {
|
||||
texture = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
if (unit.sampler == handle) {
|
||||
unit.sampler = 0;
|
||||
for (auto& sampler : samplers) {
|
||||
if (sampler == handle) {
|
||||
sampler = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
|
||||
@@ -118,21 +118,9 @@ public:
|
||||
GLenum operation;
|
||||
} logic_op;
|
||||
|
||||
// 3 texture units - one for each that is used in PICA fragment shader emulation
|
||||
struct TextureUnit {
|
||||
GLuint texture; // GL_TEXTURE_BINDING_2D
|
||||
GLuint sampler; // GL_SAMPLER_BINDING
|
||||
|
||||
void Unbind() {
|
||||
texture = 0;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
Unbind();
|
||||
sampler = 0;
|
||||
}
|
||||
};
|
||||
std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units;
|
||||
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> textures{};
|
||||
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers{};
|
||||
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumImages> images{};
|
||||
|
||||
struct {
|
||||
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
|
||||
@@ -187,14 +175,6 @@ public:
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
static bool GetsRGBUsed() {
|
||||
return s_rgb_used;
|
||||
}
|
||||
|
||||
static void ClearsRGBUsed() {
|
||||
s_rgb_used = false;
|
||||
}
|
||||
|
||||
void SetDefaultViewports();
|
||||
/// Apply this state as the current OpenGL state
|
||||
void Apply();
|
||||
@@ -220,6 +200,7 @@ public:
|
||||
void ApplyLogicOp() const;
|
||||
void ApplyTextures() const;
|
||||
void ApplySamplers() const;
|
||||
void ApplyImages() const;
|
||||
void ApplyDepthClamp() const;
|
||||
void ApplyPolygonOffset() const;
|
||||
void ApplyAlphaTest() const;
|
||||
@@ -264,8 +245,6 @@ public:
|
||||
private:
|
||||
static OpenGLState cur_state;
|
||||
|
||||
// Workaround for sRGB problems caused by QT not supporting srgb output
|
||||
static bool s_rgb_used;
|
||||
struct {
|
||||
bool blend_state;
|
||||
bool stencil_state;
|
||||
|
||||
@@ -78,6 +78,17 @@ public:
|
||||
/// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER
|
||||
void Attach(GLenum attachment, GLenum target) const;
|
||||
|
||||
void ApplySwizzle(Tegra::Texture::SwizzleSource x_source,
|
||||
Tegra::Texture::SwizzleSource y_source,
|
||||
Tegra::Texture::SwizzleSource z_source,
|
||||
Tegra::Texture::SwizzleSource w_source);
|
||||
|
||||
void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
|
||||
|
||||
void MarkAsModified(u64 tick) {
|
||||
surface.MarkAsModified(true, tick);
|
||||
}
|
||||
|
||||
GLuint GetTexture() const {
|
||||
if (is_proxy) {
|
||||
return surface.GetTexture();
|
||||
@@ -89,13 +100,6 @@ public:
|
||||
return surface.GetSurfaceParams();
|
||||
}
|
||||
|
||||
void ApplySwizzle(Tegra::Texture::SwizzleSource x_source,
|
||||
Tegra::Texture::SwizzleSource y_source,
|
||||
Tegra::Texture::SwizzleSource z_source,
|
||||
Tegra::Texture::SwizzleSource w_source);
|
||||
|
||||
void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
|
||||
|
||||
private:
|
||||
u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
|
||||
Tegra::Texture::SwizzleSource y_source,
|
||||
@@ -111,8 +115,8 @@ private:
|
||||
GLenum target{};
|
||||
|
||||
OGLTextureView texture_view;
|
||||
u32 swizzle;
|
||||
bool is_proxy;
|
||||
u32 swizzle{};
|
||||
bool is_proxy{};
|
||||
};
|
||||
|
||||
class TextureCacheOpenGL final : public TextureCacheBase {
|
||||
|
||||
@@ -145,7 +145,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
|
||||
case Tegra::Texture::TextureMipmapFilter::None:
|
||||
return GL_LINEAR;
|
||||
case Tegra::Texture::TextureMipmapFilter::Nearest:
|
||||
return GL_NEAREST_MIPMAP_LINEAR;
|
||||
return GL_LINEAR_MIPMAP_NEAREST;
|
||||
case Tegra::Texture::TextureMipmapFilter::Linear:
|
||||
return GL_LINEAR_MIPMAP_LINEAR;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
|
||||
case Tegra::Texture::TextureMipmapFilter::Nearest:
|
||||
return GL_NEAREST_MIPMAP_NEAREST;
|
||||
case Tegra::Texture::TextureMipmapFilter::Linear:
|
||||
return GL_LINEAR_MIPMAP_NEAREST;
|
||||
return GL_NEAREST_MIPMAP_LINEAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +264,6 @@ void RendererOpenGL::CreateRasterizer() {
|
||||
if (rasterizer) {
|
||||
return;
|
||||
}
|
||||
OpenGLState::ClearsRGBUsed();
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info);
|
||||
}
|
||||
|
||||
@@ -342,21 +341,17 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
|
||||
}};
|
||||
|
||||
state.texture_units[0].texture = screen_info.display_texture;
|
||||
// Workaround brigthness problems in SMO by enabling sRGB in the final output
|
||||
// if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
|
||||
state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
|
||||
state.textures[0] = screen_info.display_texture;
|
||||
state.framebuffer_srgb.enabled = screen_info.display_srgb;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), vertices.data());
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
// Restore default state
|
||||
state.framebuffer_srgb.enabled = false;
|
||||
state.texture_units[0].texture = 0;
|
||||
state.textures[0] = 0;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
// Clear sRGB state for the next frame
|
||||
OpenGLState::ClearsRGBUsed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,8 +401,8 @@ void RendererOpenGL::CaptureScreenshot() {
|
||||
GLuint renderbuffer;
|
||||
glGenRenderbuffers(1, &renderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, state.GetsRGBUsed() ? GL_SRGB8 : GL_RGB8, layout.width,
|
||||
layout.height);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, screen_info.display_srgb ? GL_SRGB8 : GL_RGB8,
|
||||
layout.width, layout.height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
|
||||
|
||||
DrawScreen(layout);
|
||||
|
||||
@@ -38,7 +38,8 @@ struct TextureInfo {
|
||||
|
||||
/// Structure used for storing information about the display target for the Switch screen
|
||||
struct ScreenInfo {
|
||||
GLuint display_texture;
|
||||
GLuint display_texture{};
|
||||
bool display_srgb{};
|
||||
const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
|
||||
TextureInfo texture;
|
||||
};
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
@@ -12,13 +13,32 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
void SetNext(void**& next, T& data) {
|
||||
*next = &data;
|
||||
next = &data.pNext;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetFeatures(vk::PhysicalDevice physical, vk::DispatchLoaderDynamic dldi) {
|
||||
vk::PhysicalDeviceFeatures2 features;
|
||||
T extension_features;
|
||||
features.pNext = &extension_features;
|
||||
physical.getFeatures2(&features, dldi);
|
||||
return extension_features;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Alternatives {
|
||||
|
||||
constexpr std::array<vk::Format, 3> Depth24UnormS8Uint = {
|
||||
vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint, {}};
|
||||
constexpr std::array<vk::Format, 3> Depth16UnormS8Uint = {
|
||||
vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint, {}};
|
||||
constexpr std::array<vk::Format, 2> Astc = {vk::Format::eA8B8G8R8UnormPack32, {}};
|
||||
constexpr std::array Depth24UnormS8Uint = {vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eD16UnormS8Uint, vk::Format{}};
|
||||
constexpr std::array Depth16UnormS8Uint = {vk::Format::eD24UnormS8Uint,
|
||||
vk::Format::eD32SfloatS8Uint, vk::Format{}};
|
||||
constexpr std::array Astc = {vk::Format::eA8B8G8R8UnormPack32, vk::Format{}};
|
||||
|
||||
} // namespace Alternatives
|
||||
|
||||
@@ -58,16 +78,53 @@ VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice phy
|
||||
VKDevice::~VKDevice() = default;
|
||||
|
||||
bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) {
|
||||
vk::PhysicalDeviceFeatures device_features;
|
||||
device_features.vertexPipelineStoresAndAtomics = true;
|
||||
device_features.independentBlend = true;
|
||||
device_features.textureCompressionASTC_LDR = is_optimal_astc_supported;
|
||||
|
||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||
const std::vector<const char*> extensions = LoadExtensions(dldi);
|
||||
const vk::DeviceCreateInfo device_ci({}, static_cast<u32>(queue_cis.size()), queue_cis.data(),
|
||||
0, nullptr, static_cast<u32>(extensions.size()),
|
||||
extensions.data(), &device_features);
|
||||
const std::vector extensions = LoadExtensions(dldi);
|
||||
|
||||
vk::PhysicalDeviceFeatures2 features2;
|
||||
void** next = &features2.pNext;
|
||||
auto& features = features2.features;
|
||||
features.vertexPipelineStoresAndAtomics = true;
|
||||
features.independentBlend = true;
|
||||
features.depthClamp = true;
|
||||
features.samplerAnisotropy = true;
|
||||
features.largePoints = true;
|
||||
features.textureCompressionASTC_LDR = is_optimal_astc_supported;
|
||||
|
||||
vk::PhysicalDeviceVertexAttributeDivisorFeaturesEXT vertex_divisor;
|
||||
vertex_divisor.vertexAttributeInstanceRateDivisor = true;
|
||||
vertex_divisor.vertexAttributeInstanceRateZeroDivisor = true;
|
||||
SetNext(next, vertex_divisor);
|
||||
|
||||
vk::PhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
|
||||
if (is_float16_supported) {
|
||||
float16_int8.shaderFloat16 = true;
|
||||
SetNext(next, float16_int8);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
|
||||
}
|
||||
|
||||
vk::PhysicalDeviceUniformBufferStandardLayoutFeaturesKHR std430_layout;
|
||||
if (khr_uniform_buffer_standard_layout) {
|
||||
std430_layout.uniformBufferStandardLayout = true;
|
||||
SetNext(next, std430_layout);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support packed UBOs");
|
||||
}
|
||||
|
||||
vk::PhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8;
|
||||
if (ext_index_type_uint8) {
|
||||
index_type_uint8.indexTypeUint8 = true;
|
||||
SetNext(next, index_type_uint8);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
|
||||
}
|
||||
|
||||
vk::DeviceCreateInfo device_ci({}, static_cast<u32>(queue_cis.size()), queue_cis.data(), 0,
|
||||
nullptr, static_cast<u32>(extensions.size()), extensions.data(),
|
||||
nullptr);
|
||||
device_ci.pNext = &features2;
|
||||
|
||||
vk::Device dummy_logical;
|
||||
if (physical.createDevice(&device_ci, nullptr, &dummy_logical, dldi) != vk::Result::eSuccess) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!");
|
||||
@@ -78,6 +135,17 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan
|
||||
logical = UniqueDevice(
|
||||
dummy_logical, vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dld));
|
||||
|
||||
if (khr_driver_properties) {
|
||||
vk::PhysicalDeviceDriverPropertiesKHR driver;
|
||||
vk::PhysicalDeviceProperties2 properties;
|
||||
properties.pNext = &driver;
|
||||
physical.getProperties2(&properties, dld);
|
||||
driver_id = driver.driverID;
|
||||
LOG_INFO(Render_Vulkan, "Driver: {} {}", driver.driverName, driver.driverInfo);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Driver: Unknown");
|
||||
}
|
||||
|
||||
graphics_queue = logical->getQueue(graphics_family, 0, dld);
|
||||
present_queue = logical->getQueue(present_family, 0, dld);
|
||||
return true;
|
||||
@@ -92,20 +160,19 @@ vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
|
||||
// The wanted format is not supported by hardware, search for alternatives
|
||||
const vk::Format* alternatives = GetFormatAlternatives(wanted_format);
|
||||
if (alternatives == nullptr) {
|
||||
LOG_CRITICAL(Render_Vulkan,
|
||||
"Format={} with usage={} and type={} has no defined alternatives and host "
|
||||
"hardware does not support it",
|
||||
vk::to_string(wanted_format), vk::to_string(wanted_usage),
|
||||
static_cast<u32>(format_type));
|
||||
UNREACHABLE();
|
||||
UNREACHABLE_MSG("Format={} with usage={} and type={} has no defined alternatives and host "
|
||||
"hardware does not support it",
|
||||
vk::to_string(wanted_format), vk::to_string(wanted_usage),
|
||||
static_cast<u32>(format_type));
|
||||
return wanted_format;
|
||||
}
|
||||
|
||||
std::size_t i = 0;
|
||||
for (vk::Format alternative = alternatives[0]; alternative != vk::Format{};
|
||||
alternative = alternatives[++i]) {
|
||||
if (!IsFormatSupported(alternative, wanted_usage, format_type))
|
||||
if (!IsFormatSupported(alternative, wanted_usage, format_type)) {
|
||||
continue;
|
||||
}
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Emulating format={} with alternative format={} with usage={} and type={}",
|
||||
static_cast<u32>(wanted_format), static_cast<u32>(alternative),
|
||||
@@ -114,12 +181,10 @@ vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
|
||||
}
|
||||
|
||||
// No alternatives found, panic
|
||||
LOG_CRITICAL(Render_Vulkan,
|
||||
"Format={} with usage={} and type={} is not supported by the host hardware and "
|
||||
"doesn't support any of the alternatives",
|
||||
static_cast<u32>(wanted_format), static_cast<u32>(wanted_usage),
|
||||
static_cast<u32>(format_type));
|
||||
UNREACHABLE();
|
||||
UNREACHABLE_MSG("Format={} with usage={} and type={} is not supported by the host hardware and "
|
||||
"doesn't support any of the alternatives",
|
||||
static_cast<u32>(wanted_format), static_cast<u32>(wanted_usage),
|
||||
static_cast<u32>(format_type));
|
||||
return wanted_format;
|
||||
}
|
||||
|
||||
@@ -132,7 +197,7 @@ bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features
|
||||
vk::FormatFeatureFlagBits::eSampledImage | vk::FormatFeatureFlagBits::eBlitSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc |
|
||||
vk::FormatFeatureFlagBits::eTransferDst};
|
||||
constexpr std::array<vk::Format, 9> astc_formats = {
|
||||
constexpr std::array astc_formats = {
|
||||
vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock, vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock, vk::Format::eAstc5x5UnormBlock,
|
||||
@@ -151,76 +216,120 @@ bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlag
|
||||
FormatType format_type) const {
|
||||
const auto it = format_properties.find(wanted_format);
|
||||
if (it == format_properties.end()) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Unimplemented format query={}", vk::to_string(wanted_format));
|
||||
UNREACHABLE();
|
||||
UNIMPLEMENTED_MSG("Unimplemented format query={}", vk::to_string(wanted_format));
|
||||
return true;
|
||||
}
|
||||
const vk::FormatFeatureFlags supported_usage = GetFormatFeatures(it->second, format_type);
|
||||
const auto supported_usage = GetFormatFeatures(it->second, format_type);
|
||||
return (supported_usage & wanted_usage) == wanted_usage;
|
||||
}
|
||||
|
||||
bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
||||
vk::SurfaceKHR surface) {
|
||||
bool has_swapchain{};
|
||||
LOG_INFO(Render_Vulkan, "{}", physical.getProperties(dldi).deviceName);
|
||||
bool is_suitable = true;
|
||||
|
||||
constexpr std::array required_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME};
|
||||
std::bitset<required_extensions.size()> available_extensions{};
|
||||
|
||||
for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
||||
has_swapchain |= prop.extensionName == std::string(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
for (std::size_t i = 0; i < required_extensions.size(); ++i) {
|
||||
if (available_extensions[i]) {
|
||||
continue;
|
||||
}
|
||||
available_extensions[i] =
|
||||
required_extensions[i] == std::string_view{prop.extensionName};
|
||||
}
|
||||
}
|
||||
if (!has_swapchain) {
|
||||
// The device doesn't support creating swapchains.
|
||||
return false;
|
||||
if (!available_extensions.all()) {
|
||||
for (std::size_t i = 0; i < required_extensions.size(); ++i) {
|
||||
if (available_extensions[i]) {
|
||||
continue;
|
||||
}
|
||||
LOG_INFO(Render_Vulkan, "Missing required extension: {}", required_extensions[i]);
|
||||
is_suitable = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_graphics{}, has_present{};
|
||||
const auto queue_family_properties = physical.getQueueFamilyProperties(dldi);
|
||||
for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
|
||||
const auto& family = queue_family_properties[i];
|
||||
if (family.queueCount == 0)
|
||||
if (family.queueCount == 0) {
|
||||
continue;
|
||||
|
||||
}
|
||||
has_graphics |=
|
||||
(family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlagBits>(0);
|
||||
has_present |= physical.getSurfaceSupportKHR(i, surface, dldi) != 0;
|
||||
}
|
||||
if (!has_graphics || !has_present) {
|
||||
// The device doesn't have a graphics and present queue.
|
||||
return false;
|
||||
LOG_INFO(Render_Vulkan, "Device lacks a graphics and present queue");
|
||||
is_suitable = false;
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Check if the device matches all requeriments.
|
||||
const auto properties{physical.getProperties(dldi)};
|
||||
const auto limits{properties.limits};
|
||||
if (limits.maxUniformBufferRange < 65536) {
|
||||
return false;
|
||||
const auto& limits{properties.limits};
|
||||
|
||||
constexpr u32 required_ubo_size = 65536;
|
||||
if (limits.maxUniformBufferRange < required_ubo_size) {
|
||||
LOG_INFO(Render_Vulkan, "Device UBO size {} is too small, {} is required)",
|
||||
limits.maxUniformBufferRange, required_ubo_size);
|
||||
is_suitable = false;
|
||||
}
|
||||
|
||||
const vk::PhysicalDeviceFeatures features{physical.getFeatures(dldi)};
|
||||
if (!features.vertexPipelineStoresAndAtomics || !features.independentBlend) {
|
||||
return false;
|
||||
const auto features{physical.getFeatures(dldi)};
|
||||
const std::array feature_report = {
|
||||
std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
|
||||
std::make_pair(features.independentBlend, "independentBlend"),
|
||||
std::make_pair(features.depthClamp, "depthClamp"),
|
||||
std::make_pair(features.samplerAnisotropy, "samplerAnisotropy"),
|
||||
std::make_pair(features.largePoints, "largePoints"),
|
||||
};
|
||||
for (const auto& [supported, name] : feature_report) {
|
||||
if (supported) {
|
||||
continue;
|
||||
}
|
||||
LOG_INFO(Render_Vulkan, "Missing required feature: {}", name);
|
||||
is_suitable = false;
|
||||
}
|
||||
|
||||
// Device is suitable.
|
||||
return true;
|
||||
return is_suitable;
|
||||
}
|
||||
|
||||
std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynamic& dldi) {
|
||||
std::vector<const char*> extensions;
|
||||
extensions.reserve(2);
|
||||
extensions.reserve(7);
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
extensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
|
||||
|
||||
const auto Test = [&](const vk::ExtensionProperties& extension,
|
||||
std::optional<std::reference_wrapper<bool>> status, const char* name,
|
||||
u32 revision) {
|
||||
if (extension.extensionName != std::string(name)) {
|
||||
bool push) {
|
||||
if (extension.extensionName != std::string_view(name)) {
|
||||
return;
|
||||
}
|
||||
extensions.push_back(name);
|
||||
if (push) {
|
||||
extensions.push_back(name);
|
||||
}
|
||||
if (status) {
|
||||
status->get() = true;
|
||||
}
|
||||
};
|
||||
|
||||
bool khr_shader_float16_int8{};
|
||||
for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
||||
Test(extension, ext_scalar_block_layout, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, 1);
|
||||
Test(extension, khr_uniform_buffer_standard_layout,
|
||||
VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
|
||||
Test(extension, ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
|
||||
Test(extension, khr_driver_properties, VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, true);
|
||||
Test(extension, khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
|
||||
}
|
||||
|
||||
if (khr_shader_float16_int8) {
|
||||
is_float16_supported =
|
||||
GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dldi).shaderFloat16;
|
||||
extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
return extensions;
|
||||
@@ -250,9 +359,10 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK
|
||||
}
|
||||
|
||||
void VKDevice::SetupProperties(const vk::DispatchLoaderDynamic& dldi) {
|
||||
const vk::PhysicalDeviceProperties props = physical.getProperties(dldi);
|
||||
const auto props = physical.getProperties(dldi);
|
||||
device_type = props.deviceType;
|
||||
uniform_buffer_alignment = static_cast<u64>(props.limits.minUniformBufferOffsetAlignment);
|
||||
storage_buffer_alignment = static_cast<u64>(props.limits.minStorageBufferOffsetAlignment);
|
||||
max_storage_buffer_range = static_cast<u64>(props.limits.maxStorageBufferRange);
|
||||
}
|
||||
|
||||
@@ -273,42 +383,53 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con
|
||||
return queue_cis;
|
||||
}
|
||||
|
||||
std::map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
|
||||
std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
|
||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) {
|
||||
static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32,
|
||||
vk::Format::eB5G6R5UnormPack16,
|
||||
vk::Format::eA2B10G10R10UnormPack32,
|
||||
vk::Format::eR32G32B32A32Sfloat,
|
||||
vk::Format::eR16G16Unorm,
|
||||
vk::Format::eR16G16Snorm,
|
||||
vk::Format::eR8G8B8A8Srgb,
|
||||
vk::Format::eR8Unorm,
|
||||
vk::Format::eB10G11R11UfloatPack32,
|
||||
vk::Format::eR32Sfloat,
|
||||
vk::Format::eR16Sfloat,
|
||||
vk::Format::eR16G16B16A16Sfloat,
|
||||
vk::Format::eD32Sfloat,
|
||||
vk::Format::eD16Unorm,
|
||||
vk::Format::eD16UnormS8Uint,
|
||||
vk::Format::eD24UnormS8Uint,
|
||||
vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eBc1RgbaUnormBlock,
|
||||
vk::Format::eBc2UnormBlock,
|
||||
vk::Format::eBc3UnormBlock,
|
||||
vk::Format::eBc4UnormBlock,
|
||||
vk::Format::eBc5UnormBlock,
|
||||
vk::Format::eBc5SnormBlock,
|
||||
vk::Format::eBc7UnormBlock,
|
||||
vk::Format::eAstc4x4UnormBlock,
|
||||
vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock,
|
||||
vk::Format::eAstc5x5UnormBlock,
|
||||
vk::Format::eAstc5x5SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock};
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties;
|
||||
constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32,
|
||||
vk::Format::eA8B8G8R8SnormPack32,
|
||||
vk::Format::eA8B8G8R8SrgbPack32,
|
||||
vk::Format::eB5G6R5UnormPack16,
|
||||
vk::Format::eA2B10G10R10UnormPack32,
|
||||
vk::Format::eR32G32B32A32Sfloat,
|
||||
vk::Format::eR16G16B16A16Uint,
|
||||
vk::Format::eR16G16Unorm,
|
||||
vk::Format::eR16G16Snorm,
|
||||
vk::Format::eR16G16Sfloat,
|
||||
vk::Format::eR16Unorm,
|
||||
vk::Format::eR8G8B8A8Srgb,
|
||||
vk::Format::eR8G8Unorm,
|
||||
vk::Format::eR8G8Snorm,
|
||||
vk::Format::eR8Unorm,
|
||||
vk::Format::eB10G11R11UfloatPack32,
|
||||
vk::Format::eR32Sfloat,
|
||||
vk::Format::eR16Sfloat,
|
||||
vk::Format::eR16G16B16A16Sfloat,
|
||||
vk::Format::eB8G8R8A8Unorm,
|
||||
vk::Format::eD32Sfloat,
|
||||
vk::Format::eD16Unorm,
|
||||
vk::Format::eD16UnormS8Uint,
|
||||
vk::Format::eD24UnormS8Uint,
|
||||
vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eBc1RgbaUnormBlock,
|
||||
vk::Format::eBc2UnormBlock,
|
||||
vk::Format::eBc3UnormBlock,
|
||||
vk::Format::eBc4UnormBlock,
|
||||
vk::Format::eBc5UnormBlock,
|
||||
vk::Format::eBc5SnormBlock,
|
||||
vk::Format::eBc7UnormBlock,
|
||||
vk::Format::eBc1RgbaSrgbBlock,
|
||||
vk::Format::eBc3SrgbBlock,
|
||||
vk::Format::eBc7SrgbBlock,
|
||||
vk::Format::eAstc4x4UnormBlock,
|
||||
vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock,
|
||||
vk::Format::eAstc5x5UnormBlock,
|
||||
vk::Format::eAstc5x5SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock};
|
||||
std::unordered_map<vk::Format, vk::FormatProperties> format_properties;
|
||||
for (const auto format : formats) {
|
||||
format_properties.emplace(format, physical.getFormatProperties(format, dldi));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
@@ -69,16 +69,26 @@ public:
|
||||
return present_family;
|
||||
}
|
||||
|
||||
/// Returns if the device is integrated with the host CPU.
|
||||
/// Returns true if the device is integrated with the host CPU.
|
||||
bool IsIntegrated() const {
|
||||
return device_type == vk::PhysicalDeviceType::eIntegratedGpu;
|
||||
}
|
||||
|
||||
/// Returns the driver ID.
|
||||
vk::DriverIdKHR GetDriverID() const {
|
||||
return driver_id;
|
||||
}
|
||||
|
||||
/// Returns uniform buffer alignment requeriment.
|
||||
u64 GetUniformBufferAlignment() const {
|
||||
return uniform_buffer_alignment;
|
||||
}
|
||||
|
||||
/// Returns storage alignment requeriment.
|
||||
u64 GetStorageBufferAlignment() const {
|
||||
return storage_buffer_alignment;
|
||||
}
|
||||
|
||||
/// Returns the maximum range for storage buffers.
|
||||
u64 GetMaxStorageBufferRange() const {
|
||||
return max_storage_buffer_range;
|
||||
@@ -89,9 +99,19 @@ public:
|
||||
return is_optimal_astc_supported;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports float16 natively
|
||||
bool IsFloat16Supported() const {
|
||||
return is_float16_supported;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_scalar_block_layout.
|
||||
bool IsExtScalarBlockLayoutSupported() const {
|
||||
return ext_scalar_block_layout;
|
||||
bool IsKhrUniformBufferStandardLayoutSupported() const {
|
||||
return khr_uniform_buffer_standard_layout;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_index_type_uint8.
|
||||
bool IsExtIndexTypeUint8Supported() const {
|
||||
return ext_index_type_uint8;
|
||||
}
|
||||
|
||||
/// Checks if the physical device is suitable.
|
||||
@@ -123,22 +143,28 @@ private:
|
||||
FormatType format_type) const;
|
||||
|
||||
/// Returns the device properties for Vulkan formats.
|
||||
static std::map<vk::Format, vk::FormatProperties> GetFormatProperties(
|
||||
static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties(
|
||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical);
|
||||
|
||||
const vk::PhysicalDevice physical; ///< Physical device.
|
||||
vk::DispatchLoaderDynamic dld; ///< Device function pointers.
|
||||
UniqueDevice logical; ///< Logical device.
|
||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||
vk::Queue present_queue; ///< Main present queue.
|
||||
u32 graphics_family{}; ///< Main graphics queue family index.
|
||||
u32 present_family{}; ///< Main present queue family index.
|
||||
vk::PhysicalDeviceType device_type; ///< Physical device type.
|
||||
u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment.
|
||||
u64 max_storage_buffer_range{}; ///< Max storage buffer size.
|
||||
bool is_optimal_astc_supported{}; ///< Support for native ASTC.
|
||||
bool ext_scalar_block_layout{}; ///< Support for VK_EXT_scalar_block_layout.
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties; ///< Format properties dictionary.
|
||||
const vk::PhysicalDevice physical; ///< Physical device.
|
||||
vk::DispatchLoaderDynamic dld; ///< Device function pointers.
|
||||
UniqueDevice logical; ///< Logical device.
|
||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||
vk::Queue present_queue; ///< Main present queue.
|
||||
u32 graphics_family{}; ///< Main graphics queue family index.
|
||||
u32 present_family{}; ///< Main present queue family index.
|
||||
vk::PhysicalDeviceType device_type; ///< Physical device type.
|
||||
vk::DriverIdKHR driver_id{}; ///< Driver ID.
|
||||
u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment.
|
||||
u64 storage_buffer_alignment{}; ///< Storage buffer alignment requeriment.
|
||||
u64 max_storage_buffer_range{}; ///< Max storage buffer size.
|
||||
bool is_optimal_astc_supported{}; ///< Support for native ASTC.
|
||||
bool is_float16_supported{}; ///< Support for float16 arithmetics.
|
||||
bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs.
|
||||
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
|
||||
bool khr_driver_properties{}; ///< Support for VK_KHR_driver_properties.
|
||||
std::unordered_map<vk::Format, vk::FormatProperties>
|
||||
format_properties; ///< Format properties dictionary.
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user