Compare commits

..

1 Commits

Author SHA1 Message Date
bunnei
148a5bef7e shader_bytecode: Implement other TEXS masks. 2018-07-22 03:23:15 -04:00
26 changed files with 192 additions and 443 deletions

View File

@@ -792,80 +792,66 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
}
}
std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string copy(filename);
std::vector<std::string> SplitPathComponents(const std::string& filename) {
auto copy(filename);
std::replace(copy.begin(), copy.end(), '\\', '/');
std::vector<std::string> out;
std::stringstream stream(copy);
std::stringstream stream(filename);
std::string item;
while (std::getline(stream, item, '/')) {
while (std::getline(stream, item, '/'))
out.push_back(std::move(item));
}
return out;
}
std::string_view GetParentPath(std::string_view path) {
const auto name_bck_index = path.rfind('\\');
const auto name_fwd_index = path.rfind('/');
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('/');
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);
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);
return out.erase(name_index);
}
std::string_view GetPathWithoutTop(std::string_view path) {
if (path.empty()) {
return path;
}
std::string GetPathWithoutTop(std::string path) {
if (path.empty())
return "";
while (path[0] == '\\' || path[0] == '/') {
path.remove_suffix(1);
if (path.empty()) {
return path;
}
path = path.substr(1);
if (path.empty())
return "";
}
const auto name_bck_index = path.find('\\');
const auto name_fwd_index = path.find('/');
const auto name_bck_index = path.find_first_of('\\');
const auto name_fwd_index = path.find_first_of('/');
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
}
std::string_view GetFilename(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
if (name_index == std::string_view::npos) {
return {};
}
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 "";
return path.substr(name_index + 1);
}
std::string_view GetExtensionFromFilename(std::string_view name) {
const size_t index = name.rfind('.');
if (index == std::string_view::npos) {
return {};
}
std::string GetExtensionFromFilename(const std::string& name) {
size_t index = name.find_last_of('.');
if (index == std::string::npos)
return "";
return name.substr(index + 1);
}
std::string_view RemoveTrailingSlash(std::string_view path) {
if (path.empty()) {
std::string RemoveTrailingSlash(const std::string& path) {
if (path.empty())
return path;
}
if (path.back() == '\\' || path.back() == '/') {
path.remove_suffix(1);
return path;
}
if (path.back() == '\\' || path.back() == '/')
return path.substr(0, path.size() - 1);
return path;
}

View File

@@ -9,7 +9,6 @@
#include <fstream>
#include <functional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
@@ -152,22 +151,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(std::string_view filename);
std::vector<std::string> SplitPathComponents(const std::string& filename);
// Gets all of the text up to the last '/' or '\' in the path.
std::string_view GetParentPath(std::string_view path);
std::string GetParentPath(const std::string& path);
// Gets all of the text after the first '/' or '\' in the path.
std::string_view GetPathWithoutTop(std::string_view path);
std::string GetPathWithoutTop(std::string path);
// Gets the filename of the path
std::string_view GetFilename(std::string_view path);
std::string GetFilename(std::string path);
// Gets the extension of the filename
std::string_view GetExtensionFromFilename(std::string_view name);
std::string GetExtensionFromFilename(const std::string& name);
// Removes the final '/' or '\' if one exists
std::string_view RemoveTrailingSlash(std::string_view path);
std::string RemoveTrailingSlash(const std::string& path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>

View File

@@ -162,21 +162,21 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
std::istringstream iss(str);
output.resize(1);
while (std::getline(iss, *output.rbegin(), delim)) {
output.emplace_back();
}
while (std::getline(iss, *output.rbegin(), delim))
output.push_back("");
output.pop_back();
}
std::string TabsToSpaces(int tab_size, std::string in) {
std::string TabsToSpaces(int tab_size, const std::string& in) {
const std::string spaces(tab_size, ' ');
std::string out(in);
size_t i = 0;
while (out.npos != (i = out.find('\t')))
out.replace(i, 1, spaces);
while ((i = in.find('\t')) != std::string::npos) {
in.replace(i, 1, tab_size, ' ');
}
return in;
return out;
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
@@ -220,37 +220,31 @@ std::u16string UTF8ToUTF16(const std::string& input) {
}
static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
const auto size =
auto const size =
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
if (size == 0) {
return L"";
}
std::wstring output;
output.resize(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()))) {
if (size == 0 ||
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) {
const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
nullptr, 0, nullptr, nullptr);
if (size == 0) {
return "";
}
std::string output(size, '\0');
std::string output;
output.resize(size);
if (size != WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
&output[0], static_cast<int>(output.size()), nullptr,
nullptr)) {
if (size == 0 ||
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;
}
@@ -271,6 +265,8 @@ 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));
@@ -282,7 +278,8 @@ 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_size, '\0');
std::string out_buffer;
out_buffer.resize(out_buffer_size);
auto src_buffer = &input[0];
size_t src_bytes = in_bytes;
@@ -307,7 +304,6 @@ 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);
@@ -317,6 +313,8 @@ 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));
@@ -328,7 +326,8 @@ 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_size, char16_t{});
std::u16string out_buffer;
out_buffer.resize(out_buffer_size);
char* src_buffer = const_cast<char*>(&input[0]);
size_t src_bytes = in_bytes;
@@ -353,7 +352,6 @@ std::u16string UTF8ToUTF16(const std::string& input) {
}
}
std::u16string result;
out_buffer.resize(out_buffer_size - dst_bytes);
out_buffer.swap(result);

