Compare commits
55 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccc42702b5 | ||
|
|
1ff3bea6c7 | ||
|
|
92304181d5 | ||
|
|
47ac369180 | ||
|
|
c2b4ff5d48 | ||
|
|
59cb258409 | ||
|
|
33e2033af5 | ||
|
|
f6657bc8d7 | ||
|
|
25635907a2 | ||
|
|
84cc5dd35d | ||
|
|
1ce5e04be8 | ||
|
|
10dd03dec5 | ||
|
|
9505283989 | ||
|
|
f6f6f3811e | ||
|
|
52ec1840f5 | ||
|
|
e0b6771e25 | ||
|
|
a27c0099ed | ||
|
|
3a19c1098d | ||
|
|
bcc184acfa | ||
|
|
89db8c2171 | ||
|
|
c4322ce87e | ||
|
|
81aa02424b | ||
|
|
e12c84d5c5 | ||
|
|
a147fa5825 | ||
|
|
184c516182 | ||
|
|
1b4d0ac20e | ||
|
|
2b497e5830 | ||
|
|
ac8133b9ee | ||
|
|
1c16700372 | ||
|
|
07e5319d55 | ||
|
|
d787873b3b | ||
|
|
3b88ce3dcb | ||
|
|
1432912ae8 | ||
|
|
344a0c91f2 | ||
|
|
cbe841c9c9 | ||
|
|
1f3889a290 | ||
|
|
e85308cd90 | ||
|
|
7138b99f21 | ||
|
|
e8f641a52d | ||
|
|
a85366a40c | ||
|
|
22f448b632 | ||
|
|
37aeecd29f | ||
|
|
e85a528bb9 | ||
|
|
9d33122197 | ||
|
|
26a157cd31 | ||
|
|
cd46b267f5 | ||
|
|
a4b2af7382 | ||
|
|
c994cdc532 | ||
|
|
5ee4c49c30 | ||
|
|
3618a68f93 | ||
|
|
ba2fb83d60 | ||
|
|
0797657bc0 | ||
|
|
148a5bef7e | ||
|
|
0081252d31 | ||
|
|
398444e676 |
@@ -792,70 +792,93 @@ 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_prefix(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;
|
||||
}
|
||||
|
||||
std::string SanitizePath(std::string_view path_) {
|
||||
std::string path(path_);
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
path.erase(std::unique(path.begin(), path.end(),
|
||||
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
|
||||
path.end());
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
IOFile::IOFile() {}
|
||||
|
||||
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
|
||||
|
||||
@@ -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>
|
||||
@@ -177,6 +178,9 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
|
||||
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
||||
}
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
|
||||
std::string SanitizePath(std::string_view path);
|
||||
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
// and make forgetting an fclose() harder
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -12,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
|
||||
@@ -38,8 +40,6 @@ add_library(core STATIC
|
||||
frontend/input.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hle/config_mem.cpp
|
||||
hle/config_mem.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
hle/kernel/address_arbiter.cpp
|
||||
@@ -249,8 +249,6 @@ add_library(core STATIC
|
||||
hle/service/vi/vi_s.h
|
||||
hle/service/vi/vi_u.cpp
|
||||
hle/service/vi/vi_u.h
|
||||
hle/shared_page.cpp
|
||||
hle/shared_page.h
|
||||
hw/hw.cpp
|
||||
hw/hw.h
|
||||
hw/lcd.cpp
|
||||
|
||||
@@ -257,7 +257,7 @@ void ARM_Dynarmic::PageTableChanged() {
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
|
||||
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
|
||||
|
||||
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, u64 addr) {
|
||||
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) {
|
||||
// Size doesn't actually matter.
|
||||
monitor.Mark(core_index, addr, 16);
|
||||
}
|
||||
@@ -266,28 +266,27 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
|
||||
monitor.Clear();
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr 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) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr 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) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr 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) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr 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) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
|
||||
Memory::Write64(vaddr, value[0]);
|
||||
Memory::Write64(vaddr, value[1]);
|
||||
|
||||
@@ -68,15 +68,14 @@ public:
|
||||
explicit DynarmicExclusiveMonitor(size_t core_count);
|
||||
~DynarmicExclusiveMonitor();
|
||||
|
||||
void SetExclusive(size_t core_index, u64 addr) override;
|
||||
void SetExclusive(size_t core_index, VAddr 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;
|
||||
bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) override;
|
||||
bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) override;
|
||||
bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) override;
|
||||
bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) override;
|
||||
bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) override;
|
||||
|
||||
private:
|
||||
friend class ARM_Dynarmic;
|
||||
|
||||
@@ -4,20 +4,18 @@
|
||||
|
||||
#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 SetExclusive(size_t core_index, VAddr 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;
|
||||
virtual bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) = 0;
|
||||
virtual bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) = 0;
|
||||
virtual bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) = 0;
|
||||
virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0;
|
||||
virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0;
|
||||
};
|
||||
|
||||
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
|
||||
@@ -11,7 +11,13 @@ namespace FileSys {
|
||||
enum class Mode : u32 {
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
ReadWrite = 3,
|
||||
Append = 4,
|
||||
WriteAppend = 6,
|
||||
};
|
||||
|
||||
inline u32 operator&(Mode lhs, Mode rhs) {
|
||||
return static_cast<u32>(lhs) & static_cast<u32>(rhs);
|
||||
}
|
||||
|
||||
} // 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;
|
||||
|
||||
|
||||
@@ -13,24 +13,31 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
static std::string PermissionsToCharArray(Mode perms) {
|
||||
std::string out;
|
||||
switch (perms) {
|
||||
case Mode::Read:
|
||||
out += "r";
|
||||
break;
|
||||
case Mode::Write:
|
||||
out += "r+";
|
||||
break;
|
||||
case Mode::Append:
|
||||
out += "a";
|
||||
break;
|
||||
static std::string ModeFlagsToString(Mode mode) {
|
||||
std::string mode_str;
|
||||
|
||||
// Calculate the correct open mode for the file.
|
||||
if (mode & Mode::Read && mode & Mode::Write) {
|
||||
if (mode & Mode::Append)
|
||||
mode_str = "a+";
|
||||
else
|
||||
mode_str = "r+";
|
||||
} else {
|
||||
if (mode & Mode::Read)
|
||||
mode_str = "r";
|
||||
else if (mode & Mode::Append)
|
||||
mode_str = "a";
|
||||
else if (mode & Mode::Write)
|
||||
mode_str = "w";
|
||||
}
|
||||
return out + "b";
|
||||
|
||||
mode_str += "b";
|
||||
|
||||
return mode_str;
|
||||
}
|
||||
|
||||
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
|
||||
: backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
|
||||
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
|
||||
parent_path(FileUtil::GetParentPath(path_)),
|
||||
path_components(FileUtil::SplitPathComponents(path_)),
|
||||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
||||
@@ -53,11 +60,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
|
||||
}
|
||||
|
||||
bool RealVfsFile::IsWritable() const {
|
||||
return perms == Mode::Append || perms == Mode::Write;
|
||||
return (perms & Mode::WriteAppend) != 0;
|
||||
}
|
||||
|
||||
bool RealVfsFile::IsReadable() const {
|
||||
return perms == Mode::Read || perms == Mode::Write;
|
||||
return (perms & Mode::ReadWrite) != 0;
|
||||
}
|
||||
|
||||
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
|
||||
@@ -72,12 +79,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);
|
||||
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
|
||||
path_components.push_back(std::move(name_str));
|
||||
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -90,7 +100,7 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
|
||||
path_components(FileUtil::SplitPathComponents(path)),
|
||||
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
|
||||
perms(perms_) {
|
||||
if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append))
|
||||
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
|
||||
FileUtil::CreateDir(path);
|
||||
|
||||
if (perms == Mode::Append)
|
||||
@@ -117,11 +127,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::IsWritable() const {
|
||||
return perms == Mode::Write || perms == Mode::Append;
|
||||
return (perms & Mode::WriteAppend) != 0;
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::IsReadable() const {
|
||||
return perms == Mode::Read || perms == Mode::Write;
|
||||
return (perms & Mode::ReadWrite) != 0;
|
||||
}
|
||||
|
||||
std::string RealVfsDirectory::GetName() const {
|
||||
@@ -135,36 +145,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;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "core/hle/config_mem.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace ConfigMem {
|
||||
|
||||
ConfigMemDef config_mem;
|
||||
|
||||
void Init() {
|
||||
std::memset(&config_mem, 0, sizeof(config_mem));
|
||||
|
||||
// Values extracted from firmware 11.2.0-35E
|
||||
config_mem.kernel_version_min = 0x34;
|
||||
config_mem.kernel_version_maj = 0x2;
|
||||
config_mem.ns_tid = 0x0004013000008002;
|
||||
config_mem.sys_core_ver = 0x2;
|
||||
config_mem.unit_info = 0x1; // Bit 0 set for Retail
|
||||
config_mem.prev_firm = 0x1;
|
||||
config_mem.ctr_sdk_ver = 0x0000F297;
|
||||
config_mem.firm_version_min = 0x34;
|
||||
config_mem.firm_version_maj = 0x2;
|
||||
config_mem.firm_sys_core_ver = 0x2;
|
||||
config_mem.firm_ctr_sdk_ver = 0x0000F297;
|
||||
}
|
||||
|
||||
} // namespace ConfigMem
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Configuration memory stores various hardware/kernel configuration settings. This memory page is
|
||||
// read-only for ARM11 processes. I'm guessing this would normally be written to by the firmware/
|
||||
// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
|
||||
// putting this as a subset of HLE for now.
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace ConfigMem {
|
||||
|
||||
struct ConfigMemDef {
|
||||
u8 kernel_unk; // 0
|
||||
u8 kernel_version_rev; // 1
|
||||
u8 kernel_version_min; // 2
|
||||
u8 kernel_version_maj; // 3
|
||||
u32_le update_flag; // 4
|
||||
u64_le ns_tid; // 8
|
||||
u32_le sys_core_ver; // 10
|
||||
u8 unit_info; // 14
|
||||
u8 boot_firm; // 15
|
||||
u8 prev_firm; // 16
|
||||
INSERT_PADDING_BYTES(0x1); // 17
|
||||
u32_le ctr_sdk_ver; // 18
|
||||
INSERT_PADDING_BYTES(0x30 - 0x1C); // 1C
|
||||
u32_le app_mem_type; // 30
|
||||
INSERT_PADDING_BYTES(0x40 - 0x34); // 34
|
||||
u32_le app_mem_alloc; // 40
|
||||
u32_le sys_mem_alloc; // 44
|
||||
u32_le base_mem_alloc; // 48
|
||||
INSERT_PADDING_BYTES(0x60 - 0x4C); // 4C
|
||||
u8 firm_unk; // 60
|
||||
u8 firm_version_rev; // 61
|
||||
u8 firm_version_min; // 62
|
||||
u8 firm_version_maj; // 63
|
||||
u32_le firm_sys_core_ver; // 64
|
||||
u32_le firm_ctr_sdk_ver; // 68
|
||||
INSERT_PADDING_BYTES(0x1000 - 0x6C); // 6C
|
||||
};
|
||||
static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE,
|
||||
"Config Memory structure size is wrong");
|
||||
|
||||
extern ConfigMemDef config_mem;
|
||||
|
||||
void Init();
|
||||
|
||||
} // namespace ConfigMem
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/config_mem.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
@@ -11,7 +10,6 @@
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
#include "core/hle/shared_page.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -19,9 +17,6 @@ unsigned int Object::next_object_id;
|
||||
|
||||
/// Initialize the kernel
|
||||
void Init(u32 system_mode) {
|
||||
ConfigMem::Init();
|
||||
SharedPage::Init();
|
||||
|
||||
Kernel::MemoryInit(system_mode);
|
||||
|
||||
Kernel::ResourceLimitsInit();
|
||||
|
||||
@@ -11,11 +11,9 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/config_mem.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/shared_page.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory_setup.h"
|
||||
|
||||
@@ -63,14 +61,6 @@ void MemoryInit(u32 mem_type) {
|
||||
|
||||
// We must've allocated the entire FCRAM by the end
|
||||
ASSERT(base == Memory::FCRAM_SIZE);
|
||||
|
||||
using ConfigMem::config_mem;
|
||||
config_mem.app_mem_type = mem_type;
|
||||
// app_mem_malloc does not always match the configured size for memory_region[0]: in case the
|
||||
// n3DS type override is in effect it reports the size the game expects, not the real one.
|
||||
config_mem.app_mem_alloc = memory_region_sizes[mem_type][0];
|
||||
config_mem.sys_mem_alloc = static_cast<u32_le>(memory_regions[1].size);
|
||||
config_mem.base_mem_alloc = static_cast<u32_le>(memory_regions[2].size);
|
||||
}
|
||||
|
||||
void MemoryShutdown() {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <algorithm>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/config_mem.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
@@ -13,7 +12,6 @@
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
#include "core/hle/shared_page.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ 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_) {
|
||||
std::string dir_name(FileUtil::SanitizePath(dir_name_));
|
||||
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
|
||||
return base;
|
||||
|
||||
@@ -38,7 +39,8 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
|
||||
return backing->GetName();
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
auto file = dir->CreateFile(FileUtil::GetFilename(path));
|
||||
if (file == nullptr) {
|
||||
@@ -52,7 +54,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 s
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (path == "/" || path == "\\") {
|
||||
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
|
||||
@@ -60,14 +63,15 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const
|
||||
}
|
||||
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
|
||||
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
|
||||
dir = backing;
|
||||
@@ -79,7 +83,8 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path)
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
@@ -88,7 +93,8 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path)
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
|
||||
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
@@ -97,8 +103,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
|
||||
const std::string& dest_path) const {
|
||||
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
|
||||
const std::string& dest_path_) const {
|
||||
std::string src_path(FileUtil::SanitizePath(src_path_));
|
||||
std::string dest_path(FileUtil::SanitizePath(dest_path_));
|
||||
auto src = backing->GetFileRelative(src_path);
|
||||
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
@@ -130,8 +138,10 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
|
||||
const std::string& dest_path) const {
|
||||
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
||||
const std::string& dest_path_) const {
|
||||
std::string src_path(FileUtil::SanitizePath(src_path_));
|
||||
std::string dest_path(FileUtil::SanitizePath(dest_path_));
|
||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
@@ -154,8 +164,9 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
|
||||
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
|
||||
FileSys::Mode mode) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto npath = path;
|
||||
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
|
||||
npath = npath.substr(1);
|
||||
@@ -171,7 +182,8 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::
|
||||
return MakeResult<FileSys::VirtualFile>(file);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
|
||||
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, path);
|
||||
if (dir == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
@@ -188,7 +200,8 @@ u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
|
||||
}
|
||||
|
||||
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
|
||||
const std::string& path) const {
|
||||
const std::string& path_) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
if (dir == nullptr)
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
@@ -272,9 +285,9 @@ void RegisterFileSystems() {
|
||||
sdmc_factory = nullptr;
|
||||
|
||||
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::Write);
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
|
||||
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::Write);
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
|
||||
|
||||
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||
save_data_factory = std::move(savedata);
|
||||
|
||||
@@ -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"},
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
|
||||
private:
|
||||
void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx);
|
||||
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Set
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/math_util.h"
|
||||
@@ -43,7 +45,9 @@ public:
|
||||
|
||||
template <typename T>
|
||||
T Read() {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
ASSERT(read_index + sizeof(T) <= buffer.size());
|
||||
|
||||
T val;
|
||||
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
|
||||
read_index += sizeof(T);
|
||||
@@ -53,7 +57,9 @@ public:
|
||||
|
||||
template <typename T>
|
||||
T ReadUnaligned() {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
ASSERT(read_index + sizeof(T) <= buffer.size());
|
||||
|
||||
T val;
|
||||
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
|
||||
read_index += sizeof(T);
|
||||
@@ -87,8 +93,12 @@ public:
|
||||
|
||||
template <typename T>
|
||||
void Write(const T& val) {
|
||||
if (buffer.size() < write_index + sizeof(T))
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
if (buffer.size() < write_index + sizeof(T)) {
|
||||
buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
|
||||
}
|
||||
|
||||
std::memcpy(buffer.data() + write_index, &val, sizeof(T));
|
||||
write_index += sizeof(T);
|
||||
write_index = Common::AlignUp(write_index, 4);
|
||||
@@ -96,7 +106,9 @@ public:
|
||||
|
||||
template <typename T>
|
||||
void WriteObject(const T& val) {
|
||||
u32_le size = static_cast<u32>(sizeof(val));
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
const u32_le size = static_cast<u32>(sizeof(val));
|
||||
Write(size);
|
||||
// TODO(Subv): Support file descriptors.
|
||||
Write<u32_le>(0); // Fd count.
|
||||
@@ -176,7 +188,7 @@ private:
|
||||
|
||||
class IGBPConnectRequestParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPConnectRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
|
||||
explicit IGBPConnectRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
|
||||
Deserialize();
|
||||
}
|
||||
~IGBPConnectRequestParcel() override = default;
|
||||
@@ -223,8 +235,8 @@ private:
|
||||
|
||||
class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPSetPreallocatedBufferRequestParcel(const std::vector<u8>& buffer)
|
||||
: Parcel(buffer) {
|
||||
explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
|
||||
: Parcel(std::move(buffer)) {
|
||||
Deserialize();
|
||||
}
|
||||
~IGBPSetPreallocatedBufferRequestParcel() override = default;
|
||||
@@ -256,7 +268,7 @@ protected:
|
||||
|
||||
class IGBPDequeueBufferRequestParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPDequeueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
|
||||
explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
|
||||
Deserialize();
|
||||
}
|
||||
~IGBPDequeueBufferRequestParcel() override = default;
|
||||
@@ -307,7 +319,7 @@ protected:
|
||||
|
||||
class IGBPRequestBufferRequestParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPRequestBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
|
||||
explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
|
||||
Deserialize();
|
||||
}
|
||||
~IGBPRequestBufferRequestParcel() override = default;
|
||||
@@ -322,8 +334,7 @@ public:
|
||||
|
||||
class IGBPRequestBufferResponseParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer)
|
||||
: Parcel(), buffer(buffer) {}
|
||||
explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer) : buffer(buffer) {}
|
||||
~IGBPRequestBufferResponseParcel() override = default;
|
||||
|
||||
protected:
|
||||
@@ -340,7 +351,7 @@ protected:
|
||||
|
||||
class IGBPQueueBufferRequestParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPQueueBufferRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
|
||||
explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
|
||||
Deserialize();
|
||||
}
|
||||
~IGBPQueueBufferRequestParcel() override = default;
|
||||
@@ -409,7 +420,7 @@ private:
|
||||
|
||||
class IGBPQueryRequestParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPQueryRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
|
||||
explicit IGBPQueryRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
|
||||
Deserialize();
|
||||
}
|
||||
~IGBPQueryRequestParcel() override = default;
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/shared_page.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace SharedPage {
|
||||
|
||||
SharedPageDef shared_page;
|
||||
|
||||
static CoreTiming::EventType* update_time_event;
|
||||
|
||||
/// Gets system time in 3DS format. The epoch is Jan 1900, and the unit is millisecond.
|
||||
static u64 GetSystemTime() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
// 3DS system does't allow user to set a time before Jan 1 2000,
|
||||
// so we use it as an auxiliary epoch to calculate the console time.
|
||||
std::tm epoch_tm;
|
||||
epoch_tm.tm_sec = 0;
|
||||
epoch_tm.tm_min = 0;
|
||||
epoch_tm.tm_hour = 0;
|
||||
epoch_tm.tm_mday = 1;
|
||||
epoch_tm.tm_mon = 0;
|
||||
epoch_tm.tm_year = 100;
|
||||
epoch_tm.tm_isdst = 0;
|
||||
auto epoch = std::chrono::system_clock::from_time_t(std::mktime(&epoch_tm));
|
||||
|
||||
// 3DS console time uses Jan 1 1900 as internal epoch,
|
||||
// so we use the milliseconds between 1900 and 2000 as base console time
|
||||
u64 console_time = 3155673600000ULL;
|
||||
|
||||
// Only when system time is after 2000, we set it as 3DS system time
|
||||
if (now > epoch) {
|
||||
console_time += std::chrono::duration_cast<std::chrono::milliseconds>(now - epoch).count();
|
||||
}
|
||||
|
||||
// If the system time is in daylight saving, we give an additional hour to console time
|
||||
std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm* now_tm = std::localtime(&now_time_t);
|
||||
if (now_tm && now_tm->tm_isdst > 0)
|
||||
console_time += 60 * 60 * 1000;
|
||||
|
||||
return console_time;
|
||||
}
|
||||
|
||||
static void UpdateTimeCallback(u64 userdata, int cycles_late) {
|
||||
DateTime& date_time =
|
||||
shared_page.date_time_counter % 2 ? shared_page.date_time_0 : shared_page.date_time_1;
|
||||
|
||||
date_time.date_time = GetSystemTime();
|
||||
date_time.update_tick = CoreTiming::GetTicks();
|
||||
date_time.tick_to_second_coefficient = CoreTiming::BASE_CLOCK_RATE;
|
||||
date_time.tick_offset = 0;
|
||||
|
||||
++shared_page.date_time_counter;
|
||||
|
||||
// system time is updated hourly
|
||||
CoreTiming::ScheduleEvent(CoreTiming::msToCycles(60 * 60 * 1000) - cycles_late,
|
||||
update_time_event);
|
||||
}
|
||||
|
||||
void Init() {
|
||||
std::memset(&shared_page, 0, sizeof(shared_page));
|
||||
|
||||
shared_page.running_hw = 0x1; // product
|
||||
|
||||
// Some games wait until this value becomes 0x1, before asking running_hw
|
||||
shared_page.unknown_value = 0x1;
|
||||
|
||||
// Set to a completely full battery
|
||||
shared_page.battery_state.is_adapter_connected.Assign(1);
|
||||
shared_page.battery_state.is_charging.Assign(1);
|
||||
|
||||
update_time_event =
|
||||
CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback);
|
||||
CoreTiming::ScheduleEvent(0, update_time_event);
|
||||
}
|
||||
|
||||
} // namespace SharedPage
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* The shared page stores various runtime configuration settings. This memory page is
|
||||
* read-only for user processes (there is a bit in the header that grants the process
|
||||
* write access, according to 3dbrew; this is not emulated)
|
||||
*/
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace SharedPage {
|
||||
|
||||
// See http://3dbrew.org/wiki/Configuration_Memory#Shared_Memory_Page_For_ARM11_Processes
|
||||
|
||||
struct DateTime {
|
||||
u64_le date_time; // 0
|
||||
u64_le update_tick; // 8
|
||||
u64_le tick_to_second_coefficient; // 10
|
||||
u64_le tick_offset; // 18
|
||||
};
|
||||
static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong");
|
||||
|
||||
union BatteryState {
|
||||
u8 raw;
|
||||
BitField<0, 1, u8> is_adapter_connected;
|
||||
BitField<1, 1, u8> is_charging;
|
||||
BitField<2, 3, u8> charge_level;
|
||||
};
|
||||
|
||||
struct SharedPageDef {
|
||||
// Most of these names are taken from the 3dbrew page linked above.
|
||||
u32_le date_time_counter; // 0
|
||||
u8 running_hw; // 4
|
||||
/// "Microcontroller hardware info"
|
||||
u8 mcu_hw_info; // 5
|
||||
INSERT_PADDING_BYTES(0x20 - 0x6); // 6
|
||||
DateTime date_time_0; // 20
|
||||
DateTime date_time_1; // 40
|
||||
u8 wifi_macaddr[6]; // 60
|
||||
u8 wifi_link_level; // 66
|
||||
u8 wifi_unknown2; // 67
|
||||
INSERT_PADDING_BYTES(0x80 - 0x68); // 68
|
||||
float_le sliderstate_3d; // 80
|
||||
u8 ledstate_3d; // 84
|
||||
BatteryState battery_state; // 85
|
||||
u8 unknown_value; // 86
|
||||
INSERT_PADDING_BYTES(0xA0 - 0x87); // 87
|
||||
u64_le menu_title_id; // A0
|
||||
u64_le active_menu_title_id; // A8
|
||||
INSERT_PADDING_BYTES(0x1000 - 0xB0); // B0
|
||||
};
|
||||
static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE,
|
||||
"Shared page structure size is wrong");
|
||||
|
||||
extern SharedPageDef shared_page;
|
||||
|
||||
void Init();
|
||||
|
||||
} // namespace SharedPage
|
||||
@@ -49,8 +49,7 @@ struct Elf64_Sym {
|
||||
static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
|
||||
|
||||
void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
|
||||
u64 relocation_offset, u64 size, bool is_jump_relocation,
|
||||
VAddr load_base) {
|
||||
u64 relocation_offset, u64 size, VAddr load_base) {
|
||||
for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
|
||||
Elf64_Rela rela;
|
||||
std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
|
||||
@@ -124,12 +123,11 @@ void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset
|
||||
}
|
||||
|
||||
if (dynamic.find(DT_RELA) != dynamic.end()) {
|
||||
WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], false,
|
||||
load_base);
|
||||
WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
|
||||
}
|
||||
|
||||
if (dynamic.find(DT_JMPREL) != dynamic.end()) {
|
||||
WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ], true,
|
||||
WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
|
||||
load_base);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@ protected:
|
||||
};
|
||||
|
||||
void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
|
||||
u64 relocation_offset, u64 size, bool is_jump_relocation,
|
||||
VAddr load_base);
|
||||
u64 relocation_offset, u64 size, VAddr load_base);
|
||||
void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
|
||||
|
||||
void ResolveImports();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
@@ -49,7 +52,62 @@ 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)) {
|
||||
const 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader_NRO::~AppLoader_NRO() = default;
|
||||
|
||||
FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
|
||||
// Read NSO header
|
||||
@@ -80,8 +138,9 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
|
||||
// Build program image
|
||||
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
|
||||
std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
|
||||
if (program_image.size() != PageAlignSize(nro_header.file_size))
|
||||
if (program_image.size() != PageAlignSize(nro_header.file_size)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
|
||||
codeset->segments[i].addr = nro_header.segments[i].offset;
|
||||
@@ -136,4 +195,39 @@ 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
|
||||
|
||||
@@ -10,12 +10,17 @@
|
||||
#include "core/loader/linker.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
class NACP;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/// Loads an NRO file
|
||||
class AppLoader_NRO final : public AppLoader, Linker {
|
||||
public:
|
||||
AppLoader_NRO(FileSys::VirtualFile file);
|
||||
explicit AppLoader_NRO(FileSys::VirtualFile file);
|
||||
~AppLoader_NRO() override;
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
@@ -30,8 +35,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;
|
||||
|
||||
|
||||
@@ -35,9 +35,11 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
|
||||
case RenderTargetFormat::RGBA32_FLOAT:
|
||||
return 16;
|
||||
case RenderTargetFormat::RGBA16_FLOAT:
|
||||
case RenderTargetFormat::RG32_FLOAT:
|
||||
return 8;
|
||||
case RenderTargetFormat::RGBA8_UNORM:
|
||||
case RenderTargetFormat::RGB10_A2_UNORM:
|
||||
case RenderTargetFormat::BGRA8_UNORM:
|
||||
return 4;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
|
||||
|
||||
@@ -18,6 +18,8 @@ enum class RenderTargetFormat : u32 {
|
||||
RGBA32_FLOAT = 0xC0,
|
||||
RGBA32_UINT = 0xC2,
|
||||
RGBA16_FLOAT = 0xCA,
|
||||
RG32_FLOAT = 0xCB,
|
||||
BGRA8_UNORM = 0xCF,
|
||||
RGB10_A2_UNORM = 0xD1,
|
||||
RGBA8_UNORM = 0xD5,
|
||||
RGBA8_SRGB = 0xD6,
|
||||
|
||||
@@ -38,7 +38,8 @@ struct FormatTuple {
|
||||
params.addr = config.tic.Address();
|
||||
params.is_tiled = config.tic.IsTiled();
|
||||
params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
|
||||
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format);
|
||||
params.pixel_format =
|
||||
PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value());
|
||||
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
|
||||
@@ -106,6 +107,10 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
|
||||
true}, // BC7U
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
|
||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8
|
||||
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
|
||||
{GL_RGBA32F, GL_RGBA, GL_FLOAT, ComponentType::Float, false}, // RGBA32F
|
||||
{GL_RG32F, GL_RG, GL_FLOAT, ComponentType::Float, false}, // RG32F
|
||||
{GL_R32F, GL_RED, GL_FLOAT, ComponentType::Float, false}, // R32F
|
||||
|
||||
// DepthStencil formats
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
|
||||
@@ -197,7 +202,9 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
|
||||
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
|
||||
MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
||||
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::Z24S8>,
|
||||
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>,
|
||||
MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>,
|
||||
MortonCopy<true, PixelFormat::R32F>, MortonCopy<true, PixelFormat::Z24S8>,
|
||||
MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
|
||||
MortonCopy<true, PixelFormat::Z16>,
|
||||
};
|
||||
@@ -213,7 +220,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
MortonCopy<false, PixelFormat::RGBA16F>,
|
||||
MortonCopy<false, PixelFormat::R11FG11FB10F>,
|
||||
MortonCopy<false, PixelFormat::RGBA32UI>,
|
||||
// TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1/BC7U formats is not yet supported
|
||||
// TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/BC7U/ASTC_2D_4X4 formats is not supported
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
@@ -221,6 +228,10 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
nullptr,
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::G8R8>,
|
||||
MortonCopy<false, PixelFormat::BGRA8>,
|
||||
MortonCopy<false, PixelFormat::RGBA32F>,
|
||||
MortonCopy<false, PixelFormat::RG32F>,
|
||||
MortonCopy<false, PixelFormat::R32F>,
|
||||
MortonCopy<false, PixelFormat::Z24S8>,
|
||||
MortonCopy<false, PixelFormat::S8Z24>,
|
||||
MortonCopy<false, PixelFormat::Z32F>,
|
||||
|
||||
@@ -37,14 +37,18 @@ struct SurfaceParams {
|
||||
BC7U = 12,
|
||||
ASTC_2D_4X4 = 13,
|
||||
G8R8 = 14,
|
||||
BGRA8 = 15,
|
||||
RGBA32F = 16,
|
||||
RG32F = 17,
|
||||
R32F = 18,
|
||||
|
||||
MaxColorFormat,
|
||||
|
||||
// DepthStencil formats
|
||||
Z24S8 = 15,
|
||||
S8Z24 = 16,
|
||||
Z32F = 17,
|
||||
Z16 = 18,
|
||||
Z24S8 = 19,
|
||||
S8Z24 = 20,
|
||||
Z32F = 21,
|
||||
Z16 = 22,
|
||||
|
||||
MaxDepthStencilFormat,
|
||||
|
||||
@@ -97,6 +101,10 @@ struct SurfaceParams {
|
||||
4, // BC7U
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32F
|
||||
@@ -127,6 +135,10 @@ struct SurfaceParams {
|
||||
128, // BC7U
|
||||
32, // ASTC_2D_4X4
|
||||
16, // G8R8
|
||||
32, // BGRA8
|
||||
128, // RGBA32F
|
||||
64, // RG32F
|
||||
32, // R32F
|
||||
32, // Z24S8
|
||||
32, // S8Z24
|
||||
32, // Z32F
|
||||
@@ -162,10 +174,16 @@ struct SurfaceParams {
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
case Tegra::RenderTargetFormat::RGBA8_SRGB:
|
||||
return PixelFormat::ABGR8;
|
||||
case Tegra::RenderTargetFormat::BGRA8_UNORM:
|
||||
return PixelFormat::BGRA8;
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return PixelFormat::A2B10G10R10;
|
||||
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
|
||||
return PixelFormat::RGBA16F;
|
||||
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
|
||||
return PixelFormat::RGBA32F;
|
||||
case Tegra::RenderTargetFormat::RG32_FLOAT:
|
||||
return PixelFormat::RG32F;
|
||||
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
|
||||
return PixelFormat::R11FG11FB10F;
|
||||
case Tegra::RenderTargetFormat::RGBA32_UINT:
|
||||
@@ -176,7 +194,8 @@ struct SurfaceParams {
|
||||
}
|
||||
}
|
||||
|
||||
static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format) {
|
||||
static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
||||
Tegra::Texture::ComponentType component_type) {
|
||||
// TODO(Subv): Properly implement this
|
||||
switch (format) {
|
||||
case Tegra::Texture::TextureFormat::A8R8G8B8:
|
||||
@@ -196,7 +215,19 @@ struct SurfaceParams {
|
||||
case Tegra::Texture::TextureFormat::BF10GF11RF11:
|
||||
return PixelFormat::R11FG11FB10F;
|
||||
case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
|
||||
return PixelFormat::RGBA32UI;
|
||||
switch (component_type) {
|
||||
case Tegra::Texture::ComponentType::FLOAT:
|
||||
return PixelFormat::RGBA32F;
|
||||
case Tegra::Texture::ComponentType::UINT:
|
||||
return PixelFormat::RGBA32UI;
|
||||
}
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
|
||||
static_cast<u32>(component_type));
|
||||
UNREACHABLE();
|
||||
case Tegra::Texture::TextureFormat::R32_G32:
|
||||
return PixelFormat::RG32F;
|
||||
case Tegra::Texture::TextureFormat::R32:
|
||||
return PixelFormat::R32F;
|
||||
case Tegra::Texture::TextureFormat::DXT1:
|
||||
return PixelFormat::DXT1;
|
||||
case Tegra::Texture::TextureFormat::DXT23:
|
||||
@@ -210,7 +241,8 @@ struct SurfaceParams {
|
||||
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
|
||||
return PixelFormat::ASTC_2D_4X4;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}",
|
||||
static_cast<u32>(format), static_cast<u32>(component_type));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
@@ -248,7 +280,18 @@ struct SurfaceParams {
|
||||
return Tegra::Texture::TextureFormat::BC7U;
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
|
||||
case PixelFormat::BGRA8:
|
||||
// TODO(bunnei): This is fine for unswizzling (since we just need the right component
|
||||
// sizes), but could be a bug if we used this function in different ways.
|
||||
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||
case PixelFormat::RGBA32F:
|
||||
return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
|
||||
case PixelFormat::RG32F:
|
||||
return Tegra::Texture::TextureFormat::R32_G32;
|
||||
case PixelFormat::R32F:
|
||||
return Tegra::Texture::TextureFormat::R32;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
@@ -264,6 +307,7 @@ struct SurfaceParams {
|
||||
case PixelFormat::Z16:
|
||||
return Tegra::DepthFormat::Z16_UNORM;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
@@ -273,6 +317,8 @@ struct SurfaceParams {
|
||||
switch (type) {
|
||||
case Tegra::Texture::ComponentType::UNORM:
|
||||
return ComponentType::UNorm;
|
||||
case Tegra::Texture::ComponentType::FLOAT:
|
||||
return ComponentType::Float;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
|
||||
UNREACHABLE();
|
||||
@@ -284,10 +330,13 @@ struct SurfaceParams {
|
||||
switch (format) {
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
case Tegra::RenderTargetFormat::RGBA8_SRGB:
|
||||
case Tegra::RenderTargetFormat::BGRA8_UNORM:
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return ComponentType::UNorm;
|
||||
case Tegra::RenderTargetFormat::RGBA16_FLOAT:
|
||||
case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
|
||||
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
|
||||
case Tegra::RenderTargetFormat::RG32_FLOAT:
|
||||
return ComponentType::Float;
|
||||
case Tegra::RenderTargetFormat::RGBA32_UINT:
|
||||
return ComponentType::UInt;
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
@@ -78,14 +81,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;
|
||||
}
|
||||
|
||||
@@ -481,6 +488,12 @@ private:
|
||||
*/
|
||||
void SetRegister(const Register& reg, u64 elem, const std::string& value,
|
||||
u64 dest_num_components, u64 value_num_components, u64 dest_elem) {
|
||||
if (reg == Register::ZeroIndex) {
|
||||
LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex");
|
||||
UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string dest = GetRegister(reg, static_cast<u32>(dest_elem));
|
||||
if (dest_num_components > 1) {
|
||||
dest += GetSwizzle(elem);
|
||||
@@ -743,6 +756,38 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void WriteTexsInstruction(const Instruction& instr, const std::string& coord,
|
||||
const std::string& texture) {
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
|
||||
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
|
||||
// goes into gpr28+0 and gpr28+1
|
||||
size_t texs_offset{};
|
||||
|
||||
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
|
||||
for (unsigned elem = 0; elem < 2; ++elem) {
|
||||
if (!instr.texs.IsComponentEnabled(elem)) {
|
||||
// Skip disabled components
|
||||
continue;
|
||||
}
|
||||
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem);
|
||||
}
|
||||
|
||||
if (!instr.texs.HasTwoDestinations()) {
|
||||
// Skip the second destination
|
||||
break;
|
||||
}
|
||||
|
||||
texs_offset += 2;
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single instruction from Tegra to GLSL.
|
||||
* @param offset the offset of the Tegra shader instruction.
|
||||
@@ -765,7 +810,8 @@ private:
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName());
|
||||
shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" +
|
||||
std::to_string(instr.value) + ')');
|
||||
|
||||
using Tegra::Shader::Pred;
|
||||
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
|
||||
@@ -1341,36 +1387,18 @@ private:
|
||||
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine("{");
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
|
||||
const std::string texture = "texture(" + sampler + ", coords)";
|
||||
|
||||
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
|
||||
// goes into gpr28+0 and gpr28+1
|
||||
size_t texs_offset{};
|
||||
|
||||
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
|
||||
for (unsigned elem = 0; elem < 2; ++elem) {
|
||||
if (!instr.texs.IsComponentEnabled(elem)) {
|
||||
// Skip disabled components
|
||||
continue;
|
||||
}
|
||||
regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
|
||||
elem);
|
||||
}
|
||||
|
||||
if (!instr.texs.HasTwoDestinations()) {
|
||||
// Skip the second destination
|
||||
break;
|
||||
}
|
||||
|
||||
texs_offset += 2;
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine("}");
|
||||
WriteTexsInstruction(instr, coord, texture);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLDS: {
|
||||
const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");";
|
||||
const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
|
||||
WriteTexsInstruction(instr, coord, texture);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -1770,11 +1798,8 @@ private:
|
||||
}; // namespace Decompiler
|
||||
|
||||
std::string GetCommonDeclarations() {
|
||||
std::string declarations;
|
||||
declarations += "#define MAX_CONSTBUFFER_ELEMENTS " +
|
||||
std::to_string(RasterizerOpenGL::MaxConstbufferSize / (sizeof(GLvec4)));
|
||||
declarations += '\n';
|
||||
return declarations;
|
||||
return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",
|
||||
RasterizerOpenGL::MaxConstbufferSize / sizeof(GLvec4));
|
||||
}
|
||||
|
||||
boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
|
||||
|
||||
@@ -61,6 +61,7 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
case TextureFormat::A8R8G8B8:
|
||||
case TextureFormat::A2B10G10R10:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
case TextureFormat::R32:
|
||||
return 4;
|
||||
case TextureFormat::A1B5G5R5:
|
||||
case TextureFormat::B5G6R5:
|
||||
@@ -72,6 +73,8 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
return 8;
|
||||
case TextureFormat::R32_G32_B32_A32:
|
||||
return 16;
|
||||
case TextureFormat::R32_G32:
|
||||
return 8;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Format not implemented");
|
||||
break;
|
||||
@@ -118,6 +121,8 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
|
||||
case TextureFormat::G8R8:
|
||||
case TextureFormat::R16_G16_B16_A16:
|
||||
case TextureFormat::R32_G32_B32_A32:
|
||||
case TextureFormat::R32_G32:
|
||||
case TextureFormat::R32:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
case TextureFormat::ASTC_2D_4X4:
|
||||
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
|
||||
@@ -174,6 +179,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
case TextureFormat::G8R8:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
case TextureFormat::R32_G32_B32_A32:
|
||||
case TextureFormat::R32_G32:
|
||||
case TextureFormat::R32:
|
||||
// TODO(Subv): For the time being just forward the same data without any decoding.
|
||||
rgba_data = texture_data;
|
||||
break;
|
||||
|
||||
@@ -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