Compare commits

..

26 Commits

Author SHA1 Message Date
Lioncash
a147fa5825 loader: Remove unnecessary constructor call in IdentifyFile()
RealVfsFile inherits from VfsFile, the instance from std::make_shared is
already compatible with the function argument type, making the copy
constructor call unnecessary.
2018-07-23 17:44:58 -04:00
bunnei
07e5319d55 Merge pull request #695 from DarkLordZach/nro-asset
NRO Assets and NACP File Format
2018-07-23 14:14:11 -07:00
bunnei
d787873b3b Merge pull request #778 from lioncash/log
set: Add missing log call in GetAvailableLanguageCodeCount()
2018-07-23 14:13:09 -07:00
Lioncash
e85308cd90 set: Add missing log call in GetAvailableLanguageCodeCount()
Forgot to include this in 22f448b632
2018-07-23 12:37:42 -04:00
bunnei
7138b99f21 Merge pull request #775 from lioncash/str
string_util: Minor changes
2018-07-23 09:34:41 -07:00
Zach Hilman
e8f641a52d NRO Assets and NACP file format
Cleanup

Review fixes
2018-07-23 12:34:26 -04:00
bunnei
a85366a40c Merge pull request #777 from lioncash/lang
set: Amend return value of GetAvailableLanguageCodes()
2018-07-23 09:34:08 -07:00
Lioncash
22f448b632 set: Implement GetAvailableLanguageCodeCount()
This just returns the size of the language code buffer.
2018-07-23 00:29:40 -04:00
Lioncash
37aeecd29f set: Correct return code size of value in GetAvailableLanguageCodes()
The return code should be 32-bit in size.
2018-07-23 00:29:22 -04:00
bunnei
e85a528bb9 Merge pull request #769 from bunnei/shader-mask-fixes
shader_bytecode: Implement other TEXS masks.
2018-07-22 18:03:31 -07:00
Lioncash
9d33122197 string_util: Get rid of separate resize() in CPToUTF16(), UTF16ToUTF8(), CodeToUTF8() and UTF8ToUTF16()
There's no need to perform the resize separately here, since the
constructor allows presizing the buffer.

Also move the empty string check before the construction of the string
to make the early out more straightforward.
2018-07-22 16:39:21 -04:00
Lioncash
26a157cd31 string_util: Use emplace_back() in SplitString() instead of push_back()
This is equivalent to doing:

push_back(std::string(""));

which is likely not to cause issues, assuming a decent std::string
implementation with small-string optimizations implemented in its
design, however it's still a little unnecessary to copy that buffer
regardless. Instead, we can use emplace_back() to directly construct the
empty string within the std::vector instance, eliminating any possible
overhead from the copy.
2018-07-22 15:36:32 -04:00
Lioncash
cd46b267f5 string_util: Remove unnecessary std::string instance in TabsToSpaces()
We can just use the variant of std::string's replace() function that can
replace an occurrence with N copies of the same character, eliminating
the need to allocate a std::string containing a buffer of spaces.
2018-07-22 15:35:48 -04:00
bunnei
a4b2af7382 Merge pull request #774 from Subv/atomic_signal
Kernel/SVC: Perform atomic accesses in SignalProcessWideKey as per the real kernel.
2018-07-22 12:26:03 -07:00
bunnei
c994cdc532 Merge pull request #773 from Subv/gl_ext_check
Frontend: Check for more required OpenGL extensions during startup.
2018-07-22 11:47:07 -07:00
bunnei
5ee4c49c30 Merge pull request #768 from lioncash/string-view
file_util, vfs: Use std::string_view where applicable
2018-07-22 11:32:28 -07:00
Subv
7841447cf0 Kernel/SVC: Perform atomic accesses in SignalProcessWideKey as per the real kernel. 2018-07-22 12:27:24 -05:00
bunnei
3618a68f93 Merge pull request #770 from lioncash/construct
gl_shader_decompiler: Remove redundant Subroutine construction in AddSubroutine()
2018-07-22 10:24:20 -07:00
Subv
ba2fb83d60 Frontend: Check for more required OpenGL extensions during startup. 2018-07-22 12:05:38 -05:00
Mat M
5fc99553d2 Merge pull request #638 from MerryMage/mp
Implement exclusive monitor
2018-07-22 12:48:44 -04:00
Sebastian Valle
3e2b32a3ee Merge pull request #772 from MerryMage/dynarmic
externals: Update dynarmic to fc6b73bd
2018-07-22 11:45:47 -05:00
MerryMage
7dde4b7a77 externals: Update dynarmic to fc6b73bd
Resolves issues:
* 128-bit exclusive writes on Windows
* Non-updating CNTPCT_EL0

