Compare commits

...

63 Commits

Author SHA1 Message Date
David Marcec
10f494eefe Better UUID randomness 2018-08-12 02:31:43 +10:00
David Marcec
448290bee4 Removed un-needed count from ListOpenUsers and ListAllUsers 2018-08-12 02:11:04 +10:00
David Marcec
2592e41301 Added better explanations in the profile manager 2018-08-12 01:51:31 +10:00
David Marcec
0b6f8ba51e Code cleanup for profile manager 2018-08-12 01:34:22 +10:00
David Marcec
d0b2950434 Removed const from ProfileBase Invalidate 2018-08-12 00:41:17 +10:00
David Marcec
42431d2aa6 fixed invalid uuid bool operator 2018-08-11 21:29:10 +10:00
David Marcec
b8e70faa2d Added GetOpenUserCount 2018-08-11 20:45:06 +10:00
David Marcec
662218e997 Removed all for loops from the profile manager 2018-08-11 20:15:59 +10:00
David Marcec
c3013c7c9c Added missing ListAllUsers count 2018-08-11 20:06:06 +10:00
David Marcec
acff922762 If statement style change 2018-08-11 18:46:42 +10:00
David Marcec
dfea525cbe Second round of account changes 2018-08-11 18:26:13 +10:00
David Marcec
82fa0bcea7 First round of account changes 2018-08-11 16:47:33 +10:00
David Marcec
4fa4d80c68 Rebase with dynarmic master 2018-08-11 13:35:04 +10:00
David Marcec
6aa8ee6943 Refactored profile manager sharing 2018-08-11 13:17:06 +10:00
David Marcec
b76ddb7647 Merge remote-tracking branch 'origin/master' into better-account 2018-08-11 10:35:47 +10:00
David Marcec
2a3b335b15 Added IsUserRegistrationRequestPermitted 2018-08-11 10:33:11 +10:00
bunnei
0a003efde4 Merge pull request #1007 from MerryMage/dynarmic
dynarmic: Update to 0118ee0
2018-08-10 15:57:50 -04:00
bunnei
2e8620c877 Merge pull request #1011 from bunnei/misc-vtx-fmt
Implements VertexAttributes Size_32_32_32 and Size_8_8.
2018-08-10 15:57:28 -04:00
bunnei
6b0bc48a42 maxwell_to_gl: Implement VertexAttribute::Size::Size_8_8.
- Used by Super Mario Odyssey.
2018-08-10 12:47:00 -04:00
bunnei
a5b65df9cf maxwell_to_gl: Implement VertexAttribute::Size::Size_32_32_32.
- Used by Super Mario Odyssey.
2018-08-10 12:46:49 -04:00
bunnei
57626fda7b Merge pull request #1004 from lioncash/unused
gl_rasterizer_cache: Remove unused viewport parameter of GetFramebufferSurfaces()
2018-08-10 12:13:32 -04:00
bunnei
6313d54cef Merge pull request #1008 from yuzu-emu/revert-697-disable-depth-cull
Revert "gl_state: Temporarily disable culling and depth test."
2018-08-10 12:13:09 -04:00
bunnei
7e6a73963e Merge pull request #1002 from bunnei/refactor-tex-fmt
textures: Refactor out for Texture/Depth FormatFromPixelFormat.
2018-08-10 11:06:23 -04:00
bunnei
2156cb3cbe Revert "gl_state: Temporarily disable culling and depth test." 2018-08-10 10:39:46 -04:00
MerryMage
ef41983c84 dynarmic: Update to 0118ee0
0118ee0 emit_x64_vector: packusdw is SSE4.1
2018-08-10 11:15:04 +01:00
Lioncash
0e1510ac29 gl_rasterizer_cache: Remove unused viewport parameter of GetFramebufferSurfaces() 2018-08-09 20:55:41 -04:00
bunnei
3a67876252 textures: Refactor out for Texture/Depth FormatFromPixelFormat. 2018-08-09 20:36:03 -04:00
bunnei
6828c25498 Merge pull request #995 from bunnei/gl-buff-bounds
gl_rasterizer_cache: Add bounds checking for gl_buffer copies.
2018-08-09 20:23:30 -04:00
bunnei
5191c20b71 Merge pull request #997 from lioncash/const-func
core: Make function reference parameters const where applicable
2018-08-09 19:30:51 -04:00
bunnei
a91bb7080d Merge pull request #989 from lioncash/log
common/logging: Add missing service log categories
2018-08-09 19:30:14 -04:00
bunnei
69cd213fac Merge pull request #990 from lioncash/entry
fsp_srv: Emplace entries first when building index instead of emplacing last
2018-08-09 19:29:36 -04:00
bunnei
05c33d89a1 Merge pull request #1001 from lioncash/reserve
gl_shader_decompiler: Reserve element memory beforehand in BuildRegisterList()
2018-08-09 19:27:35 -04:00
bunnei
96ef22d3d0 Merge pull request #897 from DarkLordZach/vfs-accuracy-2
vfs: Add VfsFilesystem and fix RealVfs* implementations
2018-08-09 19:22:06 -04:00
bunnei
e8c52d4c89 gl_rasterizer_cache: Add bounds checking for gl_buffer copies. 2018-08-09 19:20:17 -04:00
bunnei
37e1ed3744 Merge pull request #991 from bunnei/ignore-mac
maxwell_3d: Ignore macros that have not been uploaded yet.
2018-08-09 19:16:28 -04:00
Khangaroo
75e12a33ae Implement SNORM for BC5/DXN2 (#998)
* Implement BC5/DXN2 (#996)

- Used by Kirby Star Allies.

* Implement BC5/DXN2 SNORM

UNORM for Kirby Star Allies
SNORM for Super Mario Odyssey
2018-08-09 19:15:32 -04:00
bunnei
e58f7ff843 Merge pull request #999 from lioncash/map
gl_rasterizer_cache: Avoid iterator invalidation issues within InvalidateRegion()
2018-08-09 19:13:27 -04:00
Lioncash
6ef027b958 gl_shader_decompiler: Reserve element memory beforehand in BuildRegisterList()
Avoids potentially perfoming multiple reallocations when we know the
total amount of memory we need beforehand.
2018-08-09 17:29:11 -04:00
Lioncash
59ea37daa7 gl_rasterizer_cache: Avoid iterator invalidation issues within InvalidateRegion()
A range-based for loop can't be used when the container being iterated
is also being erased from.
2018-08-09 15:30:20 -04:00
Lioncash
b46a5c42ff buffer_queue: Make reference parameter of SetPreallocatedBuffer const
This is simply copied by value, so there's no need to make it a
modifiable reference.

While we're at it, make the names of the parameters match its
definition.
2018-08-09 03:08:14 -04:00
Lioncash
ff5024ee2a hle_ipc: Make WriteToOutgoingCommandBuffer()'s reference parameter const
This function doesn't modify anything within the reference Thread
instance.
2018-08-09 02:51:38 -04:00
David Marcec
4e1471ef21 Don't add user if the uuid already exists 2018-08-09 13:30:58 +10:00
bunnei
efe6b473c5 maxwell_3d: Ignore macros that have not been uploaded yet.
- Used by Super Mario Odyssey (in game).
2018-08-08 23:25:37 -04:00
Zach Hilman
668458525e vfs: Fix documentation 2018-08-08 21:45:04 -04:00
Zach Hilman
94cf327e77 vfs: Fix typo in VfsFilesystem docs 2018-08-08 21:18:45 -04:00
Zach Hilman
2b6128fe0b file_util: Use enum instead of bool for specifing path behavior 2018-08-08 21:18:45 -04:00
Zach Hilman
dad2ae1ee0 loader: Remove unused IdentifyFile overload 2018-08-08 21:18:45 -04:00
Zach Hilman
656e97df16 vfs: Use RealVfsFilesystem for fs-operations in RealVfsDirectory 2018-08-08 21:18:45 -04:00
Zach Hilman
52a2e42cb9 file_sys: Add missing include in savedata_factory 2018-08-08 21:18:45 -04:00
Zach Hilman
4b471f0554 core: Port core to VfsFilesystem for file access 2018-08-08 21:18:45 -04:00
Zach Hilman
aaa8fdea52 vfs: Add unreachable assert to file permissions converter 2018-08-08 21:18:45 -04:00
Zach Hilman
2de2ec25d6 vfs: Add RealVfsFilesystem implementation 2018-08-08 21:18:45 -04:00
Zach Hilman
3f82dad1e4 file_util: Add platform-specific slash option to SanitizePath 2018-08-08 21:18:45 -04:00
Zach Hilman
3bf488ce52 vfs: Add VfsFilesystem interface and default implementation 2018-08-08 21:18:45 -04:00
Zach Hilman
b36dee364e filesystem: Remove unnecessary if conditions 2018-08-08 21:18:45 -04:00
Lioncash
7353cfc781 fsp_srv: Use std::string_view's copy() function instead of strncpy()
Given elements inserted into a vector are zeroed out, we can just copy
MAX_LEN - 1 elements and the data will already be properly null
terminated.
2018-08-08 18:51:52 -04:00
Lioncash
4afb05d0cc fsp_srv: Emplace entries first when building index instead of emplacing last
The current way were doing it would require copying a 768 character
buffer (part of the Entry struct) to the new element in the vector.
Given it's a plain array, std::move won't eliminate that.

Instead, we can emplace an instance directly into the destination buffer
and then fill it out, avoiding the need to perform any unnecessary
copies.

Given this is done in a loop, we can request the destination to allocate
all of the necessary memory ahead of time, avoiding the need to
potentially keep reallocating over and over on every few insertions into
the vector.
2018-08-08 18:51:41 -04:00
Lioncash
6e90f0bf6a common/logging: Add missing service log categories
These weren't added when the services were introduced.
2018-08-08 16:46:28 -04:00
David Marcec
e9978fd4f5 Open first user added 2018-08-09 01:37:55 +10:00
David Marcec
75169c7570 Inital pass of account backend implementation
This commit verified working on puyo
2018-08-09 01:09:12 +10:00
David Marcec
03d7faf583 GetProfileBase and GetProfileBaseAndData added 2018-08-08 23:41:12 +10:00
David Marcec
6f691e71bf began initial implementation of "ProfileManager" 2018-08-08 22:26:42 +10:00
David Marcec
5f8d253ce0 Switched uuids from u128 to new UUID struct 2018-08-08 21:09:45 +10:00
52 changed files with 1194 additions and 459 deletions

View File

@@ -884,11 +884,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
return path;
}
std::string SanitizePath(std::string_view path_) {
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string path(path_);
std::replace(path.begin(), path.end(), '\\', '/');
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
if (directory_separator == DirectorySeparator::PlatformDefault) {
#ifdef _WIN32
type1 = '/';
type2 = '\\';
#endif
}
std::replace(path.begin(), path.end(), type1, type2);
path.erase(std::unique(path.begin(), path.end(),
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
return std::string(RemoveTrailingSlash(path));
}

View File

@@ -182,8 +182,12 @@ 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);
enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
std::string SanitizePath(std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier

View File

@@ -171,15 +171,21 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BTDRV) \
SUB(Service, BTM) \
SUB(Service, Capture) \
SUB(Service, ERPT) \
SUB(Service, ETicket) \
SUB(Service, EUPLD) \
SUB(Service, Fatal) \
SUB(Service, FGM) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \
SUB(Service, LM) \
SUB(Service, Migration) \
SUB(Service, Mii) \
@@ -188,11 +194,13 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, NFC) \
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
SUB(Service, PM) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, SET) \