View File

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

View File

@@ -1,7 +1,5 @@
add_library(core STATIC
arm/arm_interface.h
arm/exclusive_monitor.cpp
arm/exclusive_monitor.h
arm/unicorn/arm_unicorn.cpp
arm/unicorn/arm_unicorn.h
core.cpp

View File

@@ -102,28 +102,18 @@ public:
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() {
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
// Callbacks
config.callbacks = cb.get();
// Memory
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
// Multi-process state
config.processor_id = core_index;
config.global_monitor = &exclusive_monitor->monitor;
// System registers
config.tpidrro_el0 = &cb->tpidrro_el0;
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
return std::make_unique<Dynarmic::A64::Jit>(config);
}
@@ -138,11 +128,8 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)),
jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(
exclusive_monitor)},
core_index{core_index} {
ARM_Dynarmic::ARM_Dynarmic()
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
ARM_Interface::ThreadContext ctx;
inner_unicorn.SaveContext(ctx);
LoadContext(ctx);
@@ -250,46 +237,6 @@ void ARM_Dynarmic::ClearExclusiveState() {
}
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit();
jit = MakeJit(cb);
current_page_table = Memory::GetCurrentPageTable();
}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, u64 addr) {
// Size doesn't actually matter.
monitor.Mark(core_index, addr, 16);
}
void DynarmicExclusiveMonitor::ClearExclusive() {
monitor.Clear();
}
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
[&] { Memory::Write8(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
[&] { Memory::Write16(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
[&] { Memory::Write32(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
[&] { Memory::Write64(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, u64 vaddr,
std::array<std::uint64_t, 2> value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
Memory::Write64(vaddr, value[0]);
Memory::Write64(vaddr, value[1]);
});
}

View File

@@ -6,18 +6,15 @@
#include <memory>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/exclusive_monitor.h>
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index);
ARM_Dynarmic();
~ARM_Dynarmic();
void MapBackingMemory(VAddr address, size_t size, u8* memory,
@@ -50,35 +47,10 @@ public:
void PageTableChanged() override;
private:
std::unique_ptr<Dynarmic::A64::Jit> MakeJit();
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
std::unique_ptr<Dynarmic::A64::Jit> jit;
ARM_Unicorn inner_unicorn;
size_t core_index;
std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
Memory::PageTable* current_page_table = nullptr;
};
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public:
explicit DynarmicExclusiveMonitor(size_t core_count);
~DynarmicExclusiveMonitor();
void SetExclusive(size_t core_index, u64 addr) override;
void ClearExclusive() override;
bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) override;
bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) override;
bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) override;
bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) override;
bool ExclusiveWrite128(size_t core_index, u64 vaddr,
std::array<std::uint64_t, 2> value) override;
private:
friend class ARM_Dynarmic;
Dynarmic::A64::ExclusiveMonitor monitor;
};

View File

@@ -1,7 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/exclusive_monitor.h"
ExclusiveMonitor::~ExclusiveMonitor() = default;

View File

@@ -1,23 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_types.h"
class ExclusiveMonitor {
public:
virtual ~ExclusiveMonitor();
virtual void SetExclusive(size_t core_index, u64 addr) = 0;
virtual void ClearExclusive() = 0;
virtual bool ExclusiveWrite8(size_t core_index, u64 vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(size_t core_index, u64 vaddr, u16 value) = 0;
virtual bool ExclusiveWrite32(size_t core_index, u64 vaddr, u32 value) = 0;
virtual bool ExclusiveWrite64(size_t core_index, u64 vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(size_t core_index, u64 vaddr,
std::array<std::uint64_t, 2> value) = 0;
};

View File

@@ -171,9 +171,8 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index);
}
gpu_core = std::make_unique<Tegra::GPU>();

View File

