Compare commits
15 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8f641a52d | ||
|
|
e85a528bb9 | ||
|
|
a4b2af7382 | ||
|
|
c994cdc532 | ||
|
|
5ee4c49c30 | ||
|
|
7841447cf0 | ||
|
|
3618a68f93 | ||
|
|
ba2fb83d60 | ||
|
|
5fc99553d2 | ||
|
|
3e2b32a3ee | ||
|
|
7dde4b7a77 | ||
|
|
0b1c2e5505 | ||
|
|
148a5bef7e | ||
|
|
0081252d31 | ||
|
|
398444e676 |
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 7ea1241953...fc6b73bd85
@@ -792,66 +792,80 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitPathComponents(const std::string& filename) {
|
||||
auto copy(filename);
|
||||
std::vector<std::string> SplitPathComponents(std::string_view filename) {
|
||||
std::string copy(filename);
|
||||
std::replace(copy.begin(), copy.end(), '\\', '/');
|
||||
std::vector<std::string> out;
|
||||
|
||||
std::stringstream stream(filename);
|
||||
std::stringstream stream(copy);
|
||||
std::string item;
|
||||
while (std::getline(stream, item, '/'))
|
||||
while (std::getline(stream, item, '/')) {
|
||||
out.push_back(std::move(item));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string GetParentPath(const std::string& path) {
|
||||
auto out = path;
|
||||
const auto name_bck_index = out.find_last_of('\\');
|
||||
const auto name_fwd_index = out.find_last_of('/');
|
||||
std::string_view GetParentPath(std::string_view path) {
|
||||
const auto name_bck_index = path.rfind('\\');
|
||||
const auto name_fwd_index = path.rfind('/');
|
||||
size_t name_index;
|
||||
if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos)
|
||||
name_index = std::min<size_t>(name_bck_index, name_fwd_index);
|
||||
else
|
||||
name_index = std::max<size_t>(name_bck_index, name_fwd_index);
|
||||
|
||||
return out.erase(name_index);
|
||||
if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
|
||||
name_index = std::min(name_bck_index, name_fwd_index);
|
||||
} else {
|
||||
name_index = std::max(name_bck_index, name_fwd_index);
|
||||
}
|
||||
|
||||
return path.substr(0, name_index);
|
||||
}
|
||||
|
||||
std::string GetPathWithoutTop(std::string path) {
|
||||
if (path.empty())
|
||||
return "";
|
||||
while (path[0] == '\\' || path[0] == '/') {
|
||||
path = path.substr(1);
|
||||
if (path.empty())
|
||||
return "";
|
||||
std::string_view GetPathWithoutTop(std::string_view path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
const auto name_bck_index = path.find_first_of('\\');
|
||||
const auto name_fwd_index = path.find_first_of('/');
|
||||
|
||||
while (path[0] == '\\' || path[0] == '/') {
|
||||
path.remove_suffix(1);
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
const auto name_bck_index = path.find('\\');
|
||||
const auto name_fwd_index = path.find('/');
|
||||
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
|
||||
}
|
||||
|
||||
std::string GetFilename(std::string path) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
auto name_index = path.find_last_of('/');
|
||||
if (name_index == std::string::npos)
|
||||
return "";
|
||||
std::string_view GetFilename(std::string_view path) {
|
||||
const auto name_index = path.find_last_of("\\/");
|
||||
|
||||
if (name_index == std::string_view::npos) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return path.substr(name_index + 1);
|
||||
}
|
||||
|
||||
std::string GetExtensionFromFilename(const std::string& name) {
|
||||
size_t index = name.find_last_of('.');
|
||||
if (index == std::string::npos)
|
||||
return "";
|
||||
std::string_view GetExtensionFromFilename(std::string_view name) {
|
||||
const size_t index = name.rfind('.');
|
||||
|
||||
if (index == std::string_view::npos) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return name.substr(index + 1);
|
||||
}
|
||||
|
||||
std::string RemoveTrailingSlash(const std::string& path) {
|
||||
if (path.empty())
|
||||
std::string_view RemoveTrailingSlash(std::string_view path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
if (path.back() == '\\' || path.back() == '/')
|
||||
return path.substr(0, path.size() - 1);
|
||||
}
|
||||
|
||||
if (path.back() == '\\' || path.back() == '/') {
|
||||
path.remove_suffix(1);
|
||||
return path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
@@ -151,22 +152,22 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||
|
||||
// Splits the path on '/' or '\' and put the components into a vector
|
||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
||||
std::vector<std::string> SplitPathComponents(const std::string& filename);
|
||||
std::vector<std::string> SplitPathComponents(std::string_view filename);
|
||||
|
||||
// Gets all of the text up to the last '/' or '\' in the path.
|
||||
std::string GetParentPath(const std::string& path);
|
||||
std::string_view GetParentPath(std::string_view path);
|
||||
|
||||
// Gets all of the text after the first '/' or '\' in the path.
|
||||
std::string GetPathWithoutTop(std::string path);
|
||||
std::string_view GetPathWithoutTop(std::string_view path);
|
||||
|
||||
// Gets the filename of the path
|
||||
std::string GetFilename(std::string path);
|
||||
std::string_view GetFilename(std::string_view path);
|
||||
|
||||
// Gets the extension of the filename
|
||||
std::string GetExtensionFromFilename(const std::string& name);
|
||||
std::string_view GetExtensionFromFilename(std::string_view name);
|
||||
|
||||
// Removes the final '/' or '\' if one exists
|
||||
std::string RemoveTrailingSlash(const std::string& path);
|
||||
std::string_view RemoveTrailingSlash(std::string_view path);
|
||||
|
||||
// Creates a new vector containing indices [first, last) from the original.
|
||||
template <typename T>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
add_library(core STATIC
|
||||
arm/arm_interface.h
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
arm/unicorn/arm_unicorn.h
|
||||
core.cpp
|
||||
@@ -10,6 +12,8 @@ add_library(core STATIC
|
||||
core_timing.h
|
||||
file_sys/content_archive.cpp
|
||||
file_sys/content_archive.h
|
||||
file_sys/control_metadata.cpp
|
||||
file_sys/control_metadata.h
|
||||
file_sys/directory.h
|
||||
file_sys/errors.h
|
||||
file_sys/mode.h
|
||||
|
||||
@@ -102,18 +102,28 @@ public:
|
||||
u64 tpidr_el0 = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
|
||||
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() {
|
||||
const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
|
||||
|
||||
Dynarmic::A64::UserConfig config;
|
||||
|
||||
// Callbacks
|
||||
config.callbacks = cb.get();
|
||||
|
||||
// Memory
|
||||
config.page_table = reinterpret_cast<void**>(page_table);
|
||||
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
|
||||
config.silently_mirror_page_table = false;
|
||||
|
||||
// Multi-process state
|
||||
config.processor_id = core_index;
|
||||
config.global_monitor = &exclusive_monitor->monitor;
|
||||
|
||||
// System registers
|
||||
config.tpidrro_el0 = &cb->tpidrro_el0;
|
||||
config.tpidr_el0 = &cb->tpidr_el0;
|
||||
config.dczid_el0 = 4;
|
||||
config.ctr_el0 = 0x8444c004;
|
||||
config.page_table = reinterpret_cast<void**>(page_table);
|
||||
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
|
||||
config.silently_mirror_page_table = false;
|
||||
|
||||
return std::make_unique<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
@@ -128,8 +138,11 @@ void ARM_Dynarmic::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic()
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
|
||||
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)),
|
||||
jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(
|
||||
exclusive_monitor)},
|
||||
core_index{core_index} {
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
inner_unicorn.SaveContext(ctx);
|
||||
LoadContext(ctx);
|
||||
@@ -237,6 +250,46 @@ void ARM_Dynarmic::ClearExclusiveState() {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged() {
|
||||
jit = MakeJit(cb);
|
||||
jit = MakeJit();
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
}
|
||||
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
|
||||
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
|
||||
|
||||
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, u64 addr) {
|
||||
// Size doesn't actually matter.
|
||||
monitor.Mark(core_index, addr, 16);
|
||||
}
|
||||
|
||||
void DynarmicExclusiveMonitor::ClearExclusive() {
|
||||
monitor.Clear();
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
|
||||
[&] { Memory::Write8(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
|
||||
[&] { Memory::Write16(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
|
||||
[&] { Memory::Write32(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
|
||||
[&] { Memory::Write64(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, u64 vaddr,
|
||||
std::array<std::uint64_t, 2> value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
|
||||
Memory::Write64(vaddr, value[0]);
|
||||
Memory::Write64(vaddr, value[1]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,15 +6,18 @@
|
||||
|
||||
#include <memory>
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/exclusive_monitor.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
|
||||
class ARM_Dynarmic_Callbacks;
|
||||
class DynarmicExclusiveMonitor;
|
||||
|
||||
class ARM_Dynarmic final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Dynarmic();
|
||||
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index);
|
||||
~ARM_Dynarmic();
|
||||
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
@@ -47,10 +50,35 @@ public:
|
||||
void PageTableChanged() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Dynarmic::A64::Jit> MakeJit();
|
||||
|
||||
friend class ARM_Dynarmic_Callbacks;
|
||||
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
size_t core_index;
|
||||
std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
|
||||
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
};
|
||||
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
public:
|
||||
explicit DynarmicExclusiveMonitor(size_t core_count);
|
||||
~DynarmicExclusiveMonitor();
|
||||
|
||||
void SetExclusive(size_t core_index, u64 addr) override;
|
||||
void ClearExclusive() override;
|
||||
|
||||
bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) override;
|
||||
bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) override;
|
||||
bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) override;
|
||||
bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) override;
|
||||
bool ExclusiveWrite128(size_t core_index, u64 vaddr,
|
||||
std::array<std::uint64_t, 2> value) override;
|
||||
|
||||
private:
|
||||
friend class ARM_Dynarmic;
|
||||
Dynarmic::A64::ExclusiveMonitor monitor;
|
||||
};
|
||||
|
||||
7
src/core/arm/exclusive_monitor.cpp
Normal file
7
src/core/arm/exclusive_monitor.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
|
||||
ExclusiveMonitor::~ExclusiveMonitor() = default;
|
||||
23
src/core/arm/exclusive_monitor.h
Normal file
23
src/core/arm/exclusive_monitor.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
|
||||
class ExclusiveMonitor {
|
||||
public:
|
||||
virtual ~ExclusiveMonitor();
|
||||
|
||||
virtual void SetExclusive(size_t core_index, u64 addr) = 0;
|
||||
virtual void ClearExclusive() = 0;
|
||||
|
||||
virtual bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) = 0;
|
||||
virtual bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) = 0;
|
||||
virtual bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) = 0;
|
||||
virtual bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) = 0;
|
||||
virtual bool ExclusiveWrite128(size_t core_index, u64 vaddr,
|
||||
std::array<std::uint64_t, 2> value) = 0;
|
||||
};
|
||||
@@ -171,8 +171,9 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
current_process = Kernel::Process::Create("main");
|
||||
|
||||
cpu_barrier = std::make_shared<CpuBarrier>();
|
||||
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
|
||||
for (size_t index = 0; index < cpu_cores.size(); ++index) {
|
||||
cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index);
|
||||
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
|
||||
}
|
||||
|
||||
gpu_core = std::make_unique<Tegra::GPU>();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
@@ -114,6 +115,11 @@ public:
|
||||
return CurrentCpuCore().ArmInterface();
|
||||
}
|
||||
|
||||
/// Gets the index of the currently running CPU core
|
||||
size_t CurrentCoreIndex() {
|
||||
return CurrentCpuCore().CoreIndex();
|
||||
}
|
||||
|
||||
/// Gets an ARM interface to the CPU core with the specified index
|
||||
ARM_Interface& ArmInterface(size_t core_index);
|
||||
|
||||
@@ -130,6 +136,11 @@ public:
|
||||
return *CurrentCpuCore().Scheduler();
|
||||
}
|
||||
|
||||
/// Gets the exclusive monitor
|
||||
ExclusiveMonitor& Monitor() {
|
||||
return *cpu_exclusive_monitor;
|
||||
}
|
||||
|
||||
/// Gets the scheduler for the CPU core with the specified index
|
||||
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
|
||||
|
||||
@@ -186,6 +197,7 @@ private:
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
Kernel::SharedPtr<Kernel::Process> current_process;
|
||||
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier;
|
||||
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
|
||||
|
||||
@@ -48,14 +48,15 @@ bool CpuBarrier::Rendezvous() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
|
||||
Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
|
||||
: cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
|
||||
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_shared<ARM_Dynarmic>();
|
||||
arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
|
||||
#else
|
||||
cpu_core = std::make_shared<ARM_Unicorn>();
|
||||
arm_interface = std::make_shared<ARM_Unicorn>();
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
} else {
|
||||
@@ -65,6 +66,18 @@ Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
|
||||
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
|
||||
}
|
||||
|
||||
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) {
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
|
||||
#else
|
||||
return nullptr; // TODO(merry): Passthrough exclusive monitor
|
||||
#endif
|
||||
} else {
|
||||
return nullptr; // TODO(merry): Passthrough exclusive monitor
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::RunLoop(bool tight_loop) {
|
||||
// Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
|
||||
if (!cpu_barrier->Rendezvous()) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
|
||||
class ARM_Interface;
|
||||
|
||||
@@ -40,7 +41,8 @@ private:
|
||||
|
||||
class Cpu {
|
||||
public:
|
||||
Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
|
||||
Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
|
||||
|
||||
void RunLoop(bool tight_loop = true);
|
||||
|
||||
@@ -64,6 +66,12 @@ public:
|
||||
return core_index == 0;
|
||||
}
|
||||
|
||||
size_t CoreIndex() const {
|
||||
return core_index;
|
||||
}
|
||||
|
||||
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores);
|
||||
|
||||
private:
|
||||
void Reschedule();
|
||||
|
||||
|
||||
42
src/core/file_sys/control_metadata.cpp
Normal file
42
src/core/file_sys/control_metadata.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::string LanguageEntry::GetApplicationName() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
|
||||
}
|
||||
|
||||
std::string LanguageEntry::GetDeveloperName() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100);
|
||||
}
|
||||
|
||||
NACP::NACP(VirtualFile file_) : file(std::move(file_)), raw(std::make_unique<RawNACP>()) {
|
||||
file->ReadObject(raw.get());
|
||||
}
|
||||
|
||||
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
|
||||
return raw->language_entries.at(static_cast<u8>(language));
|
||||
}
|
||||
|
||||
std::string NACP::GetApplicationName(Language language) const {
|
||||
return GetLanguageEntry(language).GetApplicationName();
|
||||
}
|
||||
|
||||
std::string NACP::GetDeveloperName(Language language) const {
|
||||
return GetLanguageEntry(language).GetDeveloperName();
|
||||
}
|
||||
|
||||
u64 NACP::GetTitleId() const {
|
||||
return raw->title_id;
|
||||
}
|
||||
|
||||
std::string NACP::GetVersionString() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10);
|
||||
}
|
||||
} // namespace FileSys
|
||||
81
src/core/file_sys/control_metadata.h
Normal file
81
src/core/file_sys/control_metadata.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
// A localized entry containing strings within the NACP.
|
||||
// One for each language of type Language.
|
||||
struct LanguageEntry {
|
||||
std::array<char, 0x200> application_name;
|
||||
std::array<char, 0x100> developer_name;
|
||||
|
||||
std::string GetApplicationName() const;
|
||||
std::string GetDeveloperName() const;
|
||||
};
|
||||
static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.");
|
||||
|
||||
// The raw file format of a NACP file.
|
||||
struct RawNACP {
|
||||
std::array<LanguageEntry, 16> language_entries;
|
||||
INSERT_PADDING_BYTES(0x38);
|
||||
u64_le title_id;
|
||||
INSERT_PADDING_BYTES(0x20);
|
||||
std::array<char, 0x10> version_string;
|
||||
u64_le dlc_base_title_id;
|
||||
u64_le title_id_2;
|
||||
INSERT_PADDING_BYTES(0x28);
|
||||
u64_le product_code;
|
||||
u64_le title_id_3;
|
||||
std::array<u64_le, 0x7> title_id_array;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
u64_le title_id_update;
|
||||
std::array<u8, 0x40> bcat_passphrase;
|
||||
INSERT_PADDING_BYTES(0xEC0);
|
||||
};
|
||||
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
|
||||
|
||||
// A language on the NX. These are for names and icons.
|
||||
enum class Language : u8 {
|
||||
AmericanEnglish = 0,
|
||||
BritishEnglish = 1,
|
||||
Japanese = 2,
|
||||
French = 3,
|
||||
German = 4,
|
||||
LatinAmericanSpanish = 5,
|
||||
Spanish = 6,
|
||||
Italian = 7,
|
||||
Dutch = 8,
|
||||
CanadianFrench = 9,
|
||||
Portugese = 10,
|
||||
Russian = 11,
|
||||
Korean = 12,
|
||||
Taiwanese = 13,
|
||||
Chinese = 14,
|
||||
};
|
||||
|
||||
// A class representing the format used by NX metadata files, typically named Control.nacp.
|
||||
// These store application name, dev name, title id, and other miscellaneous data.
|
||||
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;
|
||||
u64 GetTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
|
||||
private:
|
||||
VirtualFile file;
|
||||
std::unique_ptr<RawNACP> raw;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -13,7 +13,7 @@ namespace FileSys {
|
||||
VfsFile::~VfsFile() = default;
|
||||
|
||||
std::string VfsFile::GetExtension() const {
|
||||
return FileUtil::GetExtensionFromFilename(GetName());
|
||||
return std::string(FileUtil::GetExtensionFromFilename(GetName()));
|
||||
}
|
||||
|
||||
VfsDirectory::~VfsDirectory() = default;
|
||||
@@ -46,64 +46,80 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
|
||||
return Write(data.data(), data.size(), offset);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const {
|
||||
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
|
||||
auto vec = FileUtil::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty())
|
||||
if (vec.empty()) {
|
||||
return nullptr;
|
||||
if (vec.size() == 1)
|
||||
}
|
||||
|
||||
if (vec.size() == 1) {
|
||||
return GetFile(vec[0]);
|
||||
}
|
||||
|
||||
auto dir = GetSubdirectory(vec[0]);
|
||||
for (size_t component = 1; component < vec.size() - 1; ++component) {
|
||||
if (dir == nullptr)
|
||||
if (dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dir = dir->GetSubdirectory(vec[component]);
|
||||
}
|
||||
if (dir == nullptr)
|
||||
|
||||
if (dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dir->GetFile(vec.back());
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const {
|
||||
if (IsRoot())
|
||||
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) const {
|
||||
if (IsRoot()) {
|
||||
return GetFileRelative(path);
|
||||
}
|
||||
|
||||
return GetParentDirectory()->GetFileAbsolute(path);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const {
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
|
||||
auto vec = FileUtil::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty())
|
||||
if (vec.empty()) {
|
||||
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
|
||||
// because of const-ness
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto dir = GetSubdirectory(vec[0]);
|
||||
for (size_t component = 1; component < vec.size(); ++component) {
|
||||
if (dir == nullptr)
|
||||
if (dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dir = dir->GetSubdirectory(vec[component]);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const {
|
||||
if (IsRoot())
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(std::string_view path) const {
|
||||
if (IsRoot()) {
|
||||
return GetDirectoryRelative(path);
|
||||
}
|
||||
|
||||
return GetParentDirectory()->GetDirectoryAbsolute(path);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const {
|
||||
std::shared_ptr<VfsFile> VfsDirectory::GetFile(std::string_view name) const {
|
||||
const auto& files = GetFiles();
|
||||
const auto iter = std::find_if(files.begin(), files.end(),
|
||||
[&name](const auto& file1) { return name == file1->GetName(); });
|
||||
return iter == files.end() ? nullptr : *iter;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const {
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||
const auto& subs = GetSubdirectories();
|
||||
const auto iter = std::find_if(subs.begin(), subs.end(),
|
||||
[&name](const auto& file1) { return name == file1->GetName(); });
|
||||
@@ -128,77 +144,96 @@ size_t VfsDirectory::GetSize() const {
|
||||
return file_total + subdir_total;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& path) {
|
||||
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
|
||||
auto vec = FileUtil::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty())
|
||||
if (vec.empty()) {
|
||||
return nullptr;
|
||||
if (vec.size() == 1)
|
||||
}
|
||||
|
||||
if (vec.size() == 1) {
|
||||
return CreateFile(vec[0]);
|
||||
}
|
||||
|
||||
auto dir = GetSubdirectory(vec[0]);
|
||||
if (dir == nullptr) {
|
||||
dir = CreateSubdirectory(vec[0]);
|
||||
if (dir == nullptr)
|
||||
if (dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path));
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) {
|
||||
if (IsRoot())
|
||||
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) {
|
||||
if (IsRoot()) {
|
||||
return CreateFileRelative(path);
|
||||
}
|
||||
|
||||
return GetParentDirectory()->CreateFileAbsolute(path);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& path) {
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
|
||||
auto vec = FileUtil::SplitPathComponents(path);
|
||||
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
|
||||
vec.end());
|
||||
if (vec.empty())
|
||||
if (vec.empty()) {
|
||||
return nullptr;
|
||||
if (vec.size() == 1)
|
||||
}
|
||||
|
||||
if (vec.size() == 1) {
|
||||
return CreateSubdirectory(vec[0]);
|
||||
}
|
||||
|
||||
auto dir = GetSubdirectory(vec[0]);
|
||||
if (dir == nullptr) {
|
||||
dir = CreateSubdirectory(vec[0]);
|
||||
if (dir == nullptr)
|
||||
if (dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path));
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) {
|
||||
if (IsRoot())
|
||||
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
|
||||
if (IsRoot()) {
|
||||
return CreateDirectoryRelative(path);
|
||||
}
|
||||
|
||||
return GetParentDirectory()->CreateDirectoryAbsolute(path);
|
||||
}
|
||||
|
||||
bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) {
|
||||
bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||
auto dir = GetSubdirectory(name);
|
||||
if (dir == nullptr)
|
||||
if (dir == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
for (const auto& file : dir->GetFiles()) {
|
||||
if (!DeleteFile(file->GetName()))
|
||||
if (!DeleteFile(file->GetName())) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& sdir : dir->GetSubdirectories()) {
|
||||
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName()))
|
||||
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool VfsDirectory::Copy(const std::string& src, const std::string& dest) {
|
||||
bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
|
||||
const auto f1 = GetFile(src);
|
||||
auto f2 = CreateFile(dest);
|
||||
if (f1 == nullptr || f2 == nullptr)
|
||||
if (f1 == nullptr || f2 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f2->Resize(f1->GetSize())) {
|
||||
DeleteFile(dest);
|
||||
@@ -216,23 +251,23 @@ bool ReadOnlyVfsDirectory::IsReadable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) {
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) {
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) {
|
||||
bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) {
|
||||
bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::Rename(const std::string& name) {
|
||||
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "boost/optional.hpp"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
|
||||
namespace FileSys {
|
||||
struct VfsFile;
|
||||
@@ -112,7 +112,7 @@ struct VfsFile : NonCopyable {
|
||||
}
|
||||
|
||||
// Renames the file to name. Returns whether or not the operation was successsful.
|
||||
virtual bool Rename(const std::string& name) = 0;
|
||||
virtual bool Rename(std::string_view name) = 0;
|
||||
};
|
||||
|
||||
// A class representing a directory in an abstract filesystem.
|
||||
@@ -121,27 +121,27 @@ struct VfsDirectory : NonCopyable {
|
||||
|
||||
// Retrives the file located at path as if the current directory was root. Returns nullptr if
|
||||
// not found.
|
||||
virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const;
|
||||
virtual std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const;
|
||||
// Calls GetFileRelative(path) on the root of the current directory.
|
||||
virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const;
|
||||
virtual std::shared_ptr<VfsFile> GetFileAbsolute(std::string_view path) const;
|
||||
|
||||
// Retrives the directory located at path as if the current directory was root. Returns nullptr
|
||||
// if not found.
|
||||
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const;
|
||||
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const;
|
||||
// Calls GetDirectoryRelative(path) on the root of the current directory.
|
||||
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const;
|
||||
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(std::string_view path) const;
|
||||
|
||||
// Returns a vector containing all of the files in this directory.
|
||||
virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0;
|
||||
// Returns the file with filename matching name. Returns nullptr if directory dosen't have a
|
||||
// file with name.
|
||||
virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const;
|
||||
virtual std::shared_ptr<VfsFile> GetFile(std::string_view name) const;
|
||||
|
||||
// Returns a vector containing all of the subdirectories in this directory.
|
||||
virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0;
|
||||
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a
|
||||
// directory with name.
|
||||
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const;
|
||||
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const;
|
||||
|
||||
// Returns whether or not the directory can be written to.
|
||||
virtual bool IsWritable() const = 0;
|
||||
@@ -161,53 +161,56 @@ struct VfsDirectory : NonCopyable {
|
||||
|
||||
// Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
|
||||
// if the operation failed.
|
||||
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0;
|
||||
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) = 0;
|
||||
// Creates a new file with name name. Returns a pointer to the new file or nullptr if the
|
||||
// operation failed.
|
||||
virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0;
|
||||
virtual std::shared_ptr<VfsFile> CreateFile(std::string_view name) = 0;
|
||||
|
||||
// Creates a new file at the path relative to this directory. Also creates directories if
|
||||
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
||||
virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& path);
|
||||
virtual std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path);
|
||||
|
||||
// Creates a new file at the path relative to root of this directory. Also creates directories
|
||||
// if they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
||||
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& path);
|
||||
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path);
|
||||
|
||||
// Creates a new directory at the path relative to this directory. Also creates directories if
|
||||
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
||||
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& path);
|
||||
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path);
|
||||
|
||||
// Creates a new directory at the path relative to root of this directory. Also creates
|
||||
// directories if they do not exist and is supported by this implementation. Returns nullptr on
|
||||
// any failure.
|
||||
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path);
|
||||
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path);
|
||||
|
||||
// Deletes the subdirectory with name and returns true on success.
|
||||
virtual bool DeleteSubdirectory(const std::string& name) = 0;
|
||||
virtual bool DeleteSubdirectory(std::string_view name) = 0;
|
||||
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
|
||||
// the subdirectory. Returns true on success.
|
||||
virtual bool DeleteSubdirectoryRecursive(const std::string& name);
|
||||
virtual bool DeleteSubdirectoryRecursive(std::string_view name);
|
||||
// Returnes whether or not the file with name name was deleted successfully.
|
||||
virtual bool DeleteFile(const std::string& name) = 0;
|
||||
virtual bool DeleteFile(std::string_view name) = 0;
|
||||
|
||||
// Returns whether or not this directory was renamed to name.
|
||||
virtual bool Rename(const std::string& name) = 0;
|
||||
virtual bool Rename(std::string_view name) = 0;
|
||||
|
||||
// Returns whether or not the file with name src was successfully copied to a new file with name
|
||||
// dest.
|
||||
virtual bool Copy(const std::string& src, const std::string& dest);
|
||||
virtual bool Copy(std::string_view src, std::string_view dest);
|
||||
|
||||
// Interprets the file with name file instead as a directory of type directory.
|
||||
// The directory must have a constructor that takes a single argument of type
|
||||
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
|
||||
// subdirectory in one call.
|
||||
template <typename Directory>
|
||||
bool InterpretAsDirectory(const std::string& file) {
|
||||
bool InterpretAsDirectory(std::string_view file) {
|
||||
auto file_p = GetFile(file);
|
||||
if (file_p == nullptr)
|
||||
|
||||
if (file_p == nullptr) {
|
||||
return false;
|
||||
return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p));
|
||||
}
|
||||
|
||||
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -221,10 +224,10 @@ protected:
|
||||
struct ReadOnlyVfsDirectory : public VfsDirectory {
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
|
||||
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
|
||||
bool DeleteSubdirectory(const std::string& name) override;
|
||||
bool DeleteFile(const std::string& name) override;
|
||||
bool Rename(const std::string& name) override;
|
||||
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
|
||||
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
|
||||
bool DeleteSubdirectory(std::string_view name) override;
|
||||
bool DeleteFile(std::string_view name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -80,7 +80,7 @@ size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
|
||||
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
|
||||
}
|
||||
|
||||
bool OffsetVfsFile::Rename(const std::string& name) {
|
||||
bool OffsetVfsFile::Rename(std::string_view name) {
|
||||
return file->Rename(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -30,7 +33,7 @@ struct OffsetVfsFile : public VfsFile {
|
||||
bool WriteByte(u8 data, size_t offset) override;
|
||||
size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
|
||||
|
||||
bool Rename(const std::string& name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
size_t GetOffset() const;
|
||||
|
||||
|
||||
@@ -72,12 +72,15 @@ size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
|
||||
return backing.WriteBytes(data, length);
|
||||
}
|
||||
|
||||
bool RealVfsFile::Rename(const std::string& name) {
|
||||
const auto out = FileUtil::Rename(GetName(), name);
|
||||
path = parent_path + DIR_SEP + name;
|
||||
bool RealVfsFile::Rename(std::string_view name) {
|
||||
std::string name_str(name.begin(), name.end());
|
||||
const auto out = FileUtil::Rename(GetName(), name_str);
|
||||
|
||||
path = (parent_path + DIR_SEP).append(name);
|
||||
path_components = parent_components;
|
||||
path_components.push_back(name);
|
||||
path_components.push_back(std::move(name_str));
|
||||
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -135,36 +138,54 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
|
||||
return std::make_shared<RealVfsDirectory>(parent_path, perms);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) {
|
||||
if (!FileUtil::CreateDir(path + DIR_SEP + name))
|
||||
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
|
||||
const std::string subdir_path = (path + DIR_SEP).append(name);
|
||||
|
||||
if (!FileUtil::CreateDir(subdir_path)) {
|
||||
return nullptr;
|
||||
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms));
|
||||
}
|
||||
|
||||
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
|
||||
return subdirectories.back();
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) {
|
||||
if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name))
|
||||
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
|
||||
const std::string file_path = (path + DIR_SEP).append(name);
|
||||
|
||||
if (!FileUtil::CreateEmptyFile(file_path)) {
|
||||
return nullptr;
|
||||
files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms));
|
||||
}
|
||||
|
||||
files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
|
||||
return files.back();
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) {
|
||||
return FileUtil::DeleteDirRecursively(path + DIR_SEP + name);
|
||||
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||
const std::string subdir_path = (path + DIR_SEP).append(name);
|
||||
|
||||
return FileUtil::DeleteDirRecursively(subdir_path);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::DeleteFile(const std::string& name) {
|
||||
auto file = GetFile(name);
|
||||
if (file == nullptr)
|
||||
bool RealVfsDirectory::DeleteFile(std::string_view name) {
|
||||
const auto file = GetFile(name);
|
||||
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
files.erase(std::find(files.begin(), files.end(), file));
|
||||
|
||||
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
|
||||
real_file->Close();
|
||||
return FileUtil::Delete(path + DIR_SEP + name);
|
||||
|
||||
const std::string file_path = (path + DIR_SEP).append(name);
|
||||
return FileUtil::Delete(file_path);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::Rename(const std::string& name) {
|
||||
return FileUtil::Rename(path, parent_path + DIR_SEP + name);
|
||||
bool RealVfsDirectory::Rename(std::string_view name) {
|
||||
const std::string new_name = (parent_path + DIR_SEP).append(name);
|
||||
|
||||
return FileUtil::Rename(path, new_name);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
@@ -24,7 +26,7 @@ struct RealVfsFile : public VfsFile {
|
||||
bool IsReadable() const override;
|
||||
size_t Read(u8* data, size_t length, size_t offset) const override;
|
||||
size_t Write(const u8* data, size_t length, size_t offset) override;
|
||||
bool Rename(const std::string& name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
private:
|
||||
bool Close();
|
||||
@@ -47,11 +49,11 @@ struct RealVfsDirectory : public VfsDirectory {
|
||||
bool IsReadable() const override;
|
||||
std::string GetName() const override;
|
||||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
|
||||
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
|
||||
bool DeleteSubdirectory(const std::string& name) override;
|
||||
bool DeleteFile(const std::string& name) override;
|
||||
bool Rename(const std::string& name) override;
|
||||
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
|
||||
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
|
||||
bool DeleteSubdirectory(std::string_view name) override;
|
||||
bool DeleteFile(std::string_view name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
@@ -650,12 +650,27 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
|
||||
ASSERT(thread->condvar_wait_address == condition_variable_addr);
|
||||
|
||||
// If the mutex is not yet acquired, acquire it.
|
||||
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
|
||||
size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
|
||||
|
||||
auto& monitor = Core::System::GetInstance().Monitor();
|
||||
|
||||
// Atomically read the value of the mutex.
|
||||
u32 mutex_val = 0;
|
||||
do {
|
||||
monitor.SetExclusive(current_core, thread->mutex_wait_address);
|
||||
|
||||
// If the mutex is not yet acquired, acquire it.
|
||||
mutex_val = Memory::Read32(thread->mutex_wait_address);
|
||||
|
||||
if (mutex_val != 0) {
|
||||
monitor.ClearExclusive();
|
||||
break;
|
||||
}
|
||||
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
|
||||
thread->wait_handle));
|
||||
|
||||
if (mutex_val == 0) {
|
||||
// We were able to acquire the mutex, resume this thread.
|
||||
Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
|
||||
ASSERT(thread->status == ThreadStatus::WaitMutex);
|
||||
thread->ResumeFromWait();
|
||||
|
||||
@@ -668,7 +683,19 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
thread->condvar_wait_address = 0;
|
||||
thread->wait_handle = 0;
|
||||
} else {
|
||||
// Couldn't acquire the mutex, block the thread.
|
||||
// Atomically signal that the mutex now has a waiting thread.
|
||||
do {
|
||||
monitor.SetExclusive(current_core, thread->mutex_wait_address);
|
||||
|
||||
// Ensure that the mutex value is still what we expect.
|
||||
u32 value = Memory::Read32(thread->mutex_wait_address);
|
||||
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
|
||||
// retries the initial read for this thread.
|
||||
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
|
||||
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
|
||||
mutex_val | Mutex::MutexHasWaitersFlag));
|
||||
|
||||
// The mutex is already owned by some other thread, make this thread wait on it.
|
||||
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
||||
auto owner = g_handle_table.Get<Thread>(owner_handle);
|
||||
ASSERT(owner);
|
||||
@@ -676,9 +703,6 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
thread->status = ThreadStatus::WaitMutex;
|
||||
thread->wakeup_callback = nullptr;
|
||||
|
||||
// Signal that the mutex now has a waiting thread.
|
||||
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
|
||||
|
||||
owner->AddMutexWaiter(thread);
|
||||
|
||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Service::FileSystem {
|
||||
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
|
||||
|
||||
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
const std::string& dir_name) {
|
||||
std::string_view dir_name) {
|
||||
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
|
||||
return base;
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ FileType GuessFromFilename(const std::string& name) {
|
||||
if (name == "main")
|
||||
return FileType::DeconstructedRomDirectory;
|
||||
|
||||
const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name));
|
||||
const std::string extension =
|
||||
Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name)));
|
||||
|
||||
if (extension == "elf")
|
||||
return FileType::ELF;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
@@ -49,7 +51,55 @@ struct ModHeader {
|
||||
};
|
||||
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
|
||||
|
||||
AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
|
||||
struct AssetSection {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
};
|
||||
static_assert(sizeof(AssetSection) == 0x10, "AssetSection has incorrect size.");
|
||||
|
||||
struct AssetHeader {
|
||||
u32_le magic;
|
||||
u32_le format_version;
|
||||
AssetSection icon;
|
||||
AssetSection nacp;
|
||||
AssetSection romfs;
|
||||
};
|
||||
static_assert(sizeof(AssetHeader) == 0x38, "AssetHeader has incorrect size.");
|
||||
|
||||
AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {
|
||||
NroHeader nro_header{};
|
||||
if (file->ReadObject(&nro_header) != sizeof(NroHeader))
|
||||
return;
|
||||
|
||||
if (file->GetSize() >= nro_header.file_size + sizeof(AssetHeader)) {
|
||||
u64 offset = nro_header.file_size;
|
||||
AssetHeader asset_header{};
|
||||
if (file->ReadObject(&asset_header, offset) != sizeof(AssetHeader))
|
||||
return;
|
||||
|
||||
if (asset_header.format_version != 0)
|
||||
LOG_WARNING(Loader,
|
||||
"NRO Asset Header has format {}, currently supported format is 0. If "
|
||||
"strange glitches occur with metadata, check NRO assets.",
|
||||
asset_header.format_version);
|
||||
if (asset_header.magic != Common::MakeMagic('A', 'S', 'E', 'T'))
|
||||
return;
|
||||
|
||||
if (asset_header.nacp.size > 0) {
|
||||
nacp = std::make_unique<FileSys::NACP>(std::make_shared<FileSys::OffsetVfsFile>(
|
||||
file, asset_header.nacp.size, offset + asset_header.nacp.offset, "Control.nacp"));
|
||||
}
|
||||
|
||||
if (asset_header.romfs.size > 0) {
|
||||
romfs = std::make_shared<FileSys::OffsetVfsFile>(
|
||||
file, asset_header.romfs.size, offset + asset_header.romfs.offset, "game.romfs");
|
||||
}
|
||||
|
||||
if (asset_header.icon.size > 0) {
|
||||
icon_data = file->ReadBytes(asset_header.icon.size, offset + asset_header.icon.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
|
||||
// Read NSO header
|
||||
@@ -136,4 +186,31 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty())
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
|
||||
if (nacp == nullptr)
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
out_program_id = nacp->GetTitleId();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (romfs == nullptr)
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
dir = romfs;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
|
||||
if (nacp == nullptr)
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
title = nacp->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
} // namespace Loader
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/loader/linker.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Loader {
|
||||
|
||||
struct AssetHeader;
|
||||
|
||||
/// Loads an NRO file
|
||||
class AppLoader_NRO final : public AppLoader, Linker {
|
||||
public:
|
||||
@@ -30,8 +33,17 @@ public:
|
||||
|
||||
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
|
||||
|
||||
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
|
||||
ResultStatus ReadProgramId(u64& out_program_id) override;
|
||||
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
|
||||
private:
|
||||
bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::unique_ptr<FileSys::NACP> nacp;
|
||||
FileSys::VirtualFile romfs;
|
||||
};
|
||||
|
||||
} // namespace Loader
|
||||
|
||||
@@ -425,6 +425,7 @@ union Instruction {
|
||||
|
||||
union {
|
||||
BitField<50, 3, u64> component_mask_selector;
|
||||
BitField<0, 8, Register> gpr0;
|
||||
BitField<28, 8, Register> gpr28;
|
||||
|
||||
bool HasTwoDestinations() const {
|
||||
@@ -432,13 +433,16 @@ union Instruction {
|
||||
}
|
||||
|
||||
bool IsComponentEnabled(size_t component) const {
|
||||
static constexpr std::array<size_t, 5> one_dest_mask{0x1, 0x2, 0x4, 0x8, 0x3};
|
||||
static constexpr std::array<size_t, 5> two_dest_mask{0x7, 0xb, 0xd, 0xe, 0xf};
|
||||
const auto& mask{HasTwoDestinations() ? two_dest_mask : one_dest_mask};
|
||||
static constexpr std::array<std::array<u32, 8>, 4> mask_lut{
|
||||
{{},
|
||||
{0x1, 0x2, 0x4, 0x8, 0x3},
|
||||
{0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
|
||||
{0x7, 0xb, 0xd, 0xe, 0xf}}};
|
||||
|
||||
ASSERT(component_mask_selector < mask.size());
|
||||
size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U};
|
||||
index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0;
|
||||
|
||||
return ((1ull << component) & mask[component_mask_selector]) != 0;
|
||||
return ((1ull << component) & mask_lut[index][component_mask_selector]) != 0;
|
||||
}
|
||||
} texs;
|
||||
|
||||
|
||||
@@ -338,6 +338,18 @@ bool GMainWindow::SupportsRequiredGLExtensions() {
|
||||
unsupported_ext.append("ARB_separate_shader_objects");
|
||||
if (!GLAD_GL_ARB_vertex_attrib_binding)
|
||||
unsupported_ext.append("ARB_vertex_attrib_binding");
|
||||
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
|
||||
unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
|
||||
|
||||
// Extensions required to support some texture formats.
|
||||
if (!GLAD_GL_EXT_texture_compression_s3tc)
|
||||
unsupported_ext.append("EXT_texture_compression_s3tc");
|
||||
if (!GLAD_GL_ARB_texture_compression_rgtc)
|
||||
unsupported_ext.append("ARB_texture_compression_rgtc");
|
||||
if (!GLAD_GL_ARB_texture_compression_bptc)
|
||||
unsupported_ext.append("ARB_texture_compression_bptc");
|
||||
if (!GLAD_GL_ARB_depth_buffer_float)
|
||||
unsupported_ext.append("ARB_depth_buffer_float");
|
||||
|
||||
for (const QString& ext : unsupported_ext)
|
||||
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
|
||||
|
||||
@@ -85,10 +85,20 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
|
||||
unsupported_ext.push_back("ARB_program_interface_query");
|
||||
if (!GLAD_GL_ARB_separate_shader_objects)
|
||||
unsupported_ext.push_back("ARB_separate_shader_objects");
|
||||
if (!GLAD_GL_ARB_shader_storage_buffer_object)
|
||||
unsupported_ext.push_back("ARB_shader_storage_buffer_object");
|
||||
if (!GLAD_GL_ARB_vertex_attrib_binding)
|
||||
unsupported_ext.push_back("ARB_vertex_attrib_binding");
|
||||
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
|
||||
unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
|
||||
|
||||
// Extensions required to support some texture formats.
|
||||
if (!GLAD_GL_EXT_texture_compression_s3tc)
|
||||
unsupported_ext.push_back("EXT_texture_compression_s3tc");
|
||||
if (!GLAD_GL_ARB_texture_compression_rgtc)
|
||||
unsupported_ext.push_back("ARB_texture_compression_rgtc");
|
||||
if (!GLAD_GL_ARB_texture_compression_bptc)
|
||||
unsupported_ext.push_back("ARB_texture_compression_bptc");
|
||||
if (!GLAD_GL_ARB_depth_buffer_float)
|
||||
unsupported_ext.push_back("ARB_depth_buffer_float");
|
||||
|
||||
for (const std::string& ext : unsupported_ext)
|
||||
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
|
||||
|
||||
Reference in New Issue
Block a user