View File

@@ -58,15 +58,21 @@ enum class Class : ClassType {
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BPC, ///< The BPC service
Service_BTDRV, ///< The Bluetooth driver service
Service_BTM, ///< The BTM service
Service_Capture, ///< The capture service
Service_ERPT, ///< The error reporting service
Service_ETicket, ///< The ETicket service
Service_EUPLD, ///< The error upload service
Service_Fatal, ///< The Fatal service
Service_FGM, ///< The FGM service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service
Service_LM, ///< The LM (Logger) service
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
@@ -75,11 +81,13 @@ enum class Class : ClassType {
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
Service_PM, ///< The PM service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_SET, ///< The SET (Settings) service

View File

@@ -114,6 +114,8 @@ add_library(core STATIC
hle/service/acc/acc_u0.h
hle/service/acc/acc_u1.cpp
hle/service/acc/acc_u1.h
hle/service/acc/profile_manager.cpp
hle/service/acc/profile_manager.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/applet_ae.cpp

View File

@@ -89,7 +89,7 @@ System::ResultStatus System::SingleStep() {
}
System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -174,6 +174,10 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
CoreTiming::Init();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
@@ -186,7 +190,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
service_manager = std::make_shared<Service::SM::ServiceManager>();
Kernel::Init();
Service::Init(service_manager);
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);

View File

@@ -17,6 +17,8 @@
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "file_sys/vfs_real.h"
#include "hle/service/filesystem/filesystem.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
@@ -211,6 +213,14 @@ public:
return debug_context;
}
void SetFilesystem(FileSys::VirtualFilesystem vfs) {
virtual_filesystem = std::move(vfs);
}
FileSys::VirtualFilesystem GetFilesystem() const {
return virtual_filesystem;
}
private:
System();
@@ -225,6 +235,8 @@ private:
*/
ResultStatus Init(EmuWindow& emu_window);
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;

View File

@@ -4,8 +4,9 @@
#pragma once
#include <array>
#include <cstddef>
#include <iterator>
#include <string_view>
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -21,9 +22,14 @@ enum EntryType : u8 {
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
const size_t FILENAME_LENGTH = 0x300;
struct Entry {
char filename[FILENAME_LENGTH];
Entry(std::string_view view, EntryType entry_type, u64 entry_size)
: type{entry_type}, file_size{entry_size} {
const size_t copy_size = view.copy(filename, std::size(filename) - 1);
filename[copy_size] = '\0';
}
char filename[0x300];
INSERT_PADDING_BYTES(4);
EntryType type;
INSERT_PADDING_BYTES(3);

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
namespace FileSys {

View File

@@ -4,12 +4,160 @@
#include <algorithm>
#include <numeric>
#include <string>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
VfsFilesystem::~VfsFilesystem() = default;
std::string VfsFilesystem::GetName() const {
return root->GetName();
}
bool VfsFilesystem::IsReadable() const {
return root->IsReadable();
}
bool VfsFilesystem::IsWritable() const {
return root->IsWritable();
}
VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
const auto path = FileUtil::SanitizePath(path_);
if (root->GetFileRelative(path) != nullptr)
return VfsEntryType::File;
if (root->GetDirectoryRelative(path) != nullptr)
return VfsEntryType::Directory;
return VfsEntryType::None;
}
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->GetFileRelative(path);
}
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->CreateFileRelative(path);
}
VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// VfsDirectory impls are only required to implement copy across the current directory.
if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) {
if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path)))
return nullptr;
return OpenFile(new_path, Mode::ReadWrite);
}
// Do it using RawCopy. Non-default impls are encouraged to optimize this.
const auto old_file = OpenFile(old_path, Mode::Read);
if (old_file == nullptr)
return nullptr;
auto new_file = OpenFile(new_path, Mode::Read);
if (new_file != nullptr)
return nullptr;
new_file = CreateFile(new_path, Mode::Write);
if (new_file == nullptr)
return nullptr;
if (!VfsRawCopy(old_file, new_file))
return nullptr;
return new_file;
}
VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Again, non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyFile(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteFile(old_path))
return out;
return nullptr;
}
bool VfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_);
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
return parent->DeleteFile(FileUtil::GetFilename(path));
}
VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->GetDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->CreateDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto old_dir = OpenDirectory(old_path, Mode::Read);
if (old_dir == nullptr)
return nullptr;
auto new_dir = OpenDirectory(new_path, Mode::Read);
if (new_dir != nullptr)
return nullptr;
new_dir = CreateDirectory(new_path, Mode::Write);
if (new_dir == nullptr)
return nullptr;
for (const auto& file : old_dir->GetFiles()) {
const auto x =
CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName());
if (x == nullptr)
return nullptr;
}
for (const auto& dir : old_dir->GetSubdirectories()) {
const auto x =
CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName());
if (x == nullptr)
return nullptr;
}
return new_dir;
}
VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyDirectory(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteDirectory(old_path))
return out;
return nullptr;
}
bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_);
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path));
}
VfsFile::~VfsFile() = default;
std::string VfsFile::GetExtension() const {

View File

@@ -11,14 +11,74 @@
#include <vector>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "core/file_sys/mode.h"
namespace FileSys {
struct VfsFilesystem;
struct VfsFile;
struct VfsDirectory;
// Convenience typedefs to use VfsDirectory and VfsFile
using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>;
using VirtualFile = std::shared_ptr<FileSys::VfsFile>;
// Convenience typedefs to use Vfs* interfaces
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
None,
File,
Directory,
};
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
// is provided for convenience, but if the Vfs implementation has any additional state or
// functionality, they will need to override.
struct VfsFilesystem : NonCopyable {
VfsFilesystem(VirtualDir root);
virtual ~VfsFilesystem();
// Gets the friendly name for the filesystem.
virtual std::string GetName() const;
// Return whether or not the user has read permissions on this filesystem.
virtual bool IsReadable() const;
// Return whether or not the user has write permission on this filesystem.
virtual bool IsWritable() const;
// Determine if the entry at path is non-existant, a file, or a directory.
virtual VfsEntryType GetEntryType(std::string_view path) const;
// Opens the file with path relative to root. If it doesn't exist, returns nullptr.
virtual VirtualFile OpenFile(std::string_view path, Mode perms);
// Creates a new, empty file at path
virtual VirtualFile CreateFile(std::string_view path, Mode perms);
// Copies the file from old_path to new_path, returning the new file on success and nullptr on
// failure.
virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
// Moves the file from old_path to new_path, returning the moved file on success and nullptr on
// failure.
virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
// Deletes the file with path relative to root, returing true on success.
virtual bool DeleteFile(std::string_view path);
// Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
// Creates a new, empty directory at path
virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
// Copies the directory from old_path to new_path, returning the new directory on success and
// nullptr on failure.
virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
// Moves the directory from old_path to new_path, returning the moved directory on success and
// nullptr on failure.
virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
// Deletes the directory with path relative to root, returing true on success.
virtual bool DeleteDirectory(std::string_view path);
protected:
// Root directory in default implementation.
VirtualDir root;
};
// A class representing a file in an abstract filesystem.
struct VfsFile : NonCopyable {

View File

@@ -6,7 +6,7 @@
#include <cstddef>
#include <iterator>
#include <utility>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -29,6 +29,8 @@ static std::string ModeFlagsToString(Mode mode) {
mode_str = "a";
else if (mode & Mode::Write)
mode_str = "w";
else
UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
}
mode_str += "b";
@@ -36,8 +38,174 @@ static std::string ModeFlagsToString(Mode mode) {
return mode_str;
}
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
std::string RealVfsFilesystem::GetName() const {
return "Real";
}
bool RealVfsFilesystem::IsReadable() const {
return true;
}
bool RealVfsFilesystem::IsWritable() const {
return true;
}
VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path))
return VfsEntryType::None;
if (FileUtil::IsDirectory(path))
return VfsEntryType::Directory;
return VfsEntryType::File;
}
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (cache.find(path) != cache.end()) {
auto weak = cache[path];
if (!weak.expired()) {
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
}
}
if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
FileUtil::CreateEmptyFile(path);
auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
cache[path] = backing;
// Cannot use make_shared as RealVfsFile constructor is private
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
}
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
return nullptr;
return OpenFile(path, perms);
}
VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
return nullptr;
return OpenFile(new_path, Mode::ReadWrite);
}
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
return nullptr;
if (cache.find(old_path) != cache.end()) {
auto cached = cache[old_path];
if (!cached.expired()) {
auto file = cached.lock();
file->Open(new_path, "r+b");
cache.erase(old_path);
cache[new_path] = file;
}
}
return OpenFile(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (cache.find(path) != cache.end()) {
if (!cache[path].expired())
cache[path].lock()->Close();
cache.erase(path);
}
return FileUtil::Delete(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path))
return nullptr;
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
!FileUtil::IsDirectory(old_path))
return nullptr;
FileUtil::CopyDir(old_path, new_path);
return OpenDirectory(new_path, Mode::ReadWrite);
}
VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
return nullptr;
for (auto& kv : cache) {
// Path in cache starts with old_path
if (kv.first.rfind(old_path, 0) == 0) {
const auto file_old_path =
FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault);
const auto file_new_path =
FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
FileUtil::DirectorySeparator::PlatformDefault);
auto cached = cache[file_old_path];
if (!cached.expired()) {
auto file = cached.lock();
file->Open(file_new_path, "r+b");
cache.erase(file_old_path);
cache[file_new_path] = file;
}
}
}
return OpenDirectory(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
for (auto& kv : cache) {
// Path in cache starts with old_path
if (kv.first.rfind(path, 0) == 0) {
if (!cache[kv.first].expired())
cache[kv.first].lock()->Close();
cache.erase(kv.first);
}
}
return FileUtil::DeleteDirRecursively(path);
}
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_,
const std::string& path_, Mode perms_)
: base(base_), backing(std::move(backing_)), path(path_),
parent_path(FileUtil::GetParentPath(path_)),
path_components(FileUtil::SplitPathComponents(path_)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -48,15 +216,15 @@ std::string RealVfsFile::GetName() const {
}
size_t RealVfsFile::GetSize() const {
return backing.GetSize();
return backing->GetSize();
}
bool RealVfsFile::Resize(size_t new_size) {
return backing.Resize(new_size);
return backing->Resize(new_size);
}
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
return std::make_shared<RealVfsDirectory>(parent_path, perms);
return base.OpenDirectory(parent_path, perms);
}
bool RealVfsFile::IsWritable() const {
@@ -68,62 +236,118 @@ bool RealVfsFile::IsReadable() const {
}
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
if (!backing.Seek(offset, SEEK_SET))
if (!backing->Seek(offset, SEEK_SET))
return 0;
return backing.ReadBytes(data, length);
return backing->ReadBytes(data, length);
}
size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
if (!backing.Seek(offset, SEEK_SET))
if (!backing->Seek(offset, SEEK_SET))
return 0;
return backing.WriteBytes(data, length);
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);
return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
}
path = (parent_path + DIR_SEP).append(name);
path_components = parent_components;
path_components.push_back(std::move(name_str));
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
bool RealVfsFile::Close() {
return backing->Close();
}
// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
// constexpr' because there is a compile error in the branch not used.
template <>
std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
if (perms == Mode::Append)
return {};
std::vector<VirtualFile> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (!FileUtil::IsDirectory(full_path))
out.emplace_back(base.OpenFile(full_path, perms));
return true;
});
return out;
}
bool RealVfsFile::Close() {
return backing.Close();
template <>
std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
if (perms == Mode::Append)
return {};
std::vector<VirtualDir> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
out.emplace_back(base.OpenDirectory(full_path, perms));
return true;
});
return out;
}
RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
: path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)),
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
: base(base_), path(FileUtil::RemoveTrailingSlash(path_)),
parent_path(FileUtil::GetParentPath(path)),
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
FileUtil::CreateDir(path);
}
if (perms == Mode::Append)
return;
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenFile(full_path, perms);
}
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms));
else
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms));
return true;
});
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenDirectory(full_path, perms);
}
std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const {
return GetFileRelative(name);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const {
return GetDirectoryRelative(name);
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
return base.CreateFile(full_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
auto parent = std::string(FileUtil::GetParentPath(full_path));
return base.CreateDirectory(full_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name));
return base.DeleteDirectory(full_path);
}
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
return files;
return IterateEntries<RealVfsFile, VfsFile>();
}
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
return subdirectories;
return IterateEntries<RealVfsDirectory, VfsDirectory>();
}
bool RealVfsDirectory::IsWritable() const {
@@ -142,57 +366,32 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
if (path_components.size() <= 1)
return nullptr;
return std::make_shared<RealVfsDirectory>(parent_path, perms);
return base.OpenDirectory(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)) {
return nullptr;
}
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
return subdirectories.back();
return base.CreateDirectory(subdir_path, perms);
}
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>(file_path, perms));
return files.back();
return base.CreateFile(file_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
return FileUtil::DeleteDirRecursively(subdir_path);
return base.DeleteDirectory(subdir_path);
}
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();
const std::string file_path = (path + DIR_SEP).append(name);
return FileUtil::Delete(file_path);
return base.DeleteFile(file_path);
}
bool RealVfsDirectory::Rename(std::string_view name) {
const std::string new_name = (parent_path + DIR_SEP).append(name);
return FileUtil::Rename(path, new_name);
return base.MoveFile(path, new_name) != nullptr;
}
std::string RealVfsDirectory::GetFullPath() const {
@@ -202,16 +401,6 @@ std::string RealVfsDirectory::GetFullPath() const {
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
const auto iter = std::find(files.begin(), files.end(), file);
if (iter == files.end())
return false;
const std::ptrdiff_t offset = std::distance(files.begin(), iter);
files[offset] = files.back();
files.pop_back();
subdirectories.emplace_back(std::move(dir));
return true;
return false;
}
} // namespace FileSys