@@ -9,7 +9,6 @@
#include <string>
#include <thread>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
@@ -115,11 +114,6 @@ public:
return CurrentCpuCore().ArmInterface();
}
/// Gets the index of the currently running CPU core
size_t CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
/// Gets an ARM interface to the CPU core with the specified index
ARM_Interface& ArmInterface(size_t core_index);
@@ -136,11 +130,6 @@ public:
return *CurrentCpuCore().Scheduler();
}
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor() {
return *cpu_exclusive_monitor;
}
/// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
@@ -197,7 +186,6 @@ private:
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;

View File

@@ -48,15 +48,14 @@ bool CpuBarrier::Rendezvous() {
return false;
}
Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
: cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
arm_interface = std::make_shared<ARM_Dynarmic>();
#else
arm_interface = std::make_shared<ARM_Unicorn>();
cpu_core = std::make_shared<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
@@ -66,18 +65,6 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
}
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
#else
return nullptr; // TODO(merry): Passthrough exclusive monitor
#endif
} else {
return nullptr; // TODO(merry): Passthrough exclusive monitor
}
}
void Cpu::RunLoop(bool tight_loop) {
// Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
if (!cpu_barrier->Rendezvous()) {

View File

@@ -10,7 +10,6 @@
#include <mutex>
#include <string>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
class ARM_Interface;
@@ -41,8 +40,7 @@ private:
class Cpu {
public:
Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
void RunLoop(bool tight_loop = true);
@@ -66,12 +64,6 @@ public:
return core_index == 0;
}
size_t CoreIndex() const {
return core_index;
}
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores);
private:
void Reschedule();

View File

@@ -13,7 +13,7 @@ namespace FileSys {
VfsFile::~VfsFile() = default;
std::string VfsFile::GetExtension() const {
return std::string(FileUtil::GetExtensionFromFilename(GetName()));
return FileUtil::GetExtensionFromFilename(GetName());
}
VfsDirectory::~VfsDirectory() = default;
@@ -46,80 +46,64 @@ 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(std::string_view path) const {
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& 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(std::string_view path) const {
if (IsRoot()) {
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const {
if (IsRoot())
return GetFileRelative(path);
}
return GetParentDirectory()->GetFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const {
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& 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(std::string_view path) const {
if (IsRoot()) {
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const {
if (IsRoot())
return GetDirectoryRelative(path);
}
return GetParentDirectory()->GetDirectoryAbsolute(path);
}
std::shared_ptr<VfsFile> VfsDirectory::GetFile(std::string_view name) const {
std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& 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(std::string_view name) const {
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const {
const auto& subs = GetSubdirectories();
const auto iter = std::find_if(subs.begin(), subs.end(),
[&name](const auto& file1) { return name == file1->GetName(); });
@@ -144,96 +128,77 @@ size_t VfsDirectory::GetSize() const {
return file_total + subdir_total;
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) {
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& 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(std::string_view path) {
if (IsRoot()) {
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) {
if (IsRoot())
return CreateFileRelative(path);
}
return GetParentDirectory()->CreateFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) {
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& 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(std::string_view path) {
if (IsRoot()) {
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) {
if (IsRoot())
return CreateDirectoryRelative(path);
}
return GetParentDirectory()->CreateDirectoryAbsolute(path);
}
bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& 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(std::string_view src, std::string_view dest) {
bool VfsDirectory::Copy(const std::string& src, const std::string& 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);
@@ -251,23 +216,23 @@ bool ReadOnlyVfsDirectory::IsReadable() const {
return true;
}
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) {
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) {
return nullptr;
}
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name) {
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) {
return nullptr;
}
bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) {
return false;
}
bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) {
return false;
}
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
bool ReadOnlyVfsDirectory::Rename(const std::string& name) {
return false;
}
} // namespace FileSys

View File

@@ -6,11 +6,11 @@
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/file_util.h"
namespace FileSys {
struct VfsFile;
@@ -112,7 +112,7 @@ struct VfsFile : NonCopyable {
}
// Renames the file to name. Returns whether or not the operation was successsful.
virtual bool Rename(std::string_view name) = 0;
virtual bool Rename(const std::string& 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(std::string_view path) const;
virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const;
// Calls GetFileRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsFile> GetFileAbsolute(std::string_view path) const;
virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& 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(std::string_view path) const;
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const;
// Calls GetDirectoryRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(std::string_view path) const;
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& 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(std::string_view name) const;
virtual std::shared_ptr<VfsFile> GetFile(const std::string& 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(std::string_view name) const;
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const;
// Returns whether or not the directory can be written to.
virtual bool IsWritable() const = 0;
@@ -161,56 +161,53 @@ 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(std::string_view name) = 0;
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& 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(std::string_view name) = 0;
virtual std::shared_ptr<VfsFile> CreateFile(const std::string& 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(std::string_view path);
virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& 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(std::string_view path);
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& 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(std::string_view path);
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& 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(std::string_view path);
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path);
// Deletes the subdirectory with name and returns true on success.
virtual bool DeleteSubdirectory(std::string_view name) = 0;
virtual bool DeleteSubdirectory(const std::string& name) = 0;
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
// the subdirectory. Returns true on success.
virtual bool DeleteSubdirectoryRecursive(std::string_view name);
virtual bool DeleteSubdirectoryRecursive(const std::string& name);
// Returnes whether or not the file with name name was deleted successfully.
virtual bool DeleteFile(std::string_view name) = 0;
virtual bool DeleteFile(const std::string& name) = 0;
// Returns whether or not this directory was renamed to name.
virtual bool Rename(std::string_view name) = 0;
virtual bool Rename(const std::string& name) = 0;
// Returns whether or not the file with name src was successfully copied to a new file with name
// dest.
virtual bool Copy(std::string_view src, std::string_view dest);
virtual bool Copy(const std::string& src, const std::string& 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(std::string_view file) {
bool InterpretAsDirectory(const std::string& file) {
auto file_p = GetFile(file);
if (file_p == nullptr) {
if (file_p == nullptr)
return false;
}
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p));
}
protected:
@@ -224,10 +221,10 @@ protected:
struct ReadOnlyVfsDirectory : public VfsDirectory {
bool IsWritable() const override;
bool IsReadable() const 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;
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;
};
} // namespace FileSys

View File

@@ -80,7 +80,7 @@ size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
}
bool OffsetVfsFile::Rename(std::string_view name) {
bool OffsetVfsFile::Rename(const std::string& name) {
return file->Rename(name);
}

View File

@@ -4,9 +4,6 @@
#pragma once
#include <memory>
#include <string_view>
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -33,7 +30,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(std::string_view name) override;
bool Rename(const std::string& name) override;
size_t GetOffset() const;

View File

@@ -72,15 +72,12 @@ size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
return backing.WriteBytes(data, length);
}
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);
bool RealVfsFile::Rename(const std::string& name) {
const auto out = FileUtil::Rename(GetName(), name);
path = parent_path + DIR_SEP + name;
path_components = parent_components;
path_components.push_back(std::move(name_str));
path_components.push_back(name);
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
return out;
}
@@ -138,54 +135,36 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
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)) {
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) {
if (!FileUtil::CreateDir(path + DIR_SEP + name))
return nullptr;
}
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms));
return subdirectories.back();
}
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)) {
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) {
if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name))
return nullptr;
}
files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms));
return files.back();
}
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
return FileUtil::DeleteDirRecursively(subdir_path);
bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) {
return FileUtil::DeleteDirRecursively(path + DIR_SEP + name);
}
bool RealVfsDirectory::DeleteFile(std::string_view name) {
const auto file = GetFile(name);
if (file == nullptr) {
bool RealVfsDirectory::DeleteFile(const std::string& name) {
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();
const std::string file_path = (path + DIR_SEP).append(name);
return FileUtil::Delete(file_path);
return FileUtil::Delete(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::Rename(const std::string& name) {
return FileUtil::Rename(path, parent_path + DIR_SEP + name);
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {

View File

@@ -4,8 +4,6 @@
#pragma once
#include <string_view>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
@@ -26,7 +24,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(std::string_view name) override;
bool Rename(const std::string& name) override;
private:
bool Close();
@@ -49,11 +47,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(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;
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;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;

View File

@@ -24,7 +24,7 @@ namespace Service::FileSystem {
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name) {
const std::string& dir_name) {
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
return base;

View File

@@ -49,8 +49,7 @@ FileType GuessFromFilename(const std::string& name) {
if (name == "main")
return FileType::DeconstructedRomDirectory;
const std::string extension =
Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name)));
const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name));
if (extension == "elf")
return FileType::ELF;

View File

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

View File

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

View File

@@ -338,18 +338,6 @@ bool GMainWindow::SupportsRequiredGLExtensions() {
unsupported_ext.append("ARB_separate_shader_objects");
if (!GLAD_GL_ARB_vertex_attrib_binding)
unsupported_ext.append("ARB_vertex_attrib_binding");
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)
unsupported_ext.append("EXT_texture_compression_s3tc");
if (!GLAD_GL_ARB_texture_compression_rgtc)
unsupported_ext.append("ARB_texture_compression_rgtc");
if (!GLAD_GL_ARB_texture_compression_bptc)
unsupported_ext.append("ARB_texture_compression_bptc");
if (!GLAD_GL_ARB_depth_buffer_float)
unsupported_ext.append("ARB_depth_buffer_float");
for (const QString& ext : unsupported_ext)
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());

View File

@@ -85,20 +85,10 @@ 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);