Compare commits
63 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50a806ea67 | ||
|
|
a1ef02c3e6 | ||
|
|
faa9e066ab | ||
|
|
99a71580c4 | ||
|
|
87be4bc283 | ||
|
|
e973cceadd | ||
|
|
23d2c50479 | ||
|
|
1280061725 | ||
|
|
8974771334 | ||
|
|
e4e55d064e | ||
|
|
58473309a0 | ||
|
|
f7eaea424d | ||
|
|
d7518cf6e0 | ||
|
|
5c8aff984e | ||
|
|
93703431e2 | ||
|
|
a040929c90 | ||
|
|
b555311438 | ||
|
|
d770c60205 | ||
|
|
dda4b5e89e | ||
|
|
9a07e9f805 | ||
|
|
ed37b68fb5 | ||
|
|
26e96d16d0 | ||
|
|
8ec1e16867 | ||
|
|
5a29b358aa | ||
|
|
c156ee8eb8 | ||
|
|
6ef84f1c4c | ||
|
|
2949d9552c | ||
|
|
978f3a3282 | ||
|
|
dce624e3f1 | ||
|
|
2081ed7db2 | ||
|
|
10bc725944 | ||
|
|
a813c10e1c | ||
|
|
1c5636e690 | ||
|
|
2afe8ac4a7 | ||
|
|
04397cd185 | ||
|
|
1ff3318458 | ||
|
|
b2268f1f8d | ||
|
|
9cfe2414cb | ||
|
|
c6fd56b00f | ||
|
|
18a89931a9 | ||
|
|
a405373144 | ||
|
|
1242c1ec0a | ||
|
|
41cd766438 | ||
|
|
325f3e0693 | ||
|
|
89be49d2f3 | ||
|
|
2714d9e64c | ||
|
|
d2ade27c3f | ||
|
|
177c45e97d | ||
|
|
9c206fe94d | ||
|
|
1ccc0457d5 | ||
|
|
7a439630bb | ||
|
|
fda8f1da20 | ||
|
|
a0e1fbfe14 | ||
|
|
60754b4728 | ||
|
|
2edab4e840 | ||
|
|
2bc6abb9a1 | ||
|
|
6f8ed9508d | ||
|
|
b89fc407d7 | ||
|
|
948bc87a59 | ||
|
|
ad3dca7e62 | ||
|
|
f32e28c7b8 | ||
|
|
19d0951ae6 | ||
|
|
7939ea18e8 |
@@ -162,10 +162,6 @@ artifacts:
|
||||
- path: $(BUILD_ZIP)
|
||||
name: build
|
||||
type: zip
|
||||
- path: $(BUILD_SYMBOLS)
|
||||
name: debugsymbols
|
||||
- path: $(BUILD_UPDATE)
|
||||
name: update
|
||||
|
||||
deploy:
|
||||
provider: GitHub
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: a42f301c28...0435ac2d80
@@ -10,6 +10,7 @@
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/settings.h"
|
||||
@@ -94,7 +95,10 @@ void Stream::PlayNextBuffer() {
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
MICROPROFILE_SCOPE(AudioOutput);
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Log {
|
||||
class Filter {
|
||||
public:
|
||||
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
||||
Filter(Level default_level = Level::Info);
|
||||
explicit Filter(Level default_level = Level::Info);
|
||||
|
||||
/// Resets the filter so that all classes have `level` as the minimum displayed level.
|
||||
void ResetAll(Level level);
|
||||
@@ -49,6 +49,6 @@ public:
|
||||
bool IsDebug() const;
|
||||
|
||||
private:
|
||||
std::array<Level, (size_t)Class::Count> class_levels;
|
||||
std::array<Level, static_cast<size_t>(Class::Count)> class_levels;
|
||||
};
|
||||
} // namespace Log
|
||||
|
||||
@@ -12,14 +12,14 @@ namespace Log {
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
/// pollute logs.
|
||||
///< pollute logs.
|
||||
Debug, ///< Less detailed debugging information.
|
||||
Info, ///< Status information from important points during execution.
|
||||
Warning, ///< Minor or potential problems found during execution of a task.
|
||||
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||
/// completed.
|
||||
Critical, ///< Major problems during execution that threathen the stability of the entire
|
||||
/// application.
|
||||
///< completed.
|
||||
Critical, ///< Major problems during execution that threaten the stability of the entire
|
||||
///< application.
|
||||
|
||||
Count ///< Total number of logging levels
|
||||
};
|
||||
@@ -49,7 +49,7 @@ enum class Class : ClassType {
|
||||
Kernel, ///< The HLE implementation of the CTR kernel
|
||||
Kernel_SVC, ///< Kernel system calls
|
||||
Service, ///< HLE implementation of system services. Each major service
|
||||
/// should have its own subclass.
|
||||
///< should have its own subclass.
|
||||
Service_ACC, ///< The ACC (Accounts) service
|
||||
Service_AM, ///< The AM (Applet manager) service
|
||||
Service_AOC, ///< The AOC (AddOn Content) service
|
||||
|
||||
@@ -15,6 +15,6 @@ struct Entry;
|
||||
std::string FormatLogMessage(const Entry& entry);
|
||||
/// Formats and prints a log entry to stderr.
|
||||
void PrintMessage(const Entry& entry);
|
||||
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
|
||||
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
|
||||
void PrintColoredMessage(const Entry& entry);
|
||||
} // namespace Log
|
||||
|
||||
@@ -49,6 +49,8 @@ add_library(core STATIC
|
||||
file_sys/savedata_factory.h
|
||||
file_sys/sdmc_factory.cpp
|
||||
file_sys/sdmc_factory.h
|
||||
file_sys/submission_package.cpp
|
||||
file_sys/submission_package.h
|
||||
file_sys/vfs.cpp
|
||||
file_sys/vfs.h
|
||||
file_sys/vfs_concat.cpp
|
||||
@@ -359,6 +361,8 @@ add_library(core STATIC
|
||||
loader/nro.h
|
||||
loader/nso.cpp
|
||||
loader/nso.h
|
||||
loader/nsp.cpp
|
||||
loader/nsp.h
|
||||
loader/xci.cpp
|
||||
loader/xci.h
|
||||
memory.cpp
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/config.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
@@ -143,7 +144,10 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
|
||||
return std::make_unique<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Dynarmic::Run() {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
|
||||
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
|
||||
|
||||
jit->Run();
|
||||
|
||||
@@ -193,10 +193,10 @@ void ARM_Unicorn::Step() {
|
||||
ExecuteInstructions(1);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit);
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
CoreTiming::AddTicks(num_instructions);
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -27,8 +30,6 @@
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "file_sys/vfs_concat.h"
|
||||
#include "file_sys/vfs_real.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -440,8 +441,8 @@ void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
|
||||
impl->debug_context = std::move(context);
|
||||
}
|
||||
|
||||
std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const {
|
||||
return impl->debug_context;
|
||||
Tegra::DebugContext* System::GetGPUDebugContext() const {
|
||||
return impl->debug_context.get();
|
||||
}
|
||||
|
||||
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
|
||||
|
||||
@@ -209,7 +209,7 @@ public:
|
||||
|
||||
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
|
||||
|
||||
std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const;
|
||||
Tegra::DebugContext* GetGPUDebugContext() const;
|
||||
|
||||
void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
|
||||
|
||||
|
||||
@@ -8,12 +8,15 @@
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
@@ -228,18 +231,28 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
const auto iter = std::find_if(
|
||||
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
|
||||
return;
|
||||
if (id == S128KeyType::Titlekey) {
|
||||
Key128 rights_id;
|
||||
std::memcpy(rights_id.data(), &field2, sizeof(u64));
|
||||
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
|
||||
WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
|
||||
}
|
||||
const auto iter2 = std::find_if(
|
||||
s128_file_id.begin(), s128_file_id.end(),
|
||||
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
if (iter != s128_file_id.end())
|
||||
WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key);
|
||||
if (iter2 != s128_file_id.end())
|
||||
WriteKeyToFile(false, iter2->first, key);
|
||||
s128_keys[{id, field1, field2}] = key;
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
|
||||
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
|
||||
return;
|
||||
const auto iter = std::find_if(
|
||||
s256_file_id.begin(), s256_file_id.end(),
|
||||
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
|
||||
|
||||
@@ -6,16 +6,19 @@
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
|
||||
|
||||
using Key128 = std::array<u8, 0x10>;
|
||||
using Key256 = std::array<u8, 0x20>;
|
||||
using SHA256Hash = std::array<u8, 0x20>;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -13,6 +14,8 @@ BISFactory::BISFactory(VirtualDir nand_root_)
|
||||
usrnand_cache(std::make_shared<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
|
||||
|
||||
BISFactory::~BISFactory() = default;
|
||||
|
||||
std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
|
||||
return sysnand_cache;
|
||||
}
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/loader/loader.h"
|
||||
#include "registered_cache.h"
|
||||
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class RegisteredCache;
|
||||
|
||||
/// 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
|
||||
/// registered caches.
|
||||
class BISFactory {
|
||||
public:
|
||||
explicit BISFactory(VirtualDir nand_root);
|
||||
~BISFactory();
|
||||
|
||||
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
|
||||
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
#include "common/logging/log.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/partition_filesystem.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@@ -43,15 +46,19 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
|
||||
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
|
||||
}
|
||||
|
||||
secure_partition = std::make_shared<NSP>(
|
||||
main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)]));
|
||||
|
||||
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
|
||||
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
|
||||
|
||||
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
|
||||
program =
|
||||
secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
|
||||
if (program != nullptr)
|
||||
program_nca_status = program->GetStatus();
|
||||
|
||||
auto result = AddNCAFromPartition(XCIPartition::Secure);
|
||||
if (result != Loader::ResultStatus::Success) {
|
||||
status = result;
|
||||
return;
|
||||
}
|
||||
|
||||
result = AddNCAFromPartition(XCIPartition::Update);
|
||||
auto result = AddNCAFromPartition(XCIPartition::Update);
|
||||
if (result != Loader::ResultStatus::Success) {
|
||||
status = result;
|
||||
return;
|
||||
@@ -74,6 +81,8 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
XCI::~XCI() = default;
|
||||
|
||||
Loader::ResultStatus XCI::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
@@ -86,6 +95,10 @@ VirtualDir XCI::GetPartition(XCIPartition partition) const {
|
||||
return partitions[static_cast<size_t>(partition)];
|
||||
}
|
||||
|
||||
std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
|
||||
return secure_partition;
|
||||
}
|
||||
|
||||
VirtualDir XCI::GetSecurePartition() const {
|
||||
return GetPartition(XCIPartition::Secure);
|
||||
}
|
||||
@@ -102,6 +115,20 @@ VirtualDir XCI::GetLogoPartition() const {
|
||||
return GetPartition(XCIPartition::Logo);
|
||||
}
|
||||
|
||||
u64 XCI::GetProgramTitleID() const {
|
||||
return secure_partition->GetProgramTitleID();
|
||||
}
|
||||
|
||||
std::shared_ptr<NCA> XCI::GetProgramNCA() const {
|
||||
return program;
|
||||
}
|
||||
|
||||
VirtualFile XCI::GetProgramNCAFile() const {
|
||||
if (GetProgramNCA() == nullptr)
|
||||
return nullptr;
|
||||
return GetProgramNCA()->GetBaseFile();
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
|
||||
return ncas;
|
||||
}
|
||||
|
||||
@@ -5,15 +5,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class NCA;
|
||||
enum class NCAContentType : u8;
|
||||
class NSP;
|
||||
|
||||
enum class GamecardSize : u8 {
|
||||
S_1GB = 0xFA,
|
||||
S_2GB = 0xF8,
|
||||
@@ -57,6 +64,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
|
||||
class XCI : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit XCI(VirtualFile file);
|
||||
~XCI() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
Loader::ResultStatus GetProgramNCAStatus() const;
|
||||
@@ -64,11 +72,16 @@ public:
|
||||
u8 GetFormatVersion() const;
|
||||
|
||||
VirtualDir GetPartition(XCIPartition partition) const;
|
||||
std::shared_ptr<NSP> GetSecurePartitionNSP() const;
|
||||
VirtualDir GetSecurePartition() const;
|
||||
VirtualDir GetNormalPartition() const;
|
||||
VirtualDir GetUpdatePartition() const;
|
||||
VirtualDir GetLogoPartition() const;
|
||||
|
||||
u64 GetProgramTitleID() const;
|
||||
|
||||
std::shared_ptr<NCA> GetProgramNCA() const;
|
||||
VirtualFile GetProgramNCAFile() const;
|
||||
const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
|
||||
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
|
||||
VirtualFile GetNCAFileByType(NCAContentType type) const;
|
||||
@@ -94,6 +107,8 @@ private:
|
||||
Loader::ResultStatus program_nca_status;
|
||||
|
||||
std::vector<VirtualDir> partitions;
|
||||
std::shared_ptr<NSP> secure_partition;
|
||||
std::shared_ptr<NCA> program;
|
||||
std::vector<std::shared_ptr<NCA>> ncas;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/ctr_encryption_layer.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "control_metadata.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -21,7 +21,17 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
|
||||
}
|
||||
|
||||
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
||||
return raw->language_entries.at(static_cast<u8>(language));
|
||||
if (language != Language::Default) {
|
||||
return raw->language_entries.at(static_cast<u8>(language));
|
||||
} else {
|
||||
for (const auto& language_entry : raw->language_entries) {
|
||||
if (!language_entry.GetApplicationName().empty())
|
||||
return language_entry;
|
||||
}
|
||||
|
||||
// Fallback to English
|
||||
return GetLanguageEntry(Language::AmericanEnglish);
|
||||
}
|
||||
}
|
||||
|
||||
std::string NACP::GetApplicationName(Language language) const {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -60,6 +62,8 @@ enum class Language : u8 {
|
||||
Korean = 12,
|
||||
Taiwanese = 13,
|
||||
Chinese = 14,
|
||||
|
||||
Default = 255,
|
||||
};
|
||||
|
||||
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
|
||||
@@ -74,9 +78,9 @@ static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
|
||||
class NACP {
|
||||
public:
|
||||
explicit NACP(VirtualFile file);
|
||||
const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const;
|
||||
std::string GetApplicationName(Language language = Language::AmericanEnglish) const;
|
||||
std::string GetDeveloperName(Language language = Language::AmericanEnglish) const;
|
||||
const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
|
||||
std::string GetApplicationName(Language language = Language::Default) const;
|
||||
std::string GetDeveloperName(Language language = Language::Default) const;
|
||||
u64 GetTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@@ -5,12 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "partition_filesystem.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
|
||||
@@ -5,13 +5,17 @@
|
||||
#include <regex>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/encryption_layer.h"
|
||||
#include "core/crypto/key_manager.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/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
std::string RegisteredCacheEntry::DebugInfo() const {
|
||||
@@ -355,17 +359,21 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) {
|
||||
const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false));
|
||||
const auto iter =
|
||||
std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(),
|
||||
[&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; });
|
||||
return iter == xci->GetNCAs().end() ? nullptr : *iter;
|
||||
static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) {
|
||||
const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
|
||||
if (file == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<NCA>(file);
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
|
||||
const VfsCopyFunction& copy) {
|
||||
const auto& ncas = xci->GetNCAs();
|
||||
return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy);
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists,
|
||||
const VfsCopyFunction& copy) {
|
||||
const auto& ncas = nsp->GetNCAsCollapsed();
|
||||
const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
|
||||
return nca->GetType() == NCAContentType::Meta;
|
||||
});
|
||||
@@ -389,7 +397,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overw
|
||||
const auto cnmt_file = section0->GetFiles()[0];
|
||||
const CNMT cnmt(cnmt_file);
|
||||
for (const auto& record : cnmt.GetContentRecords()) {
|
||||
const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
|
||||
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
|
||||
if (nca == nullptr)
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
|
||||
|
||||
@@ -11,15 +11,19 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
class XCI;
|
||||
class CNMT;
|
||||
class NCA;
|
||||
class NSP;
|
||||
class XCI;
|
||||
|
||||
enum class ContentRecordType : u8;
|
||||
enum class TitleType : u8;
|
||||
|
||||
struct ContentRecord;
|
||||
|
||||
using NcaID = std::array<u8, 0x10>;
|
||||
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
|
||||
@@ -86,10 +90,12 @@ public:
|
||||
boost::optional<ContentRecordType> record_type = boost::none,
|
||||
boost::optional<u64> title_id = boost::none) const;
|
||||
|
||||
// Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
|
||||
// is a meta NCA and all of them are accessible.
|
||||
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
|
||||
// there is a meta NCA and all of them are accessible.
|
||||
InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
|
||||
const VfsCopyFunction& copy = &VfsRawCopy);
|
||||
InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false,
|
||||
const VfsCopyFunction& copy = &VfsRawCopy);
|
||||
|
||||
// Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
|
||||
// poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
236
src/core/file_sys/submission_package.cpp
Normal file
236
src/core/file_sys/submission_package.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/hex_util.h"
|
||||
#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/submission_package.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
NSP::NSP(VirtualFile file_)
|
||||
: file(std::move(file_)),
|
||||
pfs(std::make_shared<PartitionFilesystem>(file)), status{Loader::ResultStatus::Success} {
|
||||
if (pfs->GetStatus() != Loader::ResultStatus::Success) {
|
||||
status = pfs->GetStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDirectoryExeFS(pfs)) {
|
||||
extracted = true;
|
||||
exefs = pfs;
|
||||
|
||||
const auto& files = pfs->GetFiles();
|
||||
const auto romfs_iter =
|
||||
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
|
||||
return file->GetName().find(".romfs") != std::string::npos;
|
||||
});
|
||||
if (romfs_iter != files.end())
|
||||
romfs = *romfs_iter;
|
||||
return;
|
||||
}
|
||||
|
||||
extracted = false;
|
||||
const auto files = pfs->GetFiles();
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
for (const auto& ticket_file : files) {
|
||||
if (ticket_file->GetExtension() == "tik") {
|
||||
if (ticket_file == nullptr ||
|
||||
ticket_file->GetSize() <
|
||||
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Core::Crypto::Key128 key{};
|
||||
ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
|
||||
std::string_view name_only(ticket_file->GetName());
|
||||
name_only.remove_suffix(4);
|
||||
const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
|
||||
u128 rights_id;
|
||||
std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
|
||||
keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& outer_file : files) {
|
||||
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") {
|
||||
const auto nca = std::make_shared<NCA>(outer_file);
|
||||
if (nca->GetStatus() != Loader::ResultStatus::Success)
|
||||
continue;
|
||||
const auto section0 = nca->GetSubdirectories()[0];
|
||||
|
||||
for (const auto& inner_file : section0->GetFiles()) {
|
||||
if (inner_file->GetExtension() != "cnmt")
|
||||
continue;
|
||||
|
||||
const CNMT cnmt(inner_file);
|
||||
auto& ncas_title = ncas[cnmt.GetTitleID()];
|
||||
|
||||
ncas_title[ContentRecordType::Meta] = nca;
|
||||
for (const auto& rec : cnmt.GetContentRecords()) {
|
||||
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
|
||||
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
|
||||
if (next_file == nullptr) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"NCA with ID {}.nca is listed in content metadata, but cannot "
|
||||
"be found in PFS. NSP appears to be corrupted.",
|
||||
id_string);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_nca = std::make_shared<NCA>(next_file);
|
||||
if (next_nca->GetType() == NCAContentType::Program)
|
||||
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
|
||||
if (next_nca->GetStatus() == Loader::ResultStatus::Success)
|
||||
ncas_title[rec.type] = std::move(next_nca);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSP::~NSP() = default;
|
||||
|
||||
Loader::ResultStatus NSP::GetStatus() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
|
||||
const auto iter = program_status.find(title_id);
|
||||
if (iter == program_status.end())
|
||||
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
u64 NSP::GetFirstTitleID() const {
|
||||
if (program_status.empty())
|
||||
return 0;
|
||||
return program_status.begin()->first;
|
||||
}
|
||||
|
||||
u64 NSP::GetProgramTitleID() const {
|
||||
const auto out = GetFirstTitleID();
|
||||
if ((out & 0x800) == 0)
|
||||
return out;
|
||||
|
||||
const auto ids = GetTitleIDs();
|
||||
const auto iter =
|
||||
std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
|
||||
return iter == ids.end() ? out : *iter;
|
||||
}
|
||||
|
||||
std::vector<u64> NSP::GetTitleIDs() const {
|
||||
std::vector<u64> out;
|
||||
out.reserve(ncas.size());
|
||||
for (const auto& kv : ncas)
|
||||
out.push_back(kv.first);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool NSP::IsExtractedType() const {
|
||||
return extracted;
|
||||
}
|
||||
|
||||
VirtualFile NSP::GetRomFS() const {
|
||||
return romfs;
|
||||
}
|
||||
|
||||
VirtualDir NSP::GetExeFS() const {
|
||||
return exefs;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const {
|
||||
if (extracted)
|
||||
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
|
||||
std::vector<std::shared_ptr<NCA>> out;
|
||||
for (const auto& map : ncas) {
|
||||
for (const auto& inner_map : map.second)
|
||||
out.push_back(inner_map.second);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
|
||||
if (extracted)
|
||||
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
|
||||
std::multimap<u64, std::shared_ptr<NCA>> out;
|
||||
for (const auto& map : ncas) {
|
||||
for (const auto& inner_map : map.second)
|
||||
out.emplace(map.first, inner_map.second);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
|
||||
return ncas;
|
||||
}
|
||||
|
||||
std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
|
||||
if (extracted)
|
||||
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
|
||||
|
||||
const auto title_id_iter = ncas.find(title_id);
|
||||
if (title_id_iter == ncas.end())
|
||||
return nullptr;
|
||||
|
||||
const auto type_iter = title_id_iter->second.find(type);
|
||||
if (type_iter == title_id_iter->second.end())
|
||||
return nullptr;
|
||||
|
||||
return type_iter->second;
|
||||
}
|
||||
|
||||
VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
|
||||
if (extracted)
|
||||
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
|
||||
const auto nca = GetNCA(title_id, type);
|
||||
if (nca != nullptr)
|
||||
return nca->GetBaseFile();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const {
|
||||
if (extracted)
|
||||
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
|
||||
std::vector<Core::Crypto::Key128> out;
|
||||
for (const auto& ticket_file : ticket_files) {
|
||||
if (ticket_file == nullptr ||
|
||||
ticket_file->GetSize() <
|
||||
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.emplace_back();
|
||||
ticket_file->Read(out.back().data(), out.back().size(),
|
||||
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<VirtualFile> NSP::GetFiles() const {
|
||||
return pfs->GetFiles();
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> NSP::GetSubdirectories() const {
|
||||
return pfs->GetSubdirectories();
|
||||
}
|
||||
|
||||
std::string NSP::GetName() const {
|
||||
return file->GetName();
|
||||
}
|
||||
|
||||
VirtualDir NSP::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
73
src/core/file_sys/submission_package.h
Normal file
73
src/core/file_sys/submission_package.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class PartitionFilesystem;
|
||||
|
||||
class NSP : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NSP(VirtualFile file);
|
||||
~NSP();
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
Loader::ResultStatus GetProgramStatus(u64 title_id) const;
|
||||
// Should only be used when one title id can be assured.
|
||||
u64 GetFirstTitleID() const;
|
||||
u64 GetProgramTitleID() const;
|
||||
std::vector<u64> GetTitleIDs() const;
|
||||
|
||||
bool IsExtractedType() const;
|
||||
|
||||
// Common (Can be safely called on both types)
|
||||
VirtualFile GetRomFS() const;
|
||||
VirtualDir GetExeFS() const;
|
||||
|
||||
// Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
|
||||
std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
|
||||
std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
|
||||
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
|
||||
std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
|
||||
VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
|
||||
std::vector<Core::Crypto::Key128> GetTitlekey() const;
|
||||
|
||||
std::vector<VirtualFile> GetFiles() const override;
|
||||
|
||||
std::vector<VirtualDir> GetSubdirectories() const override;
|
||||
|
||||
std::string GetName() const override;
|
||||
|
||||
VirtualDir GetParentDirectory() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
VirtualFile file;
|
||||
|
||||
bool extracted;
|
||||
Loader::ResultStatus status;
|
||||
std::map<u64, Loader::ResultStatus> program_status;
|
||||
|
||||
std::shared_ptr<PartitionFilesystem> pfs;
|
||||
// Map title id -> {map type -> NCA}
|
||||
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
|
||||
std::vector<VirtualFile> ticket_files;
|
||||
|
||||
VirtualFile romfs;
|
||||
VirtualDir exefs;
|
||||
};
|
||||
} // namespace FileSys
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
|
||||
@@ -39,6 +40,7 @@ static std::string ModeFlagsToString(Mode mode) {
|
||||
}
|
||||
|
||||
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
|
||||
RealVfsFilesystem::~RealVfsFilesystem() = default;
|
||||
|
||||
std::string RealVfsFilesystem::GetName() const {
|
||||
return "Real";
|
||||
@@ -219,6 +221,8 @@ RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOF
|
||||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
||||
perms(perms_) {}
|
||||
|
||||
RealVfsFile::~RealVfsFile() = default;
|
||||
|
||||
std::string RealVfsFile::GetName() const {
|
||||
return path_components.back();
|
||||
}
|
||||
@@ -312,6 +316,8 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
|
||||
FileUtil::CreateDir(path);
|
||||
}
|
||||
|
||||
RealVfsDirectory::~RealVfsDirectory() = default;
|
||||
|
||||
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
|
||||
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
|
||||
if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path))
|
||||
|
||||
@@ -6,15 +6,19 @@
|
||||
|
||||
#include <string_view>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileUtil {
|
||||
class IOFile;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class RealVfsFilesystem : public VfsFilesystem {
|
||||
public:
|
||||
RealVfsFilesystem();
|
||||
~RealVfsFilesystem() override;
|
||||
|
||||
std::string GetName() const override;
|
||||
bool IsReadable() const override;
|
||||
@@ -40,10 +44,9 @@ class RealVfsFile : public VfsFile {
|
||||
friend class RealVfsDirectory;
|
||||
friend class RealVfsFilesystem;
|
||||
|
||||
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
|
||||
const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
public:
|
||||
~RealVfsFile() override;
|
||||
|
||||
std::string GetName() const override;
|
||||
size_t GetSize() const override;
|
||||
bool Resize(size_t new_size) override;
|
||||
@@ -55,6 +58,9 @@ public:
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
private:
|
||||
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
|
||||
const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
bool Close();
|
||||
|
||||
RealVfsFilesystem& base;
|
||||
@@ -70,9 +76,9 @@ private:
|
||||
class RealVfsDirectory : public VfsDirectory {
|
||||
friend class RealVfsFilesystem;
|
||||
|
||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
public:
|
||||
~RealVfsDirectory() override;
|
||||
|
||||
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
|
||||
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
|
||||
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
|
||||
@@ -97,6 +103,8 @@ protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
template <typename T, typename R>
|
||||
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -124,6 +125,8 @@ struct KernelCore::Impl {
|
||||
|
||||
timer_callback_handle_table.Clear();
|
||||
timer_callback_event_type = nullptr;
|
||||
|
||||
named_ports.clear();
|
||||
}
|
||||
|
||||
void InitializeResourceLimits(KernelCore& kernel) {
|
||||
@@ -217,6 +220,10 @@ struct KernelCore::Impl {
|
||||
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
|
||||
// allowing us to simply use a pool index or similar.
|
||||
Kernel::HandleTable thread_wakeup_callback_handle_table;
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using
|
||||
/// the ConnectToPort SVC.
|
||||
NamedPortTable named_ports;
|
||||
};
|
||||
|
||||
KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
|
||||
@@ -257,6 +264,23 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
|
||||
impl->process_list.push_back(std::move(process));
|
||||
}
|
||||
|
||||
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
|
||||
impl->named_ports.emplace(std::move(name), std::move(port));
|
||||
}
|
||||
|
||||
KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) {
|
||||
return impl->named_ports.find(name);
|
||||
}
|
||||
|
||||
KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort(
|
||||
const std::string& name) const {
|
||||
return impl->named_ports.find(name);
|
||||
}
|
||||
|
||||
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
|
||||
return port != impl->named_ports.cend();
|
||||
}
|
||||
|
||||
u32 KernelCore::CreateNewObjectID() {
|
||||
return impl->next_object_id++;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
template <typename T>
|
||||
@@ -15,6 +17,7 @@ struct EventType;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ClientPort;
|
||||
class HandleTable;
|
||||
class Process;
|
||||
class ResourceLimit;
|
||||
@@ -25,6 +28,9 @@ enum class ResourceLimitCategory : u8;
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
private:
|
||||
using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
|
||||
|
||||
public:
|
||||
KernelCore();
|
||||
~KernelCore();
|
||||
@@ -59,6 +65,18 @@ public:
|
||||
/// Adds the given shared pointer to an internal list of active processes.
|
||||
void AppendNewProcess(SharedPtr<Process> process);
|
||||
|
||||
/// Adds a port to the named port table
|
||||
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
|
||||
|
||||
/// Finds a port within the named port table with the given name.
|
||||
NamedPortTable::iterator FindNamedPort(const std::string& name);
|
||||
|
||||
/// Finds a port within the named port table with the given name.
|
||||
NamedPortTable::const_iterator FindNamedPort(const std::string& name) const;
|
||||
|
||||
/// Determines whether or not the given port is a valid named port.
|
||||
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
||||
|
||||
private:
|
||||
friend class Object;
|
||||
friend class Process;
|
||||
|
||||
@@ -68,19 +68,22 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
|
||||
if (!Memory::IsValidVirtualAddress(port_name_address))
|
||||
if (!Memory::IsValidVirtualAddress(port_name_address)) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static constexpr std::size_t PortNameMaxLength = 11;
|
||||
// Read 1 char beyond the max allowed port name to detect names that are too long.
|
||||
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
|
||||
if (port_name.size() > PortNameMaxLength)
|
||||
if (port_name.size() > PortNameMaxLength) {
|
||||
return ERR_PORT_NAME_TOO_LONG;
|
||||
}
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
|
||||
|
||||
auto it = Service::g_kernel_named_ports.find(port_name);
|
||||
if (it == Service::g_kernel_named_ports.end()) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto it = kernel.FindNamedPort(port_name);
|
||||
if (!kernel.IsValidNamedPort(it)) {
|
||||
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
@@ -91,7 +94,6 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
|
||||
CASCADE_RESULT(client_session, client_port->Connect());
|
||||
|
||||
// Return the client session
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -60,17 +60,20 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (path.empty()) {
|
||||
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
|
||||
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,17 @@
|
||||
|
||||
namespace Service::FileSystem {
|
||||
|
||||
enum class FileSystemType : u8 {
|
||||
Invalid0 = 0,
|
||||
Invalid1 = 1,
|
||||
Logo = 2,
|
||||
ContentControl = 3,
|
||||
ContentManual = 4,
|
||||
ContentMeta = 5,
|
||||
ContentData = 6,
|
||||
ApplicationPackage = 7,
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(FileSys::VirtualFile backend_)
|
||||
@@ -420,7 +431,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{0, nullptr, "MountContent"},
|
||||
{1, &FSP_SRV::Initialize, "Initialize"},
|
||||
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
|
||||
{7, nullptr, "OpenFileSystemWithPatch"},
|
||||
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
|
||||
{8, nullptr, "OpenFileSystemWithId"},
|
||||
{9, nullptr, "OpenDataFileSystemByApplicationId"},
|
||||
{11, nullptr, "OpenBisFileSystem"},
|
||||
@@ -444,7 +455,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
|
||||
{34, nullptr, "GetCacheStorageSize"},
|
||||
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
|
||||
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
|
||||
{53, nullptr, "OpenReadOnlySaveDataFileSystem"},
|
||||
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
|
||||
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
|
||||
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
|
||||
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
|
||||
@@ -516,6 +527,16 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto type = rp.PopRaw<FileSystemType>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(ResultCode(-1));
|
||||
}
|
||||
|
||||
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
@@ -563,6 +584,11 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
|
||||
MountSaveData(ctx);
|
||||
}
|
||||
|
||||
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ public:
|
||||
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
|
||||
void MountSdCard(Kernel::HLERequestContext& ctx);
|
||||
void CreateSaveData(Kernel::HLERequestContext& ctx);
|
||||
void MountSaveData(Kernel::HLERequestContext& ctx);
|
||||
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
|
||||
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
@@ -114,7 +115,7 @@ void ServiceFrameworkBase::InstallAsNamedPort() {
|
||||
std::tie(server_port, client_port) =
|
||||
ServerPort::CreatePortPair(kernel, max_sessions, service_name);
|
||||
server_port->SetHleHandler(shared_from_this());
|
||||
AddNamedPort(service_name, std::move(client_port));
|
||||
kernel.AddNamedPort(service_name, std::move(client_port));
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
|
||||
@@ -197,11 +198,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Module interface
|
||||
|
||||
// TODO(yuriks): Move to kernel
|
||||
void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
|
||||
g_kernel_named_ports.emplace(std::move(name), std::move(port));
|
||||
}
|
||||
|
||||
/// Initialize ServiceManager
|
||||
void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
|
||||
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
|
||||
@@ -264,7 +260,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
|
||||
|
||||
/// Shutdown ServiceManager
|
||||
void Shutdown() {
|
||||
g_kernel_named_ports.clear();
|
||||
LOG_DEBUG(Service, "shutdown OK");
|
||||
}
|
||||
} // namespace Service
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
@@ -187,10 +186,4 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm,
|
||||
/// Shutdown ServiceManager
|
||||
void Shutdown();
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC.
|
||||
extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports;
|
||||
|
||||
/// Adds a port to the named port table
|
||||
void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port);
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/ssl/ssl.h"
|
||||
|
||||
namespace Service::SSL {
|
||||
@@ -81,36 +84,43 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
void SSL::CreateContext(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||
class SSL final : public ServiceFramework<SSL> {
|
||||
public:
|
||||
explicit SSL() : ServiceFramework{"ssl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &SSL::CreateContext, "CreateContext"},
|
||||
{1, nullptr, "GetContextCount"},
|
||||
{2, nullptr, "GetCertificates"},
|
||||
{3, nullptr, "GetCertificateBufSize"},
|
||||
{4, nullptr, "DebugIoctl"},
|
||||
{5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
|
||||
{6, nullptr, "FlushSessionCache"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISslContext>();
|
||||
}
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
SSL::SSL() : ServiceFramework("ssl") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &SSL::CreateContext, "CreateContext"},
|
||||
{1, nullptr, "GetContextCount"},
|
||||
{2, nullptr, "GetCertificates"},
|
||||
{3, nullptr, "GetCertificateBufSize"},
|
||||
{4, nullptr, "DebugIoctl"},
|
||||
{5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
|
||||
{6, nullptr, "FlushSessionCache"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
private:
|
||||
void CreateContext(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||
|
||||
void SSL::SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
|
||||
u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISslContext>();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
|
||||
u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
std::make_shared<SSL>()->InstallAsService(service_manager);
|
||||
|
||||
@@ -4,20 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::SSL {
|
||||
|
||||
class SSL final : public ServiceFramework<SSL> {
|
||||
public:
|
||||
explicit SSL();
|
||||
~SSL() = default;
|
||||
|
||||
private:
|
||||
void CreateContext(Kernel::HLERequestContext& ctx);
|
||||
void SetInterfaceVersion(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
/// Registers all SSL services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
|
||||
|
||||
if (nacp_file != nullptr) {
|
||||
FileSys::NACP nacp(nacp_file);
|
||||
title_id = nacp.GetTitleId();
|
||||
name = nacp.GetApplicationName();
|
||||
}
|
||||
}
|
||||
@@ -120,6 +119,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
}
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
title_id = metadata.GetTitleID();
|
||||
process->program_id = metadata.GetTitleID();
|
||||
process->svc_access_mask.set();
|
||||
process->resource_limit =
|
||||
@@ -159,8 +159,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buff
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
|
||||
if (name.empty())
|
||||
return ResultStatus::ErrorNoControl;
|
||||
out_program_id = title_id;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/elf.h"
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/loader/nsp.h"
|
||||
#include "core/loader/xci.h"
|
||||
|
||||
namespace Loader {
|
||||
@@ -34,6 +35,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
|
||||
CHECK_TYPE(NCA)
|
||||
CHECK_TYPE(XCI)
|
||||
CHECK_TYPE(NAX)
|
||||
CHECK_TYPE(NSP)
|
||||
|
||||
#undef CHECK_TYPE
|
||||
|
||||
@@ -59,6 +61,8 @@ FileType GuessFromFilename(const std::string& name) {
|
||||
return FileType::NCA;
|
||||
if (extension == "xci")
|
||||
return FileType::XCI;
|
||||
if (extension == "nsp")
|
||||
return FileType::NSP;
|
||||
|
||||
return FileType::Unknown;
|
||||
}
|
||||
@@ -77,6 +81,8 @@ std::string GetFileTypeString(FileType type) {
|
||||
return "XCI";
|
||||
case FileType::NAX:
|
||||
return "NAX";
|
||||
case FileType::NSP:
|
||||
return "NSP";
|
||||
case FileType::DeconstructedRomDirectory:
|
||||
return "Directory";
|
||||
case FileType::Error:
|
||||
@@ -87,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 49> RESULT_MESSAGES{
|
||||
constexpr std::array<const char*, 50> RESULT_MESSAGES{
|
||||
"The operation completed successfully.",
|
||||
"The loader requested to load is already loaded.",
|
||||
"The operation is not implemented.",
|
||||
@@ -137,13 +143,16 @@ constexpr std::array<const char*, 49> RESULT_MESSAGES{
|
||||
"The AES Key Generation Source could not be found.",
|
||||
"The SD Save Key Source could not be found.",
|
||||
"The SD NCA Key Source could not be found.",
|
||||
};
|
||||
"The NSP file is missing a Program-type NCA."};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, ResultStatus status) {
|
||||
os << RESULT_MESSAGES.at(static_cast<size_t>(status));
|
||||
return os;
|
||||
}
|
||||
|
||||
AppLoader::AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
|
||||
AppLoader::~AppLoader() = default;
|
||||
|
||||
/**
|
||||
* Get a loader for a file with a specific type
|
||||
* @param file The file to load
|
||||
@@ -179,6 +188,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
|
||||
case FileType::NAX:
|
||||
return std::make_unique<AppLoader_NAX>(std::move(file));
|
||||
|
||||
// NX NSP (Nintendo Submission Package) file format
|
||||
case FileType::NSP:
|
||||
return std::make_unique<AppLoader_NSP>(std::move(file));
|
||||
|
||||
// NX deconstructed ROM directory.
|
||||
case FileType::DeconstructedRomDirectory:
|
||||
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -12,7 +11,6 @@
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
@@ -31,6 +29,7 @@ enum class FileType {
|
||||
NSO,
|
||||
NRO,
|
||||
NCA,
|
||||
NSP,
|
||||
XCI,
|
||||
NAX,
|
||||
DeconstructedRomDirectory,
|
||||
@@ -107,6 +106,7 @@ enum class ResultStatus : u16 {
|
||||
ErrorMissingAESKeyGenerationSource,
|
||||
ErrorMissingSDSaveKeySource,
|
||||
ErrorMissingSDNCAKeySource,
|
||||
ErrorNSPMissingProgramNCA,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, ResultStatus status);
|
||||
@@ -114,8 +114,8 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
|
||||
/// Interface for loading an application
|
||||
class AppLoader : NonCopyable {
|
||||
public:
|
||||
explicit AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
|
||||
virtual ~AppLoader() {}
|
||||
explicit AppLoader(FileSys::VirtualFile file);
|
||||
virtual ~AppLoader();
|
||||
|
||||
/**
|
||||
* Returns the type of this file
|
||||
|
||||
135
src/core/loader/nsp.cpp
Normal file
135
src/core/loader/nsp.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/loader/deconstructed_rom_directory.h"
|
||||
#include "core/loader/nca.h"
|
||||
#include "core/loader/nsp.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
|
||||
: AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
|
||||
title_id(nsp->GetProgramTitleID()) {
|
||||
|
||||
if (nsp->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
if (nsp->IsExtractedType())
|
||||
return;
|
||||
|
||||
const auto control_nca =
|
||||
nsp->GetNCA(nsp->GetFirstTitleID(), FileSys::ContentRecordType::Control);
|
||||
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
|
||||
const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS());
|
||||
if (romfs == nullptr)
|
||||
return;
|
||||
|
||||
for (const auto& language : FileSys::LANGUAGE_NAMES) {
|
||||
icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
|
||||
if (icon_file != nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
const auto nacp_raw = romfs->GetFile("control.nacp");
|
||||
if (nacp_raw == nullptr)
|
||||
return;
|
||||
nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
|
||||
}
|
||||
|
||||
AppLoader_NSP::~AppLoader_NSP() = default;
|
||||
|
||||
FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
|
||||
FileSys::NSP nsp(file);
|
||||
|
||||
if (nsp.GetStatus() == ResultStatus::Success) {
|
||||
// Extracted Type case
|
||||
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
|
||||
FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
|
||||
// Non-Ectracted Type case
|
||||
if (!nsp.IsExtractedType() &&
|
||||
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
|
||||
AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
|
||||
nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) {
|
||||
return FileType::NSP;
|
||||
}
|
||||
}
|
||||
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
if (is_loaded) {
|
||||
return ResultStatus::ErrorAlreadyLoaded;
|
||||
}
|
||||
|
||||
if (nsp->IsExtractedType()) {
|
||||
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
|
||||
} else {
|
||||
if (title_id == 0)
|
||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
|
||||
secondary_loader = std::make_unique<AppLoader_NCA>(
|
||||
nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
|
||||
|
||||
if (nsp->GetStatus() != ResultStatus::Success)
|
||||
return nsp->GetStatus();
|
||||
|
||||
if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
|
||||
return nsp->GetProgramStatus(title_id);
|
||||
|
||||
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false))
|
||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
||||
return ResultStatus::ErrorNSPMissingProgramNCA;
|
||||
}
|
||||
}
|
||||
|
||||
const auto result = secondary_loader->Load(process);
|
||||
if (result != ResultStatus::Success)
|
||||
return result;
|
||||
|
||||
is_loaded = true;
|
||||
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
return secondary_loader->ReadRomFS(dir);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
|
||||
if (title_id == 0)
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
out_program_id = title_id;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
buffer = icon_file->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorNoControl;
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
} // namespace Loader
|
||||
54
src/core/loader/nsp.h
Normal file
54
src/core/loader/nsp.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
class NSP;
|
||||
} // namespace FileSys
|
||||
|
||||
namespace Loader {
|
||||
|
||||
class AppLoader_NCA;
|
||||
|
||||
/// Loads an XCI file
|
||||
class AppLoader_NSP final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_NSP(FileSys::VirtualFile file);
|
||||
~AppLoader_NSP() override;
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
* @param file std::shared_ptr<VfsFile> open file
|
||||
* @return FileType found, or FileType::Error if this loader doesn't know it
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& file);
|
||||
|
||||
FileType GetFileType() override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
|
||||
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileSys::NSP> nsp;
|
||||
std::unique_ptr<AppLoader> secondary_loader;
|
||||
|
||||
FileSys::VirtualFile icon_file;
|
||||
std::shared_ptr<FileSys::NACP> nacp_file;
|
||||
u64 title_id;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
@@ -17,8 +17,7 @@ namespace Loader {
|
||||
|
||||
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
|
||||
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(
|
||||
xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
|
||||
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
|
||||
if (xci->GetStatus() != ResultStatus::Success)
|
||||
return;
|
||||
const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
|
||||
@@ -64,11 +63,11 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
if (xci->GetProgramNCAStatus() != ResultStatus::Success)
|
||||
return xci->GetProgramNCAStatus();
|
||||
|
||||
const auto nca = xci->GetNCAFileByType(FileSys::NCAContentType::Program);
|
||||
const auto nca = xci->GetProgramNCA();
|
||||
if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
|
||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
||||
|
||||
auto result = nca_loader->Load(process);
|
||||
const auto result = nca_loader->Load(process);
|
||||
if (result != ResultStatus::Success)
|
||||
return result;
|
||||
|
||||
|
||||
@@ -127,6 +127,8 @@ struct Values {
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
std::string nand_dir;
|
||||
std::string sdmc_dir;
|
||||
|
||||
// Renderer
|
||||
float resolution_factor;
|
||||
|
||||
@@ -22,6 +22,7 @@ add_library(video_core STATIC
|
||||
rasterizer_interface.h
|
||||
renderer_base.cpp
|
||||
renderer_base.h
|
||||
renderer_opengl/gl_buffer_cache.cpp
|
||||
renderer_opengl/gl_rasterizer.cpp
|
||||
renderer_opengl/gl_rasterizer.h
|
||||
renderer_opengl/gl_rasterizer_cache.cpp
|
||||
|
||||
@@ -34,6 +34,8 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
|
||||
"{:08X} remaining params {}",
|
||||
method, subchannel, value, remaining_params);
|
||||
|
||||
ASSERT(subchannel < bound_engines.size());
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
|
||||
@@ -47,8 +49,6 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(bound_engines.find(subchannel) != bound_engines.end());
|
||||
|
||||
const EngineID engine = bound_engines[subchannel];
|
||||
|
||||
switch (engine) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cinttypes>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@@ -194,8 +195,8 @@ void Maxwell3D::ProcessQueryGet() {
|
||||
// wait queues.
|
||||
LongQueryResult query_result{};
|
||||
query_result.value = result;
|
||||
// TODO(Subv): Generate a real GPU timestamp and write it here instead of 0
|
||||
query_result.timestamp = 0;
|
||||
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
|
||||
query_result.timestamp = CoreTiming::GetTicks();
|
||||
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -243,7 +243,8 @@ enum class TextureType : u64 {
|
||||
TextureCube = 3,
|
||||
};
|
||||
|
||||
enum class IpaMode : u64 { Pass = 0, None = 1, Constant = 2, Sc = 3 };
|
||||
enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
|
||||
enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
|
||||
|
||||
union Instruction {
|
||||
Instruction& operator=(const Instruction& instr) {
|
||||
@@ -328,10 +329,16 @@ union Instruction {
|
||||
} alu;
|
||||
|
||||
union {
|
||||
BitField<54, 3, IpaMode> mode;
|
||||
BitField<51, 1, u64> saturate;
|
||||
BitField<52, 2, IpaSampleMode> sample_mode;
|
||||
BitField<54, 2, IpaInterpMode> interp_mode;
|
||||
} ipa;
|
||||
|
||||
union {
|
||||
BitField<39, 2, u64> tab5cb8_2;
|
||||
BitField<41, 3, u64> tab5c68_1;
|
||||
BitField<44, 2, u64> tab5c68_0;
|
||||
BitField<47, 1, u64> cc;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
} fmul;
|
||||
|
||||
@@ -399,8 +406,11 @@ union Instruction {
|
||||
} flow;
|
||||
|
||||
union {
|
||||
BitField<47, 1, u64> cc;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_c;
|
||||
BitField<51, 2, u64> tab5980_1;
|
||||
BitField<53, 2, u64> tab5980_0;
|
||||
} ffma;
|
||||
|
||||
union {
|
||||
@@ -509,6 +519,7 @@ union Instruction {
|
||||
union {
|
||||
BitField<0, 8, Register> gpr0;
|
||||
BitField<28, 8, Register> gpr28;
|
||||
BitField<49, 1, u64> nodep;
|
||||
BitField<50, 3, u64> component_mask_selector;
|
||||
BitField<53, 4, u64> texture_info;
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
@@ -136,7 +136,7 @@ private:
|
||||
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
||||
|
||||
/// Mapping of command subchannels to their bound engine ids.
|
||||
std::unordered_map<u32, EngineID> bound_engines;
|
||||
std::array<EngineID, 8> bound_engines = {};
|
||||
|
||||
/// 3D engine
|
||||
std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
|
||||
|
||||
90
src/video_core/renderer_opengl/gl_buffer_cache.cpp
Normal file
90
src/video_core/renderer_opengl/gl_buffer_cache.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
OGLBufferCache::OGLBufferCache(size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {}
|
||||
|
||||
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, size_t alignment,
|
||||
bool cache) {
|
||||
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
|
||||
const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
||||
|
||||
// Cache management is a big overhead, so only cache entries with a given size.
|
||||
// TODO: Figure out which size is the best for given games.
|
||||
cache &= size >= 2048;
|
||||
|
||||
if (cache) {
|
||||
auto entry = TryGet(*cpu_addr);
|
||||
if (entry) {
|
||||
if (entry->size >= size && entry->alignment == alignment) {
|
||||
return entry->offset;
|
||||
}
|
||||
Unregister(entry);
|
||||
}
|
||||
}
|
||||
|
||||
AlignBuffer(alignment);
|
||||
GLintptr uploaded_offset = buffer_offset;
|
||||
|
||||
Memory::ReadBlock(*cpu_addr, buffer_ptr, size);
|
||||
|
||||
buffer_ptr += size;
|
||||
buffer_offset += size;
|
||||
|
||||
if (cache) {
|
||||
auto entry = std::make_shared<CachedBufferEntry>();
|
||||
entry->offset = uploaded_offset;
|
||||
entry->size = size;
|
||||
entry->alignment = alignment;
|
||||
entry->addr = *cpu_addr;
|
||||
Register(entry);
|
||||
}
|
||||
|
||||
return uploaded_offset;
|
||||
}
|
||||
|
||||
GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, size_t size, size_t alignment) {
|
||||
AlignBuffer(alignment);
|
||||
std::memcpy(buffer_ptr, raw_pointer, size);
|
||||
GLintptr uploaded_offset = buffer_offset;
|
||||
|
||||
buffer_ptr += size;
|
||||
buffer_offset += size;
|
||||
return uploaded_offset;
|
||||
}
|
||||
|
||||
void OGLBufferCache::Map(size_t max_size) {
|
||||
bool invalidate;
|
||||
std::tie(buffer_ptr, buffer_offset_base, invalidate) =
|
||||
stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4);
|
||||
buffer_offset = buffer_offset_base;
|
||||
|
||||
if (invalidate) {
|
||||
InvalidateAll();
|
||||
}
|
||||
}
|
||||
void OGLBufferCache::Unmap() {
|
||||
stream_buffer.Unmap(buffer_offset - buffer_offset_base);
|
||||
}
|
||||
|
||||
GLuint OGLBufferCache::GetHandle() {
|
||||
return stream_buffer.GetHandle();
|
||||
}
|
||||
|
||||
void OGLBufferCache::AlignBuffer(size_t alignment) {
|
||||
// Align the offset, not the mapped pointer
|
||||
GLintptr offset_aligned =
|
||||
static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment));
|
||||
buffer_ptr += offset_aligned - buffer_offset;
|
||||
buffer_offset = offset_aligned;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
57
src/video_core/renderer_opengl/gl_buffer_cache.h
Normal file
57
src/video_core/renderer_opengl/gl_buffer_cache.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
struct CachedBufferEntry final {
|
||||
VAddr GetAddr() const {
|
||||
return addr;
|
||||
}
|
||||
|
||||
size_t GetSizeInBytes() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
VAddr addr;
|
||||
size_t size;
|
||||
GLintptr offset;
|
||||
size_t alignment;
|
||||
};
|
||||
|
||||
class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
|
||||
public:
|
||||
OGLBufferCache(size_t size);
|
||||
|
||||
GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, size_t alignment = 4,
|
||||
bool cache = true);
|
||||
|
||||
GLintptr UploadHostMemory(const void* raw_pointer, size_t size, size_t alignment = 4);
|
||||
|
||||
void Map(size_t max_size);
|
||||
void Unmap();
|
||||
|
||||
GLuint GetHandle();
|
||||
|
||||
protected:
|
||||
void AlignBuffer(size_t alignment);
|
||||
|
||||
private:
|
||||
OGLStreamBuffer stream_buffer;
|
||||
|
||||
u8* buffer_ptr;
|
||||
GLintptr buffer_offset;
|
||||
GLintptr buffer_offset_base;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -33,14 +33,17 @@ using PixelFormat = SurfaceParams::PixelFormat;
|
||||
using SurfaceType = SurfaceParams::SurfaceType;
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_FS, "OpenGL", "Fragment Shader Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Texture, "OpenGL", "Texture Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Framebuffer, "OpenGL", "Framebuffer Setup", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
|
||||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
|
||||
: emu_window{window}, screen_info{info}, stream_buffer(GL_ARRAY_BUFFER, STREAM_BUFFER_SIZE) {
|
||||
: emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) {
|
||||
// Create sampler objects
|
||||
for (size_t i = 0; i < texture_samplers.size(); ++i) {
|
||||
texture_samplers[i].Create();
|
||||
@@ -80,14 +83,14 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
|
||||
|
||||
hw_vao.Create();
|
||||
|
||||
state.draw.vertex_buffer = stream_buffer.GetHandle();
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
|
||||
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
|
||||
state.draw.shader_program = 0;
|
||||
state.draw.vertex_array = hw_vao.handle;
|
||||
state.Apply();
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle());
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle());
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
@@ -98,14 +101,13 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
|
||||
|
||||
RasterizerOpenGL::~RasterizerOpenGL() {}
|
||||
|
||||
std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
GLintptr buffer_offset) {
|
||||
void RasterizerOpenGL::SetupVertexArrays() {
|
||||
MICROPROFILE_SCOPE(OpenGL_VAO);
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
state.draw.vertex_array = hw_vao.handle;
|
||||
state.draw.vertex_buffer = stream_buffer.GetHandle();
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
state.Apply();
|
||||
|
||||
// Upload all guest vertex arrays sequentially to our buffer
|
||||
@@ -124,12 +126,10 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
ASSERT(end > start);
|
||||
u64 size = end - start + 1;
|
||||
|
||||
GLintptr vertex_buffer_offset;
|
||||
std::tie(array_ptr, buffer_offset, vertex_buffer_offset) =
|
||||
UploadMemory(array_ptr, buffer_offset, start, size);
|
||||
GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
|
||||
|
||||
// Bind the vertex array to the buffer at the current offset.
|
||||
glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset,
|
||||
glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset,
|
||||
vertex_array.stride);
|
||||
|
||||
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
|
||||
@@ -174,11 +174,10 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
}
|
||||
glVertexAttribBinding(index, attrib.buffer);
|
||||
}
|
||||
|
||||
return {array_ptr, buffer_offset};
|
||||
}
|
||||
|
||||
std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
|
||||
void RasterizerOpenGL::SetupShaders() {
|
||||
MICROPROFILE_SCOPE(OpenGL_Shader);
|
||||
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
|
||||
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
|
||||
@@ -195,21 +194,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
|
||||
continue;
|
||||
}
|
||||
|
||||
std::tie(buffer_ptr, buffer_offset) =
|
||||
AlignBuffer(buffer_ptr, buffer_offset, static_cast<size_t>(uniform_buffer_alignment));
|
||||
|
||||
const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
|
||||
|
||||
GLShader::MaxwellUniformData ubo{};
|
||||
ubo.SetFromRegs(gpu.state.shader_stages[stage]);
|
||||
std::memcpy(buffer_ptr, &ubo, sizeof(ubo));
|
||||
GLintptr offset = buffer_cache.UploadHostMemory(
|
||||
&ubo, sizeof(ubo), static_cast<size_t>(uniform_buffer_alignment));
|
||||
|
||||
// Bind the buffer
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, stage, stream_buffer.GetHandle(), buffer_offset,
|
||||
sizeof(ubo));
|
||||
|
||||
buffer_ptr += sizeof(ubo);
|
||||
buffer_offset += sizeof(ubo);
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo));
|
||||
|
||||
Shader shader{shader_cache.GetStageProgram(program)};
|
||||
|
||||
@@ -230,9 +223,8 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
|
||||
}
|
||||
|
||||
// Configure the const buffers for this shader stage.
|
||||
std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) =
|
||||
SetupConstBuffers(buffer_ptr, buffer_offset, static_cast<Maxwell::ShaderStage>(stage),
|
||||
shader, current_constbuffer_bindpoint);
|
||||
current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage),
|
||||
shader, current_constbuffer_bindpoint);
|
||||
|
||||
// Configure the textures for this shader stage.
|
||||
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
|
||||
@@ -246,8 +238,6 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
|
||||
}
|
||||
|
||||
shader_program_manager->UseTrivialGeometryShader();
|
||||
|
||||
return {buffer_ptr, buffer_offset};
|
||||
}
|
||||
|
||||
size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
|
||||
@@ -312,6 +302,7 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
|
||||
std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb,
|
||||
bool using_depth_fb,
|
||||
bool preserve_contents) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) {
|
||||
@@ -434,31 +425,6 @@ void RasterizerOpenGL::Clear() {
|
||||
glClear(clear_mask);
|
||||
}
|
||||
|
||||
std::pair<u8*, GLintptr> RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset,
|
||||
size_t alignment) {
|
||||
// Align the offset, not the mapped pointer
|
||||
GLintptr offset_aligned =
|
||||
static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment));
|
||||
return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned};
|
||||
}
|
||||
|
||||
std::tuple<u8*, GLintptr, GLintptr> RasterizerOpenGL::UploadMemory(u8* buffer_ptr,
|
||||
GLintptr buffer_offset,
|
||||
Tegra::GPUVAddr gpu_addr,
|
||||
size_t size, size_t alignment) {
|
||||
std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, alignment);
|
||||
GLintptr uploaded_offset = buffer_offset;
|
||||
|
||||
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
|
||||
const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
||||
Memory::ReadBlock(*cpu_addr, buffer_ptr, size);
|
||||
|
||||
buffer_ptr += size;
|
||||
buffer_offset += size;
|
||||
|
||||
return {buffer_ptr, buffer_offset, uploaded_offset};
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::DrawArrays() {
|
||||
if (accelerate_draw == AccelDraw::Disabled)
|
||||
return;
|
||||
@@ -484,7 +450,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()};
|
||||
|
||||
state.draw.vertex_buffer = stream_buffer.GetHandle();
|
||||
state.draw.vertex_buffer = buffer_cache.GetHandle();
|
||||
state.Apply();
|
||||
|
||||
size_t buffer_size = CalculateVertexArraysSize();
|
||||
@@ -501,24 +467,21 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
// Add space for at least 18 constant buffers
|
||||
buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment);
|
||||
|
||||
u8* buffer_ptr;
|
||||
GLintptr buffer_offset;
|
||||
std::tie(buffer_ptr, buffer_offset, std::ignore) =
|
||||
stream_buffer.Map(static_cast<GLsizeiptr>(buffer_size), 4);
|
||||
u8* buffer_ptr_base = buffer_ptr;
|
||||
buffer_cache.Map(buffer_size);
|
||||
|
||||
std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset);
|
||||
SetupVertexArrays();
|
||||
|
||||
// If indexed mode, copy the index buffer
|
||||
GLintptr index_buffer_offset = 0;
|
||||
if (is_indexed) {
|
||||
std::tie(buffer_ptr, buffer_offset, index_buffer_offset) = UploadMemory(
|
||||
buffer_ptr, buffer_offset, regs.index_array.StartAddress(), index_buffer_size);
|
||||
MICROPROFILE_SCOPE(OpenGL_Index);
|
||||
index_buffer_offset =
|
||||
buffer_cache.UploadMemory(regs.index_array.StartAddress(), index_buffer_size);
|
||||
}
|
||||
|
||||
std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset);
|
||||
SetupShaders();
|
||||
|
||||
stream_buffer.Unmap(buffer_ptr - buffer_ptr_base);
|
||||
buffer_cache.Unmap();
|
||||
|
||||
shader_program_manager->ApplyTo(state);
|
||||
state.Apply();
|
||||
@@ -563,6 +526,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
res_cache.InvalidateRegion(addr, size);
|
||||
shader_cache.InvalidateRegion(addr, size);
|
||||
buffer_cache.InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
|
||||
@@ -652,11 +616,9 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_ptr,
|
||||
GLintptr buffer_offset,
|
||||
Maxwell::ShaderStage stage,
|
||||
Shader& shader,
|
||||
u32 current_bindpoint) {
|
||||
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
|
||||
u32 current_bindpoint) {
|
||||
MICROPROFILE_SCOPE(OpenGL_UBO);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
|
||||
@@ -692,26 +654,25 @@ std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_pt
|
||||
size = Common::AlignUp(size, sizeof(GLvec4));
|
||||
ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
|
||||
|
||||
GLintptr const_buffer_offset;
|
||||
std::tie(buffer_ptr, buffer_offset, const_buffer_offset) =
|
||||
UploadMemory(buffer_ptr, buffer_offset, buffer.address, size,
|
||||
static_cast<size_t>(uniform_buffer_alignment));
|
||||
GLintptr const_buffer_offset = buffer_cache.UploadMemory(
|
||||
buffer.address, size, static_cast<size_t>(uniform_buffer_alignment));
|
||||
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint,
|
||||
stream_buffer.GetHandle(), const_buffer_offset, size);
|
||||
buffer_cache.GetHandle(), const_buffer_offset, size);
|
||||
|
||||
// Now configure the bindpoint of the buffer inside the shader
|
||||
glUniformBlockBinding(shader->GetProgramHandle(),
|
||||
shader->GetProgramResourceIndex(used_buffer.GetName()),
|
||||
shader->GetProgramResourceIndex(used_buffer),
|
||||
current_bindpoint + bindpoint);
|
||||
}
|
||||
|
||||
state.Apply();
|
||||
|
||||
return {buffer_ptr, buffer_offset, current_bindpoint + static_cast<u32>(entries.size())};
|
||||
return current_bindpoint + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
const auto& entries = shader->GetShaderEntries().texture_samplers;
|
||||
@@ -725,7 +686,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
|
||||
// Bind the uniform to the sampler.
|
||||
|
||||
glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry.GetName()),
|
||||
glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry),
|
||||
current_bindpoint);
|
||||
|
||||
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
@@ -109,9 +111,8 @@ private:
|
||||
* @param current_bindpoint The offset at which to start counting new buffer bindpoints.
|
||||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
std::tuple<u8*, GLintptr, u32> SetupConstBuffers(
|
||||
u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
Shader& shader, u32 current_bindpoint);
|
||||
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
u32 current_bindpoint);
|
||||
|
||||
/*
|
||||
* Configures the current textures to use for the draw command.
|
||||
@@ -173,22 +174,16 @@ private:
|
||||
std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers;
|
||||
|
||||
static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
OGLStreamBuffer stream_buffer;
|
||||
OGLBufferCache buffer_cache;
|
||||
OGLBuffer uniform_buffer;
|
||||
OGLFramebuffer framebuffer;
|
||||
GLint uniform_buffer_alignment;
|
||||
|
||||
size_t CalculateVertexArraysSize() const;
|
||||
|
||||
std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset);
|
||||
void SetupVertexArrays();
|
||||
|
||||
std::pair<u8*, GLintptr> SetupShaders(u8* buffer_ptr, GLintptr buffer_offset);
|
||||
|
||||
std::pair<u8*, GLintptr> AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment);
|
||||
|
||||
std::tuple<u8*, GLintptr, GLintptr> UploadMemory(u8* buffer_ptr, GLintptr buffer_offset,
|
||||
Tegra::GPUVAddr gpu_addr, size_t size,
|
||||
size_t alignment = 4);
|
||||
void SetupShaders();
|
||||
|
||||
enum class AccelDraw { Disabled, Arrays, Indexed };
|
||||
AccelDraw accelerate_draw = AccelDraw::Disabled;
|
||||
|
||||
@@ -85,23 +85,23 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
||||
SetShaderUniformBlockBindings(program.handle);
|
||||
}
|
||||
|
||||
GLuint CachedShader::GetProgramResourceIndex(const std::string& name) {
|
||||
auto search{resource_cache.find(name)};
|
||||
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
|
||||
auto search{resource_cache.find(buffer.GetHash())};
|
||||
if (search == resource_cache.end()) {
|
||||
const GLuint index{
|
||||
glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, name.c_str())};
|
||||
resource_cache[name] = index;
|
||||
glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, buffer.GetName().c_str())};
|
||||
resource_cache[buffer.GetHash()] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
return search->second;
|
||||
}
|
||||
|
||||
GLint CachedShader::GetUniformLocation(const std::string& name) {
|
||||
auto search{uniform_cache.find(name)};
|
||||
GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
|
||||
auto search{uniform_cache.find(sampler.GetHash())};
|
||||
if (search == uniform_cache.end()) {
|
||||
const GLint index{glGetUniformLocation(program.handle, name.c_str())};
|
||||
uniform_cache[name] = index;
|
||||
const GLint index{glGetUniformLocation(program.handle, sampler.GetName().c_str())};
|
||||
uniform_cache[sampler.GetHash()] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
@@ -43,10 +43,10 @@ public:
|
||||
}
|
||||
|
||||
/// Gets the GL program resource location for the specified resource, caching as needed
|
||||
GLuint GetProgramResourceIndex(const std::string& name);
|
||||
GLuint GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer);
|
||||
|
||||
/// Gets the GL uniform location for the specified resource, caching as needed
|
||||
GLint GetUniformLocation(const std::string& name);
|
||||
GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
|
||||
|
||||
private:
|
||||
VAddr addr;
|
||||
@@ -55,8 +55,8 @@ private:
|
||||
GLShader::ShaderEntries entries;
|
||||
OGLProgram program;
|
||||
|
||||
std::unordered_map<std::string, GLuint> resource_cache;
|
||||
std::unordered_map<std::string, GLint> uniform_cache;
|
||||
std::map<u32, GLuint> resource_cache;
|
||||
std::map<u32, GLint> uniform_cache;
|
||||
};
|
||||
|
||||
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
|
||||
|
||||
@@ -441,7 +441,7 @@ public:
|
||||
declarations.AddNewLine();
|
||||
|
||||
// Append the sampler2D array for the used textures.
|
||||
size_t num_samplers = GetSamplers().size();
|
||||
const size_t num_samplers = used_samplers.size();
|
||||
if (num_samplers > 0) {
|
||||
declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' +
|
||||
std::to_string(num_samplers) + "];");
|
||||
@@ -887,6 +887,8 @@ private:
|
||||
// TEXS has two destination registers and a swizzle. The first two elements in the swizzle
|
||||
// go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
|
||||
|
||||
ASSERT_MSG(instr.texs.nodep == 0, "TEXS nodep not implemented");
|
||||
|
||||
size_t written_components = 0;
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
if (!instr.texs.IsComponentEnabled(component)) {
|
||||
@@ -1038,6 +1040,15 @@ private:
|
||||
case OpCode::Id::FMUL_R:
|
||||
case OpCode::Id::FMUL_IMM: {
|
||||
// FMUL does not have 'abs' bits and only the second operand has a 'neg' bit.
|
||||
ASSERT_MSG(instr.fmul.tab5cb8_2 == 0, "FMUL tab5cb8_2({}) is not implemented",
|
||||
instr.fmul.tab5cb8_2.Value());
|
||||
ASSERT_MSG(instr.fmul.tab5c68_1 == 0, "FMUL tab5cb8_1({}) is not implemented",
|
||||
instr.fmul.tab5c68_1.Value());
|
||||
ASSERT_MSG(instr.fmul.tab5c68_0 == 1, "FMUL tab5cb8_0({}) is not implemented",
|
||||
instr.fmul.tab5c68_0
|
||||
.Value()); // SMO typical sends 1 here which seems to be the default
|
||||
ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented");
|
||||
|
||||
op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
|
||||
instr.alu.saturate_d);
|
||||
@@ -1436,6 +1447,12 @@ private:
|
||||
std::string op_b = instr.ffma.negate_b ? "-" : "";
|
||||
std::string op_c = instr.ffma.negate_c ? "-" : "";
|
||||
|
||||
ASSERT_MSG(instr.ffma.cc == 0, "FFMA cc not implemented");
|
||||
ASSERT_MSG(instr.ffma.tab5980_0 == 1, "FFMA tab5980_0({}) not implemented",
|
||||
instr.ffma.tab5980_0.Value()); // Seems to be 1 by default based on SMO
|
||||
ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented",
|
||||
instr.ffma.tab5980_1.Value());
|
||||
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::FFMA_CR: {
|
||||
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
|
||||
@@ -2110,8 +2127,12 @@ private:
|
||||
case OpCode::Id::IPA: {
|
||||
const auto& attribute = instr.attribute.fmt28;
|
||||
const auto& reg = instr.gpr0;
|
||||
switch (instr.ipa.mode) {
|
||||
case Tegra::Shader::IpaMode::Pass:
|
||||
ASSERT_MSG(instr.ipa.sample_mode == Tegra::Shader::IpaSampleMode::Default,
|
||||
"Unhandled IPA sample mode: {}",
|
||||
static_cast<u32>(instr.ipa.sample_mode.Value()));
|
||||
ASSERT_MSG(instr.ipa.saturate == 0, "IPA saturate not implemented");
|
||||
switch (instr.ipa.interp_mode) {
|
||||
case Tegra::Shader::IpaInterpMode::Linear:
|
||||
if (stage == Maxwell3D::Regs::ShaderStage::Fragment &&
|
||||
attribute.index == Attribute::Index::Position) {
|
||||
switch (attribute.element) {
|
||||
@@ -2132,12 +2153,12 @@ private:
|
||||
regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index);
|
||||
}
|
||||
break;
|
||||
case Tegra::Shader::IpaMode::None:
|
||||
case Tegra::Shader::IpaInterpMode::Perspective:
|
||||
regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled IPA mode: {}",
|
||||
static_cast<u32>(instr.ipa.mode.Value()));
|
||||
static_cast<u32>(instr.ipa.interp_mode.Value()));
|
||||
UNREACHABLE();
|
||||
regs.SetRegisterToInputAttibute(reg, attribute.element, attribute.index);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ public:
|
||||
return BufferBaseNames[static_cast<size_t>(stage)] + std::to_string(index);
|
||||
}
|
||||
|
||||
u32 GetHash() const {
|
||||
return (static_cast<u32>(stage) << 16) | index;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::array<const char*, Maxwell::MaxShaderStage> BufferBaseNames = {
|
||||
"buffer_vs_c", "buffer_tessc_c", "buffer_tesse_c", "buffer_gs_c", "buffer_fs_c",
|
||||
@@ -89,6 +93,10 @@ public:
|
||||
std::to_string(sampler_index) + ']';
|
||||
}
|
||||
|
||||
u32 GetHash() const {
|
||||
return (static_cast<u32>(stage) << 16) | static_cast<u32>(sampler_index);
|
||||
}
|
||||
|
||||
static std::string GetArrayName(Maxwell::ShaderStage stage) {
|
||||
return TextureSamplerNames[static_cast<size_t>(stage)];
|
||||
}
|
||||
|
||||
@@ -102,6 +102,20 @@ void Config::ReadValues() {
|
||||
|
||||
qt_config->beginGroup("Data Storage");
|
||||
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
|
||||
FileUtil::GetUserPath(
|
||||
FileUtil::UserPath::NANDDir,
|
||||
qt_config
|
||||
->value("nand_directory",
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
FileUtil::GetUserPath(
|
||||
FileUtil::UserPath::SDMCDir,
|
||||
qt_config
|
||||
->value("sdmc_directory",
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
|
||||
.toString()
|
||||
.toStdString());
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
@@ -222,6 +236,10 @@ void Config::SaveValues() {
|
||||
|
||||
qt_config->beginGroup("Data Storage");
|
||||
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
|
||||
qt_config->setValue("nand_directory",
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
|
||||
qt_config->setValue("sdmc_directory",
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
|
||||
@@ -13,13 +13,14 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QThreadPool>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
@@ -431,7 +432,7 @@ void GameList::LoadInterfaceLayout() {
|
||||
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
|
||||
}
|
||||
|
||||
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
|
||||
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
|
||||
|
||||
static bool HasSupportedFileExtension(const std::string& file_name) {
|
||||
const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
class GameListWorker;
|
||||
class GMainWindow;
|
||||
|
||||
|
||||
@@ -4,18 +4,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
#include <QRunnable>
|
||||
#include <QStandardItem>
|
||||
#include <QString>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
|
||||
#define QT_NO_OPENGL
|
||||
#include <QDesktopWidget>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -29,9 +31,12 @@
|
||||
#include "core/core.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
@@ -73,6 +78,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
*/
|
||||
enum class CalloutFlag : uint32_t {
|
||||
Telemetry = 0x1,
|
||||
DRDDeprecation = 0x2,
|
||||
};
|
||||
|
||||
static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
|
||||
@@ -372,6 +378,10 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
&GMainWindow::OnMenuInstallToNAND);
|
||||
connect(ui.action_Select_Game_List_Root, &QAction::triggered, this,
|
||||
&GMainWindow::OnMenuSelectGameListRoot);
|
||||
connect(ui.action_Select_NAND_Directory, &QAction::triggered, this,
|
||||
[this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::NAND); });
|
||||
connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
|
||||
[this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
|
||||
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
|
||||
|
||||
// Emulation
|
||||
@@ -481,6 +491,23 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
|
||||
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
|
||||
|
||||
const auto drd_callout =
|
||||
(UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
|
||||
|
||||
if (result == Core::System::ResultStatus::Success &&
|
||||
system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory &&
|
||||
drd_callout) {
|
||||
UISettings::values.callout_flags |= static_cast<u32>(CalloutFlag::DRDDeprecation);
|
||||
QMessageBox::warning(
|
||||
this, tr("Warning Outdated Game Format"),
|
||||
tr("You are using the deconstructed ROM directory format for this game, which is an "
|
||||
"outdated format that has been superseded by others such as NCA, NAX, XCI, or "
|
||||
"NSP. Deconstructed ROM directories lack icons, metadata, and update "
|
||||
"support.<br><br>For an explanation of the various Switch formats yuzu supports, <a "
|
||||
"href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our "
|
||||
"wiki</a>. This message will not be shown again."));
|
||||
}
|
||||
|
||||
render_window->DoneCurrent();
|
||||
|
||||
if (result != Core::System::ResultStatus::Success) {
|
||||
@@ -739,7 +766,8 @@ void GMainWindow::OnMenuLoadFolder() {
|
||||
|
||||
void GMainWindow::OnMenuInstallToNAND() {
|
||||
const QString file_filter =
|
||||
tr("Installable Switch File (*.nca *.xci);;Nintendo Content Archive (*.nca);;NX Cartridge "
|
||||
tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
|
||||
"(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge "
|
||||
"Image (*.xci)");
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Install File"),
|
||||
UISettings::values.roms_path, file_filter);
|
||||
@@ -799,22 +827,34 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||
QMessageBox::Yes;
|
||||
};
|
||||
|
||||
if (filename.endsWith("xci", Qt::CaseInsensitive)) {
|
||||
const auto xci = std::make_shared<FileSys::XCI>(
|
||||
vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
|
||||
if (xci->GetStatus() != Loader::ResultStatus::Success) {
|
||||
if (filename.endsWith("xci", Qt::CaseInsensitive) ||
|
||||
filename.endsWith("nsp", Qt::CaseInsensitive)) {
|
||||
|
||||
std::shared_ptr<FileSys::NSP> nsp;
|
||||
if (filename.endsWith("nsp", Qt::CaseInsensitive)) {
|
||||
nsp = std::make_shared<FileSys::NSP>(
|
||||
vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
|
||||
if (nsp->IsExtractedType())
|
||||
failed();
|
||||
} else {
|
||||
const auto xci = std::make_shared<FileSys::XCI>(
|
||||
vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
|
||||
nsp = xci->GetSecurePartitionNSP();
|
||||
}
|
||||
|
||||
if (nsp->GetStatus() != Loader::ResultStatus::Success) {
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
const auto res =
|
||||
Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy);
|
||||
Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy);
|
||||
if (res == FileSys::InstallResult::Success) {
|
||||
success();
|
||||
} else {
|
||||
if (res == FileSys::InstallResult::ErrorAlreadyExists) {
|
||||
if (overwrite()) {
|
||||
const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
|
||||
xci, true, qt_raw_copy);
|
||||
nsp, true, qt_raw_copy);
|
||||
if (res2 == FileSys::InstallResult::Success) {
|
||||
success();
|
||||
} else {
|
||||
@@ -887,6 +927,28 @@ void GMainWindow::OnMenuSelectGameListRoot() {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) {
|
||||
const auto res = QMessageBox::information(
|
||||
this, tr("Changing Emulated Directory"),
|
||||
tr("You are about to change the emulated %1 directory of the system. Please note "
|
||||
"that this does not also move the contents of the previous directory to the "
|
||||
"new one and you will have to do that yourself.")
|
||||
.arg(target == EmulatedDirectoryTarget::SDMC ? tr("SD card") : tr("NAND")),
|
||||
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
|
||||
|
||||
if (res == QMessageBox::Cancel)
|
||||
return;
|
||||
|
||||
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
|
||||
if (!dir_path.isEmpty()) {
|
||||
FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
|
||||
: FileUtil::UserPath::NANDDir,
|
||||
dir_path.toStdString());
|
||||
Service::FileSystem::CreateFactories(vfs);
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuRecentFile() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
assert(action);
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QTimer>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "ui_main.h"
|
||||
#include "yuzu/hotkeys.h"
|
||||
@@ -32,6 +35,11 @@ namespace Tegra {
|
||||
class DebugContext;
|
||||
}
|
||||
|
||||
enum class EmulatedDirectoryTarget {
|
||||
NAND,
|
||||
SDMC,
|
||||
};
|
||||
|
||||
class GMainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -137,6 +145,8 @@ private slots:
|
||||
void OnMenuInstallToNAND();
|
||||
/// Called whenever a user selects the "File->Select Game List Root" menu item
|
||||
void OnMenuSelectGameListRoot();
|
||||
/// Called whenever a user select the "File->Select -- Directory" where -- is NAND or SD Card
|
||||
void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
|
||||
void OnMenuRecentFile();
|
||||
void OnConfigure();
|
||||
void OnAbout();
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
<addaction name="action_Select_Game_List_Root"/>
|
||||
<addaction name="menu_recent_files"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Select_NAND_Directory"/>
|
||||
<addaction name="action_Select_SDMC_Directory"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Exit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Emulation">
|
||||
@@ -204,6 +207,22 @@
|
||||
<string>Selects a folder to display in the game list</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Select_NAND_Directory">
|
||||
<property name="text">
|
||||
<string>Select NAND Directory...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Selects a folder to use as the root of the emulated NAND</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Select_SDMC_Directory">
|
||||
<property name="text">
|
||||
<string>Select SD Card Directory...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Selects a folder to use as the root of the emulated SD card</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Fullscreen">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -114,6 +114,12 @@ void Config::ReadValues() {
|
||||
// Data Storage
|
||||
Settings::values.use_virtual_sd =
|
||||
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
|
||||
sdl2_config->Get("Data Storage", "nand_directory",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
|
||||
sdl2_config->Get("Data Storage", "nand_directory",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
|
||||
|
||||
// System
|
||||
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
Reference in New Issue
Block a user