View File

@@ -6,18 +6,45 @@
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
std::string GetName() const override;
bool IsReadable() const override;
bool IsWritable() const override;
VfsEntryType GetEntryType(std::string_view path) const override;
VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override;
VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override;
VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
bool DeleteFile(std::string_view path) override;
VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override;
VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override;
VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
bool DeleteDirectory(std::string_view path) override;
private:
boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache;
};
// An implmentation of VfsFile that represents a file on the user's computer.
struct RealVfsFile : public VfsFile {
friend struct RealVfsDirectory;
class RealVfsFile : public VfsFile {
friend class RealVfsDirectory;
friend class RealVfsFilesystem;
RealVfsFile(const std::string& name, Mode perms = Mode::Read);
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
public:
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
@@ -31,7 +58,8 @@ struct RealVfsFile : public VfsFile {
private:
bool Close();
FileUtil::IOFile backing;
RealVfsFilesystem& base;
std::shared_ptr<FileUtil::IOFile> backing;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
@@ -40,9 +68,19 @@ private:
};
// An implementation of VfsDirectory that represents a directory on the user's computer.
struct RealVfsDirectory : public VfsDirectory {
RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem;
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
public:
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
bool DeleteSubdirectoryRecursive(std::string_view name) override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
@@ -60,13 +98,15 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const;
RealVfsFilesystem& base;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
std::vector<std::string> parent_components;
Mode perms;
std::vector<std::shared_ptr<VfsFile>> files;
std::vector<std::shared_ptr<VfsDirectory>> subdirectories;
};
} // namespace FileSys

View File

@@ -201,7 +201,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));