fc6b73 a64_emit_x64: Ensure host has updated ticks in EmitA64GetCNTPCT
888c67 a64_emit_x64: Fix stack misalignment on Windows for 128-bit exclusive writes
352d53 emit_x64_aes: Eliminate extraneous usage of a scratch register in EmitAESInverseMixColumns()
ab7fe7 A64: Implement SADDLV
09bd2b A64: Implement UADDLV
62e86d fp: Use forward declarations where applicable
b3edb7 emit_x64_vector: Append 'v' prefix onto movq in AVX path
2018-07-22 16:26:47 +01:00
MerryMage
0b1c2e5505 Implement exclusive monitor 2018-07-22 15:55:17 +01:00
Lioncash
0797657bc0 gl_shader_decompiler: Remove redundant Subroutine construction in AddSubroutine()
We don't need to toss away the Subroutine instance after the find() call
and reconstruct another instance with the same data right after it.
Particularly give Subroutine contains a std::set.
2018-07-22 03:30:35 -04:00
Lioncash
0081252d31 vfs: Correct file_p variable usage within InterpretAsDirectory()
ReplaceFileWithSubdirectory() takes a VirtualFile and a VirtualDir, but
it was being passed a string as one of its arguments. The only reason
this never caused issues is because this template isn't instantiated
anywhere yet.

This corrects an issue before it occurs.
2018-07-22 03:22:28 -04:00
Lioncash
398444e676 file_util, vfs: Use std::string_view where applicable
Avoids unnecessary construction of std::string instances where
applicable.
2018-07-22 03:22:21 -04:00
32 changed files with 720 additions and 217 deletions

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -162,21 +162,21 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
std::istringstream iss(str);
output.resize(1);
while (std::getline(iss, *output.rbegin(), delim))
output.push_back("");
while (std::getline(iss, *output.rbegin(), delim)) {
output.emplace_back();
}
output.pop_back();
}
std::string TabsToSpaces(int tab_size, const std::string& in) {
const std::string spaces(tab_size, ' ');
std::string out(in);
std::string TabsToSpaces(int tab_size, std::string in) {
size_t i = 0;
while (out.npos != (i = out.find('\t')))
out.replace(i, 1, spaces);
return out;
while ((i = in.find('\t')) != std::string::npos) {
in.replace(i, 1, tab_size, ' ');
}
return in;
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
@@ -220,31 +220,37 @@ std::u16string UTF8ToUTF16(const std::string& input) {
}
static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
auto const size =
const auto size =
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
std::wstring output;
output.resize(size);
if (size == 0) {
return L"";
}
if (size == 0 ||
size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size())))
std::wstring output(size, L'\0');
if (size != MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size()))) {
output.clear();
}
return output;
}
std::string UTF16ToUTF8(const std::wstring& input) {
auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
nullptr, 0, nullptr, nullptr);
if (size == 0) {
return "";
}
std::string output;
output.resize(size);
std::string output(size, '\0');
if (size == 0 ||
size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size()), nullptr, nullptr))
if (size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size()), nullptr,
nullptr)) {
output.clear();
}
return output;
}
@@ -265,8 +271,6 @@ std::string CP1252ToUTF8(const std::string& input) {
template <typename T>
static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) {
std::string result;
iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
if ((iconv_t)(-1) == conv_desc) {
LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno));
@@ -278,8 +282,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
// Multiply by 4, which is the max number of bytes to encode a codepoint
const size_t out_buffer_size = 4 * in_bytes;
std::string out_buffer;
out_buffer.resize(out_buffer_size);
std::string out_buffer(out_buffer_size, '\0');
auto src_buffer = &input[0];
size_t src_bytes = in_bytes;
@@ -304,6 +307,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
}
}
std::string result;
out_buffer.resize(out_buffer_size - dst_bytes);
out_buffer.swap(result);
@@ -313,8 +317,6 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
}
std::u16string UTF8ToUTF16(const std::string& input) {
std::u16string result;
iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
if ((iconv_t)(-1) == conv_desc) {
LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno));
@@ -326,8 +328,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
// Multiply by 4, which is the max number of bytes to encode a codepoint
const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
std::u16string out_buffer;
out_buffer.resize(out_buffer_size);
std::u16string out_buffer(out_buffer_size, char16_t{});
char* src_buffer = const_cast<char*>(&input[0]);
size_t src_bytes = in_bytes;
@@ -352,6 +353,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
}
}
std::u16string result;
out_buffer.resize(out_buffer_size - dst_bytes);
out_buffer.swap(result);