View File

@@ -132,7 +132,7 @@ public:
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
HandleTable& src_table);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
u32_le GetCommand() const {
return command;

View File

@@ -3,7 +3,10 @@
// Refer to the license.txt file included.
#include <array>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/acc/acc_aa.h"
@@ -13,7 +16,6 @@
#include "core/settings.h"
namespace Service::Account {
// TODO: RE this structure
struct UserData {
INSERT_PADDING_WORDS(1);
@@ -25,19 +27,13 @@ struct UserData {
};
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
struct ProfileBase {
u128 user_id;
u64 timestamp;
std::array<u8, 0x20> username;
};
static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size");
// TODO(ogniK): Generate a real user id based on username, md5(username) maybe?
static constexpr u128 DEFAULT_USER_ID{1ull, 0ull};
static UUID DEFAULT_USER_ID{1ull, 0ull};
class IProfile final : public ServiceFramework<IProfile> {
public:
explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
: ServiceFramework("IProfile"), user_id(user_id), profile_manager(profile_manager) {
static const FunctionInfo functions[] = {
{0, &IProfile::Get, "Get"},
{1, &IProfile::GetBase, "GetBase"},
@@ -49,38 +45,34 @@ public:
private:
void Get(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
profile_base.user_id = user_id;
if (Settings::values.username.size() > profile_base.username.size()) {
std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
profile_base.username.begin());
std::array<u8, MAX_DATA> data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
} else {
std::copy(Settings::values.username.begin(), Settings::values.username.end(),
profile_base.username.begin());
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}",
user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
}
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
}
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): Retrieve this information from somewhere.
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
profile_base.user_id = user_id;
if (Settings::values.username.size() > profile_base.username.size()) {
std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
profile_base.username.begin());
if (profile_manager.GetProfileBase(user_id, profile_base)) {
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
} else {
std::copy(Settings::values.username.begin(), Settings::values.username.end(),
profile_base.username.begin());
LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1)); // TODO(ogniK): Get actual error code
}
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
}
void LoadImage(Kernel::HLERequestContext& ctx) {
@@ -104,7 +96,8 @@ private:
rb.Push<u32>(jpeg_size);
}
u128 user_id; ///< The user id this profile refers to.
const ProfileManager& profile_manager;
UUID user_id; ///< The user id this profile refers to.
};
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
@@ -141,44 +134,57 @@ private:
};
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
LOG_INFO(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
}
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::RequestParser rp{ctx};
UUID user_id = rp.PopRaw<UUID>();
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(true); // TODO: Check when this is supposed to return true and when not
rb.Push(profile_manager->UserExists(user_id));
}
void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): There is only one user for now.
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
ctx.WriteBuffer(user_ids);
LOG_INFO(Service_ACC, "called");
ctx.WriteBuffer(profile_manager->GetAllUsers());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// TODO(Subv): There is only one user for now.
const std::vector<u128> user_ids = {DEFAULT_USER_ID};
ctx.WriteBuffer(user_ids);
LOG_INFO(Service_ACC, "called");
ctx.WriteBuffer(profile_manager->GetOpenUsers());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<UUID>(profile_manager->GetLastOpenedUser());
}
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 user_id = rp.PopRaw<u128>();
UUID user_id = rp.PopRaw<UUID>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IProfile>(user_id);
LOG_DEBUG(Service_ACC, "called user_id=0x{:016X}{:016X}", user_id[1], user_id[0]);
rb.PushIpcInterface<IProfile>(user_id, *profile_manager);
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
}
void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(profile_manager->CanSystemRegisterUser());
}
void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
@@ -194,22 +200,18 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
LOG_DEBUG(Service_ACC, "called");
}
void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(DEFAULT_USER_ID);
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
Module::Interface::Interface(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager, const char* name)
: ServiceFramework(name), module(std::move(module)),
profile_manager(std::move(profile_manager)) {}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<ACC_AA>(module)->InstallAsService(service_manager);
std::make_shared<ACC_SU>(module)->InstallAsService(service_manager);
std::make_shared<ACC_U0>(module)->InstallAsService(service_manager);
std::make_shared<ACC_U1>(module)->InstallAsService(service_manager);
auto profile_manager = std::make_shared<ProfileManager>();
std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
}
} // namespace Service::Account

View File

@@ -4,6 +4,7 @@
#pragma once
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/service.h"
namespace Service::Account {
@@ -12,7 +13,8 @@ class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
explicit Interface(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager, const char* name);
void GetUserCount(Kernel::HLERequestContext& ctx);
void GetUserExistence(Kernel::HLERequestContext& ctx);
@@ -22,9 +24,11 @@ public:
void GetProfile(Kernel::HLERequestContext& ctx);
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
std::shared_ptr<ProfileManager> profile_manager;
};
};

View File

@@ -6,7 +6,8 @@
namespace Service::Account {
ACC_AA::ACC_AA(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:aa") {
ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
: Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") {
static const FunctionInfo functions[] = {
{0, nullptr, "EnsureCacheAsync"},
{1, nullptr, "LoadCache"},

View File

@@ -10,7 +10,8 @@ namespace Service::Account {
class ACC_AA final : public Module::Interface {
public:
explicit ACC_AA(std::shared_ptr<Module> module);
explicit ACC_AA(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
};
} // namespace Service::Account

View File

@@ -6,7 +6,8 @@
namespace Service::Account {
ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:su") {
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
: Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
static const FunctionInfo functions[] = {
{0, &ACC_SU::GetUserCount, "GetUserCount"},
{1, &ACC_SU::GetUserExistence, "GetUserExistence"},
@@ -15,7 +16,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
{4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_SU::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, nullptr, "IsUserRegistrationRequestPermitted"},
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, nullptr, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, nullptr, "GetUserRegistrationNotifier"},

View File

@@ -11,7 +11,8 @@ namespace Account {
class ACC_SU final : public Module::Interface {
public:
explicit ACC_SU(std::shared_ptr<Module> module);
explicit ACC_SU(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
};
} // namespace Account

View File

@@ -6,7 +6,8 @@
namespace Service::Account {
ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u0") {
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
static const FunctionInfo functions[] = {
{0, &ACC_U0::GetUserCount, "GetUserCount"},
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
@@ -15,7 +16,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
{4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U0::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, nullptr, "IsUserRegistrationRequestPermitted"},
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, nullptr, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},

View File

@@ -10,7 +10,8 @@ namespace Service::Account {
class ACC_U0 final : public Module::Interface {
public:
explicit ACC_U0(std::shared_ptr<Module> module);
explicit ACC_U0(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
};
} // namespace Service::Account

View File

@@ -6,7 +6,8 @@
namespace Service::Account {
ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u1") {
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
static const FunctionInfo functions[] = {
{0, &ACC_U1::GetUserCount, "GetUserCount"},
{1, &ACC_U1::GetUserExistence, "GetUserExistence"},
@@ -15,7 +16,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(mod
{4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U1::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, nullptr, "IsUserRegistrationRequestPermitted"},
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, nullptr, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, nullptr, "GetUserRegistrationNotifier"},

View File

@@ -10,7 +10,8 @@ namespace Service::Account {
class ACC_U1 final : public Module::Interface {
public:
explicit ACC_U1(std::shared_ptr<Module> module);
explicit ACC_U1(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
};
} // namespace Service::Account

View File

@@ -0,0 +1,226 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <boost/optional.hpp>
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
namespace Service::Account {
// TODO(ogniK): Get actual error codes
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
ProfileManager::ProfileManager() {
// TODO(ogniK): Create the default user we have for now until loading/saving users is added
auto user_uuid = UUID{1, 0};
CreateNewUser(user_uuid, Settings::values.username);
OpenUser(user_uuid);
}
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
/// internal management of the users profiles
boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
if (user_count >= MAX_USERS) {
return boost::none;
}
profiles[user_count] = std::move(user);
return user_count++;
}
/// Deletes a specific profile based on it's profile index
bool ProfileManager::RemoveProfileAtIndex(size_t index) {
if (index >= MAX_USERS || index >= user_count) {
return false;
}
if (index < user_count - 1) {
std::rotate(profiles.begin() + index, profiles.begin() + index + 1, profiles.end());
}
profiles.back() = {};
user_count--;
return true;
}
/// Helper function to register a user to the system
ResultCode ProfileManager::AddUser(ProfileInfo user) {
if (AddToProfiles(user) == boost::none) {
return ERROR_TOO_MANY_USERS;
}
return RESULT_SUCCESS;
}
/// Create a new user on the system. If the uuid of the user already exists, the user is not
/// created.
ResultCode ProfileManager::CreateNewUser(UUID uuid, std::array<u8, 0x20>& username) {
if (user_count == MAX_USERS) {
return ERROR_TOO_MANY_USERS;
}
if (!uuid) {
return ERROR_ARGUMENT_IS_NULL;
}
if (username[0] == 0x0) {
return ERROR_ARGUMENT_IS_NULL;
}
if (std::any_of(profiles.begin(), profiles.end(),
[&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) {
return ERROR_USER_ALREADY_EXISTS;
}
ProfileInfo profile;
profile.user_uuid = std::move(uuid);
profile.username = username;
profile.data = {};
profile.creation_time = 0x0;
profile.is_open = false;
return AddUser(profile);
}
/// Creates a new user on the system. This function allows a much simpler method of registration
/// specifically by allowing an std::string for the username. This is required specifically since
/// we're loading a string straight from the config
ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
std::array<u8, 0x20> username_output;
if (username.size() > username_output.size()) {
std::copy_n(username.begin(), username_output.size(), username_output.begin());
} else {
std::copy(username.begin(), username.end(), username_output.begin());
}
return CreateNewUser(uuid, username_output);
}
/// Returns a users profile index based on their user id.
boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
return boost::none;
}
auto iter = std::find_if(profiles.begin(), profiles.end(),
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
if (iter == profiles.end()) {
return boost::none;
}
return static_cast<size_t>(std::distance(profiles.begin(), iter));
}
/// Returns a users profile index based on their profile
boost::optional<size_t> ProfileManager::GetUserIndex(ProfileInfo user) const {
return GetUserIndex(user.user_uuid);
}
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
bool ProfileManager::GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const {
if (index == boost::none || index >= MAX_USERS) {
return false;
}
const auto& prof_info = profiles[index.get()];
profile.user_uuid = prof_info.user_uuid;
profile.username = prof_info.username;
profile.timestamp = prof_info.creation_time;
return true;
}
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
auto idx = GetUserIndex(uuid);
return GetProfileBase(idx, profile);
}
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
bool ProfileManager::GetProfileBase(ProfileInfo user, ProfileBase& profile) const {
return GetProfileBase(user.user_uuid, profile);
}
/// Returns the current user count on the system. We keep a variable which tracks the count so we
/// don't have to loop the internal profile array every call.
size_t ProfileManager::GetUserCount() const {
return user_count;
}
/// Lists the current "opened" users on the system. Users are typically not open until they sign
/// into something or pick a profile. As of right now users should all be open until qlaunch is
/// booting
size_t ProfileManager::GetOpenUserCount() const {
return std::count_if(profiles.begin(), profiles.end(),
[](const ProfileInfo& p) { return p.is_open; });
}
/// Checks if a user id exists in our profile manager
bool ProfileManager::UserExists(UUID uuid) const {
return (GetUserIndex(uuid) != boost::none);
}
/// Opens a specific user
void ProfileManager::OpenUser(UUID uuid) {
auto idx = GetUserIndex(uuid);
if (idx == boost::none) {
return;
}
profiles[idx.get()].is_open = true;
last_opened_user = uuid;
}
/// Closes a specific user
void ProfileManager::CloseUser(UUID uuid) {
auto idx = GetUserIndex(uuid);
if (idx == boost::none) {
return;
}
profiles[idx.get()].is_open = false;
}
/// Gets all valid user ids on the system
std::array<UUID, MAX_USERS> ProfileManager::GetAllUsers() const {
std::array<UUID, MAX_USERS> output;
std::transform(profiles.begin(), profiles.end(), output.begin(),
[](const ProfileInfo& p) { return p.user_uuid; });
return output;
}
/// Get all the open users on the system and zero out the rest of the data. This is specifically
/// needed for GetOpenUsers and we need to ensure the rest of the output buffer is zero'd out
std::array<UUID, MAX_USERS> ProfileManager::GetOpenUsers() const {
std::array<UUID, MAX_USERS> output;
std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
if (p.is_open)
return p.user_uuid;
return UUID{};
});
std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
return output;
}
/// Returns the last user which was opened
UUID ProfileManager::GetLastOpenedUser() const {
return last_opened_user;
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile,
std::array<u8, MAX_DATA>& data) const {
if (GetProfileBase(index, profile)) {
std::memcpy(data.data(), profiles[index.get()].data.data(), MAX_DATA);
return true;
}
return false;
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
std::array<u8, MAX_DATA>& data) const {
auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(ProfileInfo user, ProfileBase& profile,
std::array<u8, MAX_DATA>& data) const {
return GetProfileBaseAndData(user.user_uuid, profile, data);
}
/// Returns if the system is allowing user registrations or not
bool ProfileManager::CanSystemRegisterUser() const {
return false; // TODO(ogniK): Games shouldn't have
// access to user registration, when we
// emulate qlaunch. Update this to dynamically change.
}
}; // namespace Service::Account

View File

@@ -0,0 +1,124 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <random>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
namespace Service::Account {
constexpr size_t MAX_USERS = 8;
constexpr size_t MAX_DATA = 128;
static const u128 INVALID_UUID = {0, 0};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid = INVALID_UUID;
UUID() = default;
explicit UUID(const u128& id) : uuid{id} {}
explicit UUID(const u64 lo, const u64 hi) {
uuid[0] = lo;
uuid[1] = hi;
};
explicit operator bool() const {
return uuid[0] != INVALID_UUID[0] || uuid[1] != INVALID_UUID[1];
}
bool operator==(const UUID& rhs) const {
return std::tie(uuid[0], uuid[1]) == std::tie(rhs.uuid[0], rhs.uuid[1]);
}
bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
const UUID& Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<uint64_t> distribution(1,
std::numeric_limits<uint64_t>::max());
uuid[0] = distribution(gen);
uuid[1] = distribution(gen);
return *this;
}
// Set the UUID to {0,0} to be considered an invalid user
void Invalidate() {
uuid = INVALID_UUID;
}
std::string Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
UUID user_uuid;
std::array<u8, 0x20> username;
u64 creation_time;
std::array<u8, MAX_DATA> data; // TODO(ognik): Work out what this is
bool is_open;
};
struct ProfileBase {
UUID user_uuid;
u64_le timestamp;
std::array<u8, 0x20> username;
// Zero out all the fields to make the profile slot considered "Empty"
void Invalidate() {
user_uuid.Invalidate();
timestamp = 0;
username.fill(0);
}
};
static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
/// The profile manager is used for handling multiple user profiles at once. It keeps track of open
/// users, all the accounts registered on the "system" as well as fetching individual "ProfileInfo"
/// objects
class ProfileManager {
public:
ProfileManager(); // TODO(ogniK): Load from system save
ResultCode AddUser(ProfileInfo user);
ResultCode CreateNewUser(UUID uuid, std::array<u8, 0x20>& username);
ResultCode CreateNewUser(UUID uuid, const std::string& username);
boost::optional<size_t> GetUserIndex(const UUID& uuid) const;
boost::optional<size_t> GetUserIndex(ProfileInfo user) const;
bool GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const;
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(ProfileInfo user, ProfileBase& profile) const;
bool GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile,
std::array<u8, MAX_DATA>& data) const;
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
std::array<u8, MAX_DATA>& data) const;
bool GetProfileBaseAndData(ProfileInfo user, ProfileBase& profile,
std::array<u8, MAX_DATA>& data) const;
size_t GetUserCount() const;
size_t GetOpenUserCount() const;
bool UserExists(UUID uuid) const;
void OpenUser(UUID uuid);
void CloseUser(UUID uuid);
std::array<UUID, MAX_USERS> GetOpenUsers() const;
std::array<UUID, MAX_USERS> GetAllUsers() const;
UUID GetLastOpenedUser() const;
bool CanSystemRegisterUser() const;
private:
std::array<ProfileInfo, MAX_USERS> profiles{};
size_t user_count = 0;
boost::optional<size_t> AddToProfiles(const ProfileInfo& profile);
bool RemoveProfileAtIndex(size_t index);
UUID last_opened_user{0, 0};
};
}; // namespace Service::Account

View File

@@ -59,7 +59,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path == "/" || path == "\\") {
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
@@ -281,15 +281,15 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
return sdmc_factory->Open();
}
void RegisterFileSystems() {
void RegisterFileSystems(const FileSys::VirtualFilesystem& vfs) {
romfs_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr;
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::ReadWrite);
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::ReadWrite);
auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
FileSys::Mode::ReadWrite);
auto sd_directory = vfs->OpenDirectory(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);
@@ -298,8 +298,8 @@ void RegisterFileSystems() {
sdmc_factory = std::move(sdcard);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
RegisterFileSystems();
void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) {
RegisterFileSystems(vfs);
std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
std::make_shared<FSP_PR>()->InstallAsService(service_manager);
std::make_shared<FSP_SRV>()->InstallAsService(service_manager);

View File

@@ -36,7 +36,7 @@ ResultVal<FileSys::VirtualDir> OpenSDMC();
// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
/// Registers all Filesystem services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs);
// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
// pointers and booleans. This makes using a VfsDirectory with switch services much easier and

View File

@@ -193,13 +193,10 @@ private:
template <typename T>
static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
FileSys::EntryType type) {
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
FileSys::Entry entry;
entry.filename[0] = '\0';
std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1);
entry.type = type;
entry.file_size = new_entry->GetSize();
entries.emplace_back(std::move(entry));
entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
}
}