View File

@@ -57,7 +57,7 @@ static bool TryParse(const std::string& str, N* const output) {
return false;
}
std::string TabsToSpaces(int tab_size, const std::string& in);
std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);

View File

@@ -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

View File

@@ -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]);
});
}

View File

@@ -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;
};

View 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;

View 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;
};

View File

@@ -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>();

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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();

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -11,31 +11,40 @@
namespace Service::Set {
constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
LanguageCode::DE,
LanguageCode::IT,
LanguageCode::ES,
LanguageCode::ZH_CN,
LanguageCode::KO,
LanguageCode::NL,
LanguageCode::PT,
LanguageCode::RU,
LanguageCode::ZH_TW,
LanguageCode::EN_GB,
LanguageCode::FR_CA,
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
}};
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
static constexpr std::array<LanguageCode, 17> available_language_codes = {{
LanguageCode::JA,
LanguageCode::EN_US,
LanguageCode::FR,
LanguageCode::DE,
LanguageCode::IT,
LanguageCode::ES,
LanguageCode::ZH_CN,
LanguageCode::KO,
LanguageCode::NL,
LanguageCode::PT,
LanguageCode::RU,
LanguageCode::ZH_TW,
LanguageCode::EN_GB,
LanguageCode::FR_CA,
LanguageCode::ES_419,
LanguageCode::ZH_HANS,
LanguageCode::ZH_HANT,
}};
ctx.WriteBuffer(available_language_codes);
IPC::ResponseBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(available_language_codes.size()));
rb.Push(static_cast<u32>(available_language_codes.size()));
LOG_DEBUG(Service_SET, "called");
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(available_language_codes.size()));
LOG_DEBUG(Service_SET, "called");
}
@@ -45,10 +54,10 @@ SET::SET() : ServiceFramework("set") {
{0, nullptr, "GetLanguageCode"},
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
{2, nullptr, "MakeLanguageCode"},
{3, nullptr, "GetAvailableLanguageCodeCount"},
{3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
{4, nullptr, "GetRegionCode"},
{5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"},
{6, nullptr, "GetAvailableLanguageCodeCount2"},
{6, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount2"},
{7, nullptr, "GetKeyCodeMap"},
{8, nullptr, "GetQuestFlag"},
};

View File

@@ -36,6 +36,7 @@ public:
private:
void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx);
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Set

View File

@@ -42,14 +42,15 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
}
FileType IdentifyFile(const std::string& file_name) {
return IdentifyFile(FileSys::VirtualFile(std::make_shared<FileSys::RealVfsFile>(file_name)));
return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name));
}
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;

View File

@@ -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

View File

@@ -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

View File

@@ -78,14 +78,18 @@ private:
/// Adds and analyzes a new subroutine if it is not added yet.
const Subroutine& AddSubroutine(u32 begin, u32 end, const std::string& suffix) {
auto iter = subroutines.find(Subroutine{begin, end, suffix});
if (iter != subroutines.end())
return *iter;
Subroutine subroutine{begin, end, suffix, ExitMethod::Undetermined, {}};
const auto iter = subroutines.find(subroutine);
if (iter != subroutines.end()) {
return *iter;
}
Subroutine subroutine{begin, end, suffix};
subroutine.exit_method = Scan(begin, end, subroutine.labels);
if (subroutine.exit_method == ExitMethod::Undetermined)
if (subroutine.exit_method == ExitMethod::Undetermined) {
throw DecompileFail("Recursive function detected");
}
return *subroutines.insert(std::move(subroutine)).first;
}

View File

@@ -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());

View File

@@ -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);