View File

@@ -16,7 +16,7 @@ BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
Buffer buffer{};
buffer.slot = slot;
buffer.igbp_buffer = igbp_buffer;

View File

@@ -72,7 +72,7 @@ public:
MathUtil::Rectangle<int> crop_rect;
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform,

View File

@@ -198,7 +198,7 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
}
/// Initialize ServiceManager
void Init(std::shared_ptr<SM::ServiceManager>& sm) {
void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
// here and pass it into the respective InstallInterfaces functions.
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -221,7 +221,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
EUPLD::InstallInterfaces(*sm);
Fatal::InstallInterfaces(*sm);
FGM::InstallInterfaces(*sm);
FileSystem::InstallInterfaces(*sm);
FileSystem::InstallInterfaces(*sm, rfs);
Friend::InstallInterfaces(*sm);
GRC::InstallInterfaces(*sm);
HID::InstallInterfaces(*sm);

View File

@@ -22,6 +22,10 @@ class ServerSession;
class HLERequestContext;
} // namespace Kernel
namespace FileSys {
struct VfsFilesystem;
}
namespace Service {
namespace SM {
@@ -177,7 +181,8 @@ private:
};
/// Initialize ServiceManager
void Init(std::shared_ptr<SM::ServiceManager>& sm);
void Init(std::shared_ptr<SM::ServiceManager>& sm,
const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
/// Shutdown ServiceManager
void Shutdown();

View File

@@ -43,10 +43,6 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
return FileType::Unknown;
}
FileType IdentifyFile(const std::string& file_name) {
return IdentifyFile(std::make_shared<FileSys::RealVfsFile>(file_name));
}
FileType GuessFromFilename(const std::string& name) {
if (name == "main")
return FileType::DeconstructedRomDirectory;

View File

@@ -42,14 +42,6 @@ enum class FileType {
*/
FileType IdentifyFile(FileSys::VirtualFile file);
/**
* Identifies the type of a bootable file based on the magic value in its header.
* @param file_name path to file
* @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
* a filetype, and will never return FileType::Error.
*/
FileType IdentifyFile(const std::string& file_name);
/**
* Guess the type of a bootable file from its name
* @param name String name of bootable file

View File

@@ -23,12 +23,17 @@ Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager&
: memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
auto macro_code = uploaded_macros.find(method);
// The requested macro must have been uploaded already.
ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method);
// Reset the current macro and execute it.
// Reset the current macro.
executing_macro = 0;
// The requested macro must have been uploaded already.
auto macro_code = uploaded_macros.find(method);
if (macro_code == uploaded_macros.end()) {
LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method);
return;
}
// Execute the current macro.
macro_interpreter.Execute(macro_code->second, std::move(parameters));
}

View File

@@ -332,8 +332,6 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
// TODO(bunnei): Implement this
const bool has_stencil = false;
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
const bool write_color_fb =
state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE;
@@ -346,9 +344,10 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
Surface depth_surface;
MathUtil::Rectangle<u32> surfaces_rect;
std::tie(color_surface, depth_surface, surfaces_rect) =
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect);
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
MathUtil::Rectangle<u32> draw_rect{
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
const MathUtil::Rectangle<u32> draw_rect{
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left,
surfaces_rect.left, surfaces_rect.right)), // Left
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top,
@@ -812,9 +811,7 @@ void RasterizerOpenGL::SyncClipCoef() {
void RasterizerOpenGL::SyncCullMode() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(bunnei): Enable the below once more things work - until then, this may hide regressions
// state.cull.enabled = regs.cull.enabled != 0;
state.cull.enabled = false;
state.cull.enabled = regs.cull.enabled != 0;
if (state.cull.enabled) {
state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);

View File

@@ -109,7 +109,9 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // DXT45
{GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1
{GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN2
{GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // DXN2UNORM
{GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
{GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // BC7U
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
@@ -181,36 +183,49 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
return {0, actual_height, width, 0};
}
/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
static bool IsFormatBCn(PixelFormat format) {
switch (format) {
case PixelFormat::DXT1:
case PixelFormat::DXT23:
case PixelFormat::DXT45:
case PixelFormat::DXN1:
case PixelFormat::DXN2SNORM:
case PixelFormat::DXN2UNORM:
case PixelFormat::BC7U:
return true;
}
return false;
}
template <bool morton_to_gl, PixelFormat format>
void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr addr) {
void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer,
Tegra::GPUVAddr addr) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
const auto& gpu = Core::System::GetInstance().GPU();
if (morton_to_gl) {
if (SurfaceParams::GetFormatType(format) == SurfaceType::ColorTexture) {
auto data = Tegra::Texture::UnswizzleTexture(
*gpu.memory_manager->GpuToCpuAddress(addr),
SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height);
std::memcpy(gl_buffer, data.data(), data.size());
} else {
auto data = Tegra::Texture::UnswizzleDepthTexture(
*gpu.memory_manager->GpuToCpuAddress(addr),
SurfaceParams::DepthFormatFromPixelFormat(format), stride, height, block_height);
std::memcpy(gl_buffer, data.data(), data.size());
}
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
// pixel values.
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
const std::vector<u8> data =
Tegra::Texture::UnswizzleTexture(*gpu.memory_manager->GpuToCpuAddress(addr), tile_size,
bytes_per_pixel, stride, height, block_height);
const size_t size_to_copy{std::min(gl_buffer.size(), data.size())};
gl_buffer.assign(data.begin(), data.begin() + size_to_copy);
} else {
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
// check the configuration for this and perform more generic un/swizzle
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
VideoCore::MortonCopyPixels128(
stride, height, bytes_per_pixel, gl_bytes_per_pixel,
Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer,
Memory::GetPointer(*gpu.memory_manager->GpuToCpuAddress(addr)), gl_buffer.data(),
morton_to_gl);
}
}
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
SurfaceParams::MaxPixelFormat>
morton_to_gl_fns = {
MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
@@ -219,20 +234,21 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>,
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
MortonCopy<true, PixelFormat::DXN2>, MortonCopy<true, PixelFormat::BC7U>,
MortonCopy<true, PixelFormat::ASTC_2D_4X4>, MortonCopy<true, PixelFormat::G8R8>,
MortonCopy<true, PixelFormat::BGRA8>, MortonCopy<true, PixelFormat::RGBA32F>,
MortonCopy<true, PixelFormat::RG32F>, MortonCopy<true, PixelFormat::R32F>,
MortonCopy<true, PixelFormat::R16F>, MortonCopy<true, PixelFormat::R16UNORM>,
MortonCopy<true, PixelFormat::RG16>, MortonCopy<true, PixelFormat::RG16F>,
MortonCopy<true, PixelFormat::RG16UI>, MortonCopy<true, PixelFormat::RG16I>,
MortonCopy<true, PixelFormat::RG16S>, MortonCopy<true, PixelFormat::RGB32F>,
MortonCopy<true, PixelFormat::SRGBA8>, MortonCopy<true, PixelFormat::Z24S8>,
MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z32FS8>,
MortonCopy<true, PixelFormat::DXN2UNORM>, MortonCopy<true, PixelFormat::DXN2SNORM>,
MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>,
MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>,
MortonCopy<true, PixelFormat::R32F>, MortonCopy<true, PixelFormat::R16F>,
MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::RG16>,
MortonCopy<true, PixelFormat::RG16F>, MortonCopy<true, PixelFormat::RG16UI>,
MortonCopy<true, PixelFormat::RG16I>, MortonCopy<true, PixelFormat::RG16S>,
MortonCopy<true, PixelFormat::RGB32F>, MortonCopy<true, PixelFormat::SRGBA8>,
MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>,
MortonCopy<true, PixelFormat::Z32FS8>,
};
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
SurfaceParams::MaxPixelFormat>
gl_to_morton_fns = {
MortonCopy<false, PixelFormat::ABGR8>,
@@ -251,6 +267,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::G8R8>,
MortonCopy<false, PixelFormat::BGRA8>,
MortonCopy<false, PixelFormat::RGBA32F>,
@@ -462,7 +480,7 @@ void CachedSurface::LoadGLBuffer() {
gl_buffer.resize(copy_size);
morton_to_gl_fns[static_cast<size_t>(params.pixel_format)](
params.width, params.block_height, params.height, gl_buffer.data(), params.addr);
params.width, params.block_height, params.height, gl_buffer, params.addr);
} else {
const u8* const texture_src_data_end = texture_src_data + copy_size;
@@ -489,7 +507,7 @@ void CachedSurface::FlushGLBuffer() {
std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
} else {
gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
params.width, params.block_height, params.height, gl_buffer.data(), params.addr);
params.width, params.block_height, params.height, gl_buffer, params.addr);
}
}
@@ -604,8 +622,8 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
return GetSurface(SurfaceParams::CreateForTexture(config));
}
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) {
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb,
bool using_depth_fb) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(bunnei): This is hard corded to use just the first render buffer
@@ -761,10 +779,12 @@ void RasterizerCacheOpenGL::FlushRegion(Tegra::GPUVAddr /*addr*/, size_t /*size*
}
void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) {
for (const auto& pair : surface_cache) {
const auto& surface{pair.second};
for (auto iter = surface_cache.cbegin(); iter != surface_cache.cend();) {
const auto& surface{iter->second};
const auto& params{surface->GetSurfaceParams()};
++iter;
if (params.IsOverlappingRegion(addr, size)) {
UnregisterSurface(surface);
}

View File

@@ -35,32 +35,33 @@ struct SurfaceParams {
DXT23 = 9,
DXT45 = 10,
DXN1 = 11, // This is also known as BC4
DXN2 = 12, // This is also known as BC5
BC7U = 13,
ASTC_2D_4X4 = 14,
G8R8 = 15,
BGRA8 = 16,
RGBA32F = 17,
RG32F = 18,
R32F = 19,
R16F = 20,
R16UNORM = 21,
RG16 = 22,
RG16F = 23,
RG16UI = 24,
RG16I = 25,
RG16S = 26,
RGB32F = 27,
SRGBA8 = 28,
DXN2UNORM = 12,
DXN2SNORM = 13,
BC7U = 14,
ASTC_2D_4X4 = 15,
G8R8 = 16,
BGRA8 = 17,
RGBA32F = 18,
RG32F = 19,
R32F = 20,
R16F = 21,
R16UNORM = 22,
RG16 = 23,
RG16F = 24,
RG16UI = 25,
RG16I = 26,
RG16S = 27,
RGB32F = 28,
SRGBA8 = 29,
MaxColorFormat,
// DepthStencil formats
Z24S8 = 29,
S8Z24 = 30,
Z32F = 31,
Z16 = 32,
Z32FS8 = 33,
Z24S8 = 30,
S8Z24 = 31,
Z32F = 32,
Z16 = 33,
Z32FS8 = 34,
MaxDepthStencilFormat,
@@ -110,7 +111,8 @@ struct SurfaceParams {
4, // DXT23
4, // DXT45
4, // DXN1
4, // DXN2
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // ASTC_2D_4X4
1, // G8R8
@@ -155,7 +157,8 @@ struct SurfaceParams {
128, // DXT23
128, // DXT45
64, // DXN1
128, // DXN2
128, // DXN2UNORM
128, // DXN2SNORM
128, // BC7U
32, // ASTC_2D_4X4
16, // G8R8
@@ -309,7 +312,15 @@ struct SurfaceParams {
case Tegra::Texture::TextureFormat::DXN1:
return PixelFormat::DXN1;
case Tegra::Texture::TextureFormat::DXN2:
return PixelFormat::DXN2;
switch (component_type) {
case Tegra::Texture::ComponentType::UNORM:
return PixelFormat::DXN2UNORM;
case Tegra::Texture::ComponentType::SNORM:
return PixelFormat::DXN2SNORM;
}
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
static_cast<u32>(component_type));
UNREACHABLE();
case Tegra::Texture::TextureFormat::BC7U:
return PixelFormat::BC7U;
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
@@ -337,91 +348,6 @@ struct SurfaceParams {
}
}
static Tegra::Texture::TextureFormat TextureFormatFromPixelFormat(PixelFormat format) {
// TODO(Subv): Properly implement this
switch (format) {
case PixelFormat::ABGR8:
case PixelFormat::SRGBA8:
return Tegra::Texture::TextureFormat::A8R8G8B8;
case PixelFormat::B5G6R5:
return Tegra::Texture::TextureFormat::B5G6R5;
case PixelFormat::A2B10G10R10:
return Tegra::Texture::TextureFormat::A2B10G10R10;
case PixelFormat::A1B5G5R5:
return Tegra::Texture::TextureFormat::A1B5G5R5;
case PixelFormat::R8:
return Tegra::Texture::TextureFormat::R8;
case PixelFormat::G8R8:
return Tegra::Texture::TextureFormat::G8R8;
case PixelFormat::RGBA16F:
return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
case PixelFormat::R11FG11FB10F:
return Tegra::Texture::TextureFormat::BF10GF11RF11;
case PixelFormat::RGBA32UI:
return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
case PixelFormat::DXT1:
return Tegra::Texture::TextureFormat::DXT1;
case PixelFormat::DXT23:
return Tegra::Texture::TextureFormat::DXT23;
case PixelFormat::DXT45:
return Tegra::Texture::TextureFormat::DXT45;
case PixelFormat::DXN1:
return Tegra::Texture::TextureFormat::DXN1;
case PixelFormat::DXN2:
return Tegra::Texture::TextureFormat::DXN2;
case PixelFormat::BC7U:
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::RGB32F:
return Tegra::Texture::TextureFormat::R32_G32_B32;
case PixelFormat::RG32F:
return Tegra::Texture::TextureFormat::R32_G32;
case PixelFormat::R32F:
return Tegra::Texture::TextureFormat::R32;
case PixelFormat::R16F:
case PixelFormat::R16UNORM:
return Tegra::Texture::TextureFormat::R16;
case PixelFormat::Z32F:
return Tegra::Texture::TextureFormat::ZF32;
case PixelFormat::Z24S8:
return Tegra::Texture::TextureFormat::Z24S8;
case PixelFormat::RG16F:
case PixelFormat::RG16:
case PixelFormat::RG16UI:
case PixelFormat::RG16I:
case PixelFormat::RG16S:
return Tegra::Texture::TextureFormat::R16_G16;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static Tegra::DepthFormat DepthFormatFromPixelFormat(PixelFormat format) {
switch (format) {
case PixelFormat::S8Z24:
return Tegra::DepthFormat::S8_Z24_UNORM;
case PixelFormat::Z24S8:
return Tegra::DepthFormat::Z24_S8_UNORM;
case PixelFormat::Z32F:
return Tegra::DepthFormat::Z32_FLOAT;
case PixelFormat::Z16:
return Tegra::DepthFormat::Z16_UNORM;
case PixelFormat::Z32FS8:
return Tegra::DepthFormat::Z32_S8_X24_FLOAT;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
}
}
static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
// TODO(Subv): Implement more component types
switch (type) {
@@ -622,8 +548,7 @@ public:
Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
/// Get the color and depth surfaces based on the framebuffer configuration
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
const MathUtil::Rectangle<s32>& viewport);
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb);
/// Flushes the surface to Switch memory
void FlushSurface(const Surface& surface);

View File

@@ -507,6 +507,8 @@ private:
/// Build the GLSL register list.
void BuildRegisterList() {
regs.reserve(Register::NumRegisters);
for (size_t index = 0; index < Register::NumRegisters; ++index) {
regs.emplace_back(index, suffix);
}

View File

@@ -45,6 +45,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
case Maxwell::VertexAttribute::Type::SignedNorm: {
switch (attrib.size) {
case Maxwell::VertexAttribute::Size::Size_32_32_32:
return GL_INT;
case Maxwell::VertexAttribute::Size::Size_8_8:
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
return GL_BYTE;
case Maxwell::VertexAttribute::Size::Size_16_16:

View File

@@ -86,88 +86,11 @@ u32 BytesPerPixel(TextureFormat format) {
}
}
static u32 DepthBytesPerPixel(DepthFormat format) {
switch (format) {
case DepthFormat::Z16_UNORM:
return 2;
case DepthFormat::S8_Z24_UNORM:
case DepthFormat::Z24_S8_UNORM:
case DepthFormat::Z32_FLOAT:
return 4;
case DepthFormat::Z32_S8_X24_FLOAT:
return 8;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
}
}
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
u32 block_height) {
u8* data = Memory::GetPointer(address);
u32 bytes_per_pixel = BytesPerPixel(format);
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
u32 height, u32 block_height) {
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
switch (format) {
case TextureFormat::DXT1:
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
case TextureFormat::DXN2:
case TextureFormat::BC7U:
// In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
// values.
CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
unswizzled_data.data(), true, block_height);
break;
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::A1B5G5R5:
case TextureFormat::B5G6R5:
case TextureFormat::R8:
case TextureFormat::G8R8:
case TextureFormat::R16_G16_B16_A16:
case TextureFormat::R32_G32_B32_A32:
case TextureFormat::R32_G32:
case TextureFormat::R32:
case TextureFormat::R16:
case TextureFormat::R16_G16:
case TextureFormat::BF10GF11RF11:
case TextureFormat::ASTC_2D_4X4:
case TextureFormat::R32_G32_B32:
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
unswizzled_data.data(), true, block_height);
break;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
}
return unswizzled_data;
}
std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
u32 block_height) {
u8* data = Memory::GetPointer(address);
u32 bytes_per_pixel = DepthBytesPerPixel(format);
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
switch (format) {
case DepthFormat::Z16_UNORM:
case DepthFormat::S8_Z24_UNORM:
case DepthFormat::Z24_S8_UNORM:
case DepthFormat::Z32_FLOAT:
case DepthFormat::Z32_S8_X24_FLOAT:
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
unswizzled_data.data(), true, block_height);
break;
default:
UNIMPLEMENTED_MSG("Format not implemented");
break;
}
CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel,
Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
return unswizzled_data;
}

View File

@@ -13,8 +13,8 @@ namespace Tegra::Texture {
/**
* Unswizzles a swizzled texture without changing its format.
*/
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
u32 block_height = TICEntry::DefaultBlockHeight);
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
u32 height, u32 block_height = TICEntry::DefaultBlockHeight);
/**
* Unswizzles a swizzled depth texture without changing its format.

View File

@@ -383,8 +383,10 @@ void GraphicsSurfaceWidget::OnUpdate() {
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
boost::optional<VAddr> address = gpu.memory_manager->GpuToCpuAddress(surface_address);
auto unswizzled_data =
Tegra::Texture::UnswizzleTexture(*address, surface_format, surface_width, surface_height);
// TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
// Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
*address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height);
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
surface_width, surface_height);

View File

@@ -15,6 +15,7 @@
#include "common/string_util.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/loader/loader.h"
#include "game_list.h"
@@ -197,7 +198,8 @@ void GameList::onFilterCloseClicked() {
main_window->filterBarSetChecked(false);
}
GameList::GameList(GMainWindow* parent) : QWidget{parent} {
GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
: QWidget{parent}, vfs(std::move(vfs)) {
watcher = new QFileSystemWatcher(this);
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
@@ -341,7 +343,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
emit ShouldCancelWorker();
GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan);
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
@@ -414,8 +416,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
bool is_dir = FileUtil::IsDirectory(physical_name);
QFileInfo file_info(physical_name.c_str());
if (!is_dir && file_info.suffix().toStdString() == "nca") {
auto nca = std::make_shared<FileSys::NCA>(
std::make_shared<FileSys::RealVfsFile>(physical_name));
auto nca =
std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
if (nca->GetType() == FileSys::NCAContentType::Control)
nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
}
@@ -436,7 +438,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
std::unique_ptr<Loader::AppLoader> loader =
Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
loader->GetFileType() == Loader::FileType::Error) &&
!UISettings::values.show_unknown))
@@ -459,7 +461,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
// Use from metadata pool.
if (nca_control_map.find(program_id) != nca_control_map.end()) {
const auto nca = nca_control_map[program_id];
const auto control_dir = nca->GetSubdirectories()[0];
const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
const auto nacp_file = control_dir->GetFile("control.nacp");
FileSys::NACP nacp(nacp_file);

View File

@@ -59,7 +59,7 @@ public:
QToolButton* button_filter_close = nullptr;
};
explicit GameList(GMainWindow* parent = nullptr);
explicit GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent = nullptr);
~GameList() override;
void clearFilter();
@@ -90,6 +90,7 @@ private:
void PopupContextMenu(const QPoint& menu_location);
void RefreshGameDirectory();
FileSys::VirtualFilesystem vfs;
SearchField* search_field;
GMainWindow* main_window = nullptr;
QVBoxLayout* layout = nullptr;

View File

@@ -139,8 +139,8 @@ class GameListWorker : public QObject, public QRunnable {
Q_OBJECT
public:
GameListWorker(QString dir_path, bool deep_scan)
: dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan)
: vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
public slots:
/// Starts the processing of directory tree information.
@@ -163,6 +163,7 @@ signals:
void Finished(QStringList watch_list);
private:
FileSys::VirtualFilesystem vfs;
QStringList watch_list;
QString dir_path;
bool deep_scan;

View File

@@ -24,6 +24,7 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h"
#include "core/settings.h"
@@ -83,7 +84,9 @@ void GMainWindow::ShowCallouts() {}
const int GMainWindow::max_recent_files_item;
GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
GMainWindow::GMainWindow()
: config(new Config()), emu_thread(nullptr),
vfs(std::make_shared<FileSys::RealVfsFilesystem>()) {
debug_context = Tegra::DebugContext::Construct();
@@ -132,7 +135,7 @@ void GMainWindow::InitializeWidgets() {
render_window = new GRenderWindow(this, emu_thread.get());
render_window->hide();
game_list = new GameList(this);
game_list = new GameList(vfs, this);
ui.horizontalLayout->addWidget(game_list);
// Create status bar
@@ -406,6 +409,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
}
Core::System& system{Core::System::GetInstance()};
system.SetFilesystem(vfs);
system.SetGPUDebugContext(debug_context);

View File

@@ -161,6 +161,9 @@ private:
bool emulation_running = false;
std::unique_ptr<EmuThread> emu_thread;
// FS
FileSys::VirtualFilesystem vfs;
// Debugger panes
ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog;

View File

@@ -161,6 +161,7 @@ int main(int argc, char** argv) {
}
Core::System& system{Core::System::GetInstance()};
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
SCOPE_EXIT({ system.Shutdown(); });