Compare commits

...

118 Commits

Author SHA1 Message Date
Lioncash
c945226973 gdbstub: Get rid of a few signed/unsigned comparisons
Ensures both operands in comparisons are the same signedness.
2018-07-19 15:27:01 -04:00
bunnei
427fc4ac6b Merge pull request #713 from lioncash/filesys
filesystem: Minor changes
2018-07-19 11:49:06 -07:00
bunnei
e91ba6c057 Merge pull request #711 from lioncash/swap
common/swap: Minor changes
2018-07-19 11:48:16 -07:00
bunnei
d6c7a05239 Merge pull request #710 from lioncash/unused
common/common_funcs: Remove unused rotation functions
2018-07-19 11:43:41 -07:00
bunnei
1034bcc742 Merge pull request #694 from lioncash/warn
loader/{nro, nso}: Resolve compilation warnings
2018-07-19 11:43:14 -07:00
bunnei
bbc31ba6af Merge pull request #709 from lioncash/thread-local
common/misc: Deduplicate code in GetLastErrorMsg()
2018-07-19 10:00:48 -07:00
bunnei
130a02f330 Merge pull request #708 from lioncash/xbyak
externals: Update Xbyak to 5.65
2018-07-19 10:00:06 -07:00
bunnei
8176ab3a07 Merge pull request #707 from lioncash/catch
externals: Update catch to v2.2.3
2018-07-19 09:59:44 -07:00
bunnei
04f7a7036a Merge pull request #705 from lioncash/string-ref
file_util: return string by const reference for GetExeDirectory()
2018-07-19 08:47:06 -07:00
bunnei
cbf43225a9 Merge pull request #704 from lioncash/string
string_util: Remove AsciiToHex()
2018-07-19 08:46:40 -07:00
bunnei
f1d7486eac Merge pull request #703 from lioncash/const
savedata_factory: Make SaveDataDescriptor's DebugInfo() function a const member function
2018-07-19 08:46:15 -07:00
bunnei
b0334af05b Merge pull request #702 from lioncash/initialize
partition_filesystem: Ensure all class members of PartitionFilesystem are initialized
2018-07-19 08:45:54 -07:00
bunnei
1bf7ae79c8 Merge pull request #701 from lioncash/moving
content_archive: Minor changes
2018-07-19 08:41:42 -07:00
bunnei
758c357868 Merge pull request #699 from lioncash/vfs
vfs: Deduplicate accumulation code in VfsDirectory's GetSize()
2018-07-19 07:38:45 -07:00
bunnei
87053fb3b8 Merge pull request #697 from bunnei/disable-depth-cull
gl_state: Temporarily disable culling and depth test.
2018-07-19 07:38:00 -07:00
Lioncash
5da4c78c6a filesystem: std::move VirtualDir instance in VfsDirectoryServiceWrapper's constructor
Avoids unnecessary atomic reference count incrementing and decrementing
2018-07-19 10:34:11 -04:00
Lioncash
abbf038191 filesystem: Use std::string's empty() function instead of comparing against a literal
This is simply a basic value check as opposed to potentially doing
string based operations (unlikely, but still, avoiding it is free).
2018-07-19 10:32:23 -04:00
Lioncash
2cc0ef83cf filesystem: Remove pragma disabling global optimizations
This was just an artifact missed during PR review.
2018-07-19 10:30:53 -04:00
Lioncash
5c47ea1a4e common/swap: Remove unnecessary const on return value of swap() 2018-07-19 09:35:54 -04:00
Lioncash
0a868641fa common/swap: Use static_cast where applicable 2018-07-19 09:35:13 -04:00
Lioncash
1edf4dd7ef common/swap: Use using aliases where applicable 2018-07-19 09:32:13 -04:00
Lioncash
9128271292 common/common_funcs: Remove unused rotation functions
These are unused and essentially don't provide much benefit either. If
we ever need rotation functions, these can be introduced in a way that
they don't sit in a common_* header and require a bunch of ifdefing to
simply be available
2018-07-19 09:21:23 -04:00
Lioncash
e0b8a35937 common/misc: Deduplicate code in GetLastErrorMsg()
Android and macOS have supported thread_local for quite a while, but
most importantly is that we don't even really need it. Instead of using
a thread-local buffer, we can just return a non-static buffer as a
std::string, avoiding the need for that quality entirely.
2018-07-19 09:15:38 -04:00
Lioncash
ef03d0178a externals: Update Xbyak to 5.65
Keeps the JIT assembler library up to date and ensures we don't run into
any issues that may have been resolved.
2018-07-19 08:38:07 -04:00
Lioncash
25f997097d externals: Update catch to v2.2.3
Keeps the unit-testing library up to date.
2018-07-19 08:33:06 -04:00
Lioncash
63e64f0131 file_util: return string by const reference for GetExeDirectory()
This disallows modifying the internal string buffer (which shouldn't be
modified anyhow).
2018-07-19 01:27:29 -04:00
bunnei
cd4fca8447 Merge pull request #700 from bunnei/update-dynarmic
externals: Update dynarmic to 5a91c94.
2018-07-18 22:01:39 -07:00
Lioncash
33fbcb45a7 string_util: Remove AsciiToHex()
Easy TODO
2018-07-18 23:57:15 -04:00
bunnei
368e1d25be Merge pull request #692 from lioncash/assign
address_arbiter: Correct assignment within an assertion statement in WakeThreads()
2018-07-18 20:56:28 -07:00
bunnei
85421f3406 Merge pull request #690 from lioncash/move
core/memory, core/hle/kernel: Use std::move where applicable
2018-07-18 20:55:55 -07:00
Lioncash
88ba94e8a2 savedata_factory: Make SaveDataDescriptor's DebugInfo() function a const member function
This function doesn't alter class state.
2018-07-18 23:50:07 -04:00
Lioncash
9abc5763b6 partition_filesystem: Ensure all class members of PartitionFilesystem are initialized
Previously is_hfs and pfs_header members wouldn't be initialized in the
constructor, as they were stored in locals instead. This would result in
things like GetName() and PrintDebugInfo() behaving incorrectly.

While we're at it, initialize the members to deterministic values as
well, in case loading ever fails.
2018-07-18 23:45:22 -04:00
bunnei
cf30c4be22 gl_state: Temporarily disable culling and depth test. 2018-07-18 23:21:43 -04:00
Lioncash
4790bb907d content_archive: Make IsDirectoryExeFS() take a shared_ptr as a const reference
There's no need to take this by value when it's possible to avoid
unnecessary copies entirely like this.
2018-07-18 23:19:28 -04:00
Lioncash
87a9bb392b content_archive: Add missing standard includes 2018-07-18 23:18:59 -04:00
bunnei
522bd5b736 externals: Update dynarmic to 5a91c94. 2018-07-18 23:14:44 -04:00
Lioncash
0b566f43a1 content_archive: std::move VirtualFile in NCA's constructor
Gets rid of unnecessary atomic reference count incrementing and
decrementing.
2018-07-18 23:13:25 -04:00
Lioncash
5e626c774f vfs: Deduplicate accumulation code in VfsDirectory's GetSize()
We can just use a generic lambda to avoid writing the same thing twice.
2018-07-18 23:03:27 -04:00
bunnei
1371e2fb6a Merge pull request #691 from lioncash/guard
service/prepo: Add missing header guard
2018-07-18 19:46:14 -07:00
bunnei
b10905c8ae Merge pull request #686 from lioncash/fmt
externals: update fmt to version 5.1.0
2018-07-18 19:44:44 -07:00
Lioncash
55ab369043 loader/nro: Resolve sign mismatch warnings 2018-07-18 22:27:22 -04:00
Lioncash
1831b5ef62 loader/nso: Remove unnecessary vector resizes
We can just initialize these vectors directly via their constructor.
2018-07-18 22:26:41 -04:00
Lioncash
e3a30ccc7c loader/nso: Resolve sign mismatch warnings 2018-07-18 22:26:37 -04:00
bunnei
90ce935f3d Merge pull request #688 from lioncash/comma
vm_manager: Add missing commas to string literal array elements in GetMemoryStateName()
2018-07-18 18:59:09 -07:00
bunnei
3f93279047 Merge pull request #693 from lioncash/unused
core/memory: Remove unused function GetSpecialHandlers() and an unused variable in ZeroBlock()
2018-07-18 18:57:13 -07:00
bunnei
49b0966003 Merge pull request #687 from lioncash/instance
core: Don't construct instance of Core::System, just to access its live instance
2018-07-18 18:55:58 -07:00
bunnei
89f0acfd36 Merge pull request #680 from bunnei/fix-swizz
decoders: Fix calc of swizzle image_width_in_gobs.
2018-07-18 18:55:37 -07:00
bunnei
d883d3b64f Merge pull request #685 from lioncash/build
hle/filesystem: Amend trace log in OpenSaveData() to compile in debug mode
2018-07-18 18:55:15 -07:00
bunnei
2975f7820e Merge pull request #684 from lioncash/nonmember
game_list: Make ContainsAllWords an internally linked non-member function
2018-07-18 18:55:00 -07:00
bunnei
b496a9eefe decoders: Fix calc of swizzle image_width_in_gobs. 2018-07-18 21:42:52 -04:00
Zach Hilman
29aff8d5ab Virtual Filesystem 2: Electric Boogaloo (#676)
* Virtual Filesystem

* Fix delete bug and documentate

* Review fixes + other stuff

* Fix puyo regression
2018-07-18 18:07:11 -07:00
Lioncash
72207577b2 core/memory: Remove unused function GetSpecialHandlers() and an unused variable in ZeroBlock() 2018-07-18 19:55:46 -04:00
Lioncash
2cd3141c30 address_arbiter: Correct assignment within an assertion statement in WakeThreads()
This was introduced within 4f81bc4e1b, and
considering there's no comment indicating that this is intentional, this
is very likely a bug.
2018-07-18 19:46:46 -04:00
Lioncash
296e68fd43 service/prepo: Add missing header guard 2018-07-18 19:43:28 -04:00
Lioncash
93cba6f699 vm_manager: Add missing commas to string literal array elements in GetMemoryStateName()
Without these, this would perform concatenation, which is definitely not
what we want here.
2018-07-18 19:37:19 -04:00
Lioncash
46458e7284 core/memory, core/hle/kernel: Use std::move where applicable
Avoids pointless copies
2018-07-18 19:34:31 -04:00
Lioncash
10d2ab8098 core: Make System's default constructor private
This makes it a compilation error to construct additional instances of
the System class directly, preventing accidental wasteful constructions
over and over.
2018-07-18 18:18:27 -04:00
Lioncash
3a4841e403 core: Don't construct instance of Core::System, just to access its live instance
This would result in a lot of allocations and related object
construction, just to toss it all away immediately after the call.

These are definitely not intentional, and it was intended that all of
these should have been accessing the static function GetInstance()
through the name itself, not constructed instances.
2018-07-18 18:18:27 -04:00
Lioncash
f5d7706ca1 externals: update fmt to version 5.1.0
Previously, we were on 4.1.0, which was a major version behind.
2018-07-18 17:46:17 -04:00
Lioncash
079be8032d hle/filesystem: Amend trace log in OpenSaveData() to compile in debug mode
Previously this wouldn't compile, since no such function named
SaveStructDebugInfo() exists.
2018-07-18 17:42:44 -04:00
bunnei
924c473bb3 Merge pull request #683 from DarkLordZach/touch
Touchscreen Support
2018-07-18 14:29:16 -07:00
Lioncash
d17e172d92 game_list: Make ContainsAllWords an internally linked non-member function
This function actually depends on no internal class state, so it doesn't
even need to be a part of the class interface.
2018-07-18 16:52:14 -04:00
Zach Hilman
c337272ca9 Fill in more fields in TouchScreenEntryTouch 2018-07-18 16:33:11 -04:00
Zach Hilman
f2f368014e Single touch support 2018-07-18 14:06:33 -04:00
bunnei
3d1e8f750c Merge pull request #681 from lioncash/const
game_list: Make containsAllWords a const member function
2018-07-18 10:02:58 -07:00
bunnei
e3da9fc367 Merge pull request #682 from lioncash/telemetry
Telemetry: Minor changes
2018-07-18 10:02:46 -07:00
bunnei
24a55bba42 Merge pull request #679 from lioncash/ctor
game_list: Remove unnecessary QString initialization in KeyReleaseEater
2018-07-17 22:18:39 -07:00
bunnei
b87a71b3fe Merge pull request #678 from lioncash/astc
astc: Minor changes
2018-07-17 22:06:20 -07:00
Lioncash
c65a8fafa0 telemetry: Remove unnecessary Field constructor
We can just take the value parameter by value which allows both moving
into it, and copies at the same time, depending on the calling code.
2018-07-18 00:32:35 -04:00
Lioncash
0aebe6b3d5 telemetry: Make operator== and operator!= const member functions of Field
These operators don't modify internal class state, so they can be made
const member functions. While we're at it, drop the unnecessary inline
keywords. Member functions that are defined in the class declaration are
already inline by default.
2018-07-18 00:28:47 -04:00
bunnei
7c3cc08957 Merge pull request #677 from bunnei/crop-fb
Implement buffer cropping and default to handheld mode
2018-07-17 21:26:35 -07:00
Lioncash
3575d367a4 telemetry: Default copy/move constructors and assignment operators
This provides the equivalent behavior, but without as much boilerplate.
While we're at it, explicitly default the move constructor, since we
have a move-assignment operator defined.
2018-07-18 00:25:12 -04:00
Lioncash
f4b98a857b game_list: Upper-case containsAllWords to ContainsAllWords()
This makes it consistent with most of the other private utility
functions.
2018-07-18 00:15:48 -04:00
Lioncash
c8f3fc9a4b game_list: Make containsAllWords a const member function
This doesn't actually modify the internal class state, so it can be a
const member function. While we're at it, amend the function to take
its arguments by const reference.
2018-07-18 00:13:04 -04:00
Lioncash
f4c69149f9 game_list: Remove unnecessary QString initialization in KeyReleaseEater
QString initializes to an empty string by default, so this does nothing
meaningful. While we're at it, use a constructor initializer list for
initializing the gamelist member variable.
2018-07-18 00:07:47 -04:00
Lioncash
6a03badcbc astc: Initialize vector size directly in Decompress
There's no need to perform a separate resize.
2018-07-17 23:58:14 -04:00
Lioncash
0f148548f3 astc: Mark functions as internally linked where applicable 2018-07-17 23:58:14 -04:00
Lioncash
c5803e30d3 astc: const-correctness changes where applicable
A few member functions didn't actually modify class state, so these can
be amended as necessary.
2018-07-17 23:58:14 -04:00
Lioncash
e3fadb9616 astc: Delete Bits' copy contstructor and assignment operator
This also potentially avoids warnings, considering the copy assignment
operator is supposed to have a return value.
2018-07-17 23:58:14 -04:00
Lioncash
4cd52a34b9 astc: In-class initialize member variables where appropriate 2018-07-17 23:58:10 -04:00
bunnei
03c2d049d4 settings: Turn docked mode off by default. 2018-07-17 22:52:25 -04:00
bunnei
49e5de9f03 vi: Change TransactionId::CancelBuffer to LOG_CRITICAL. 2018-07-17 22:47:35 -04:00
bunnei
0d1a99edf6 vi: Fix size for ListDisplays default display. 2018-07-17 22:18:14 -04:00
bunnei
c3dd456d51 vi: Partially implement buffer crop parameters. 2018-07-17 20:13:17 -04:00
bunnei
8e28af6f89 Merge pull request #675 from Subv/stencil
GPU: Added register definitions for the stencil parameters.
2018-07-17 15:53:00 -07:00
Subv
3d3b10adc7 GPU: Added register definitions for the stencil parameters. 2018-07-17 15:00:21 -05:00
Zach Hilman
69bfe075b5 General Filesystem and Save Data Fixes (#670) 2018-07-17 12:42:15 -07:00
bunnei
88a3140c9b Merge pull request #671 from MerryMage/clear-exclusive-state
scheduler: Clear exclusive state when switching contexts
2018-07-17 07:33:32 -07:00
bunnei
519035db3d Merge pull request #672 from SciresM/to_address_fix
svc:: Fix bug in svcWaitForAddress
2018-07-17 07:32:42 -07:00
bunnei
5f119bed56 Merge pull request #673 from bunnei/fix-buffer-queue-evt
nvflinger: Fix for BufferQueue event handling.
2018-07-17 07:07:57 -07:00
bunnei
170e19d4ea nvflinger: Fix for BufferQueue event handling. 2018-07-17 00:26:23 -04:00
Michael Scire
3b885691a1 Kernel/Arbiter: Fix bug in WaitIfLessThan 2018-07-16 20:55:53 -06:00
MerryMage
56cc1c11ec scheduler: Clear exclusive state when switching contexts 2018-07-16 11:24:00 +01:00
bunnei
f00ca69a81 Merge pull request #669 from lioncash/dynarmic
externals: Update dynarmic to dfdec79
2018-07-15 18:36:32 -07:00
Lioncash
0722adb471 externals: Update dynarmic to dfdec79 2018-07-15 16:11:45 -04:00
bunnei
068668780c Merge pull request #668 from jroweboy/controller-lag
HID: Update controllers less often
2018-07-15 13:06:28 -07:00
bunnei
04b9cde4f5 Merge pull request #664 from jroweboy/logging-stuff
Minor logging improvements
2018-07-15 12:58:52 -07:00
James Rowe
7d209b3c9f HID: Update controllers less often 2018-07-15 13:47:41 -06:00
James Rowe
497b81558e Logging: Dump all logs in the queue on close in debug mode 2018-07-15 13:02:09 -06:00
bunnei
98762e9601 Merge pull request #666 from bunnei/g8r8
gl_rasterizer_cache: Implement texture format G8R8.
2018-07-15 10:09:36 -07:00
bunnei
3a96670f2d gl_rasterizer_cache: Implement texture format G8R8. 2018-07-15 01:33:42 -04:00
bunnei
aaec0b7e70 Merge pull request #665 from bunnei/fix-z24-s8
gl_rasterizer_cache: Fix incorrect offset in ConvertS8Z24ToZ24S8.
2018-07-14 22:18:55 -07:00
bunnei
f8ab956189 Merge pull request #659 from bunnei/depth16
gl_rasterizer_cache: Implement depth format Z16_UNORM.
2018-07-14 21:39:23 -07:00
bunnei
3145114190 gl_rasterizer_cache: Fix incorrect offset in ConvertS8Z24ToZ24S8. 2018-07-15 00:02:05 -04:00
bunnei
e21190f47f gl_rasterizer_cache: Implement depth format Z16_UNORM. 2018-07-14 23:43:28 -04:00
bunnei
2cb3fdca86 Merge pull request #598 from bunnei/makedonecurrent
OpenGL: Use MakeCurrent/DoneCurrent for multithreaded rendering.
2018-07-14 20:18:11 -07:00
bunnei
c324a378ac Merge pull request #663 from Subv/bsd
Services/BSD: Corrected the return for StartMonitoring according to SwIPC
2018-07-14 19:40:34 -07:00
bunnei
fd1f5c5414 Merge pull request #662 from Subv/delete_file
FileSys: Append the requested path to the filesystem base path in DeleteFile
2018-07-14 13:11:58 -07:00
James Rowe
6daebaaa57 Logging: Don't lock the queue for the duration of the write 2018-07-14 11:57:13 -06:00
Subv
b07f4d6afb Services/BSD: Corrected the return for StartMonitoring according to SwIPC. 2018-07-14 12:34:07 -05:00
bunnei
ad0166a982 Merge pull request #661 from ogniK5377/assert-nit
No need to use ASSERT_MSG with an empty assert message
2018-07-14 09:12:21 -07:00
Subv
7e5e4f8d7a FileSys: Append the requested path to the filesystem base path in DeleteFile.
We were trying to delete things in the current directory instead of the actual filesystem directory. This may fix some savedata issues in some games.
2018-07-14 10:57:22 -05:00
David Marcec
a7d6c0d6ea No need to use ASSERT_MSG with an empty message 2018-07-14 23:13:16 +10:00
bunnei
81739a5448 Merge pull request #660 from Subv/depth_write
GPU: Always enable the depth write when clearing the depth buffer.
2018-07-14 00:38:12 -07:00
bunnei
05cb10530f OpenGL: Use MakeCurrent/DoneCurrent for multithreaded rendering. 2018-07-14 02:50:35 -04:00
Subv
b37354cca8 GPU: Always enable the depth write when clearing the depth buffer.
The GPU ignores that register when clearing, but OpenGL obeys the glDepthMask parameter, so we set the depth mask to GL_TRUE when clearing the depth buffer. It will be restored to the correct value automatically on the next draw call.
2018-07-14 00:52:23 -05:00
bunnei
9fc0d1d701 Merge pull request #657 from bunnei/dual-vs
gl_shader_gen: Implement dual vertex shader mode.
2018-07-13 07:08:54 -07:00
Hedges
e066bc75b9 More improvements to GDBStub (#653)
* More improvements to GDBStub
- Debugging of threads should work correctly with source and assembly level stepping and modifying registers and memory, meaning threads and callstacks are fully clickable in VS.
- List of modules is available to the client, with assumption that .nro and .nso are backed up by an .elf with symbols, while deconstructed ROMs keep N names.
- Initial support for floating point registers.

* Tidy up as requested in PR feedback

* Tidy up as requested in PR feedback
2018-07-12 20:22:59 -07:00
112 changed files with 2802 additions and 2578 deletions

2
externals/fmt vendored

View File

@@ -4,6 +4,8 @@
#pragma once
#include <string>
#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM)
#include <cstdlib> // for exit
#endif
@@ -36,40 +38,6 @@
#define Crash() exit(1)
#endif
// GCC 4.8 defines all the rotate functions now
// Small issue with GCC's lrotl/lrotr intrinsics is they are still 32bit while we require 64bit
#ifdef _rotl
#define rotl _rotl
#else
inline u32 rotl(u32 x, int shift) {
shift &= 31;
if (!shift)
return x;
return (x << shift) | (x >> (32 - shift));
}
#endif
#ifdef _rotr
#define rotr _rotr
#else
inline u32 rotr(u32 x, int shift) {
shift &= 31;
if (!shift)
return x;
return (x >> shift) | (x << (32 - shift));
}
#endif
inline u64 _rotl64(u64 x, unsigned int shift) {
unsigned int n = shift % 64;
return (x << n) | (x >> (64 - n));
}
inline u64 _rotr64(u64 x, unsigned int shift) {
unsigned int n = shift % 64;
return (x >> n) | (x << (64 - n));
}
#else // _MSC_VER
// Locale Cross-Compatibility
@@ -80,17 +48,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
}
#define Crash() DebugBreak()
// cstdlib provides these on MSVC
#define rotr _rotr
#define rotl _rotl
#endif // _MSC_VER ndef
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
// Defined in Misc.cpp.
const char* GetLastErrorMsg();
std::string GetLastErrorMsg();
namespace Common {

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sstream>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
@@ -386,7 +387,7 @@ u64 GetSize(FILE* f) {
bool CreateEmptyFile(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "{}", filename);
if (!FileUtil::IOFile(filename, "wb")) {
if (!FileUtil::IOFile(filename, "wb").IsOpen()) {
LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
return false;
}
@@ -591,7 +592,7 @@ std::string GetBundleDirectory() {
#endif
#ifdef _WIN32
std::string& GetExeDirectory() {
const std::string& GetExeDirectory() {
static std::string exe_path;
if (exe_path.empty()) {
wchar_t wchar_exe_path[2048];
@@ -750,7 +751,7 @@ size_t WriteStringToFile(bool text_file, const std::string& str, const char* fil
size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
IOFile file(filename, text_file ? "r" : "rb");
if (!file)
if (!file.IsOpen())
return false;
str.resize(static_cast<u32>(file.GetSize()));
@@ -799,6 +800,71 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
}
}
std::vector<std::string> SplitPathComponents(const std::string& filename) {
auto copy(filename);
std::replace(copy.begin(), copy.end(), '\\', '/');
std::vector<std::string> out;
std::stringstream stream(filename);
std::string item;
while (std::getline(stream, item, '/'))
out.push_back(std::move(item));
return out;
}
std::string GetParentPath(const std::string& path) {
auto out = path;
const auto name_bck_index = out.find_last_of('\\');
const auto name_fwd_index = out.find_last_of('/');
size_t name_index;
if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos)
name_index = std::min<size_t>(name_bck_index, name_fwd_index);
else
name_index = std::max<size_t>(name_bck_index, name_fwd_index);
return out.erase(name_index);
}
std::string GetPathWithoutTop(std::string path) {
if (path.empty())
return "";
while (path[0] == '\\' || path[0] == '/') {
path = path.substr(1);
if (path.empty())
return "";
}
const auto name_bck_index = path.find_first_of('\\');
const auto name_fwd_index = path.find_first_of('/');
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
}
std::string GetFilename(std::string path) {
std::replace(path.begin(), path.end(), '\\', '/');
auto name_index = path.find_last_of('/');
if (name_index == std::string::npos)
return "";
return path.substr(name_index + 1);
}
std::string GetExtensionFromFilename(const std::string& name) {
size_t index = name.find_last_of('.');
if (index == std::string::npos)
return "";
return name.substr(index + 1);
}
std::string RemoveTrailingSlash(const std::string& path) {
if (path.empty())
return path;
if (path.back() == '\\' || path.back() == '/')
return path.substr(0, path.size() - 1);
return path;
}
IOFile::IOFile() {}
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
@@ -820,7 +886,6 @@ IOFile& IOFile::operator=(IOFile&& other) noexcept {
void IOFile::Swap(IOFile& other) noexcept {
std::swap(m_file, other.m_file);
std::swap(m_good, other.m_good);
}
bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
@@ -837,16 +902,15 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
m_file = fopen(filename.c_str(), openmode);
#endif
m_good = IsOpen();
return m_good;
return IsOpen();
}
bool IOFile::Close() {
if (!IsOpen() || 0 != std::fclose(m_file))
m_good = false;
return false;
m_file = nullptr;
return m_good;
return true;
}
u64 IOFile::GetSize() const {
@@ -856,11 +920,8 @@ u64 IOFile::GetSize() const {
return 0;
}
bool IOFile::Seek(s64 off, int origin) {
if (!IsOpen() || 0 != fseeko(m_file, off, origin))
m_good = false;
return m_good;
bool IOFile::Seek(s64 off, int origin) const {
return IsOpen() && 0 == fseeko(m_file, off, origin);
}
u64 IOFile::Tell() const {
@@ -871,26 +932,20 @@ u64 IOFile::Tell() const {
}
bool IOFile::Flush() {
if (!IsOpen() || 0 != std::fflush(m_file))
m_good = false;
return m_good;
return IsOpen() && 0 == std::fflush(m_file);
}
bool IOFile::Resize(u64 size) {
if (!IsOpen() || 0 !=
return IsOpen() && 0 ==
#ifdef _WIN32
// ector: _chsize sucks, not 64-bit safe
// F|RES: changed to _chsize_s. i think it is 64-bit safe
_chsize_s(_fileno(m_file), size)
// ector: _chsize sucks, not 64-bit safe
// F|RES: changed to _chsize_s. i think it is 64-bit safe
_chsize_s(_fileno(m_file), size)
#else
// TODO: handle 64bit and growing
ftruncate(fileno(m_file), size)
// TODO: handle 64bit and growing
ftruncate(fileno(m_file), size)
#endif
)
m_good = false;
return m_good;
;
}
} // namespace FileUtil

View File

@@ -133,7 +133,7 @@ std::string GetBundleDirectory();
#endif
#ifdef _WIN32
std::string& GetExeDirectory();
const std::string& GetExeDirectory();
std::string AppDataRoamingDirectory();
#endif
@@ -150,6 +150,34 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension);
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
std::vector<std::string> SplitPathComponents(const std::string& filename);
// Gets all of the text up to the last '/' or '\' in the path.
std::string GetParentPath(const std::string& path);
// Gets all of the text after the first '/' or '\' in the path.
std::string GetPathWithoutTop(std::string path);
// Gets the filename of the path
std::string GetFilename(std::string path);
// Gets the extension of the filename
std::string GetExtensionFromFilename(const std::string& name);
// Removes the final '/' or '\' if one exists
std::string RemoveTrailingSlash(const std::string& path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) {
if (first >= last)
return {};
last = std::min<size_t>(last, vector.size());
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
// and make forgetting an fclose() harder
@@ -172,41 +200,27 @@ public:
bool Close();
template <typename T>
size_t ReadArray(T* data, size_t length) {
size_t ReadArray(T* data, size_t length) const {
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
m_good = false;
if (!IsOpen())
return -1;
}
size_t items_read = std::fread(data, sizeof(T), length, m_file);
if (items_read != length)
m_good = false;
return items_read;
return std::fread(data, sizeof(T), length, m_file);
}
template <typename T>
size_t WriteArray(const T* data, size_t length) {
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
m_good = false;
if (!IsOpen())
return -1;
}
size_t items_written = std::fwrite(data, sizeof(T), length, m_file);
if (items_written != length)
m_good = false;
return items_written;
return std::fwrite(data, sizeof(T), length, m_file);
}
template <typename T>
size_t ReadBytes(T* data, size_t length) {
size_t ReadBytes(T* data, size_t length) const {
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
return ReadArray(reinterpret_cast<char*>(data), length);
}
@@ -231,15 +245,7 @@ public:
return nullptr != m_file;
}
// m_good is set to false when a read, write or other function fails
bool IsGood() const {
return m_good;
}
explicit operator bool() const {
return IsGood();
}
bool Seek(s64 off, int origin);
bool Seek(s64 off, int origin) const;
u64 Tell() const;
u64 GetSize() const;
bool Resize(u64 size);
@@ -247,13 +253,11 @@ public:
// clear error state
void Clear() {
m_good = true;
std::clearerr(m_file);
}
private:
std::FILE* m_file = nullptr;
bool m_good = true;
};
} // namespace FileUtil

View File

@@ -5,6 +5,7 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <climits>
#include <condition_variable>
#include <memory>
#include <thread>
@@ -83,8 +84,10 @@ private:
}
};
while (true) {
std::unique_lock<std::mutex> lock(message_mutex);
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
{
std::unique_lock<std::mutex> lock(message_mutex);
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
}
if (!running) {
break;
}
@@ -92,7 +95,7 @@ private:
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
// where a system is repeatedly spamming logs even on close.
constexpr int MAX_LOGS_TO_WRITE = 100;
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
int logs_written = 0;
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
write_logs(entry);
@@ -282,4 +285,4 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
Impl::Instance().PushEntry(std::move(entry));
}
} // namespace Log
} // namespace Log

View File

@@ -94,4 +94,11 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
bool Filter::CheckMessage(Class log_class, Level level) const {
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
}
bool Filter::IsDebug() const {
return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) {
return static_cast<u8>(l) <= static_cast<u8>(Level::Debug);
});
}
} // namespace Log

View File

@@ -47,6 +47,9 @@ public:
/// Matches class/level combination against the filter, returning true if it passed.
bool CheckMessage(Class log_class, Level level) const;
/// Returns true if any logging classes are set to debug
bool IsDebug() const;
private:
std::array<Level, (size_t)Class::Count> class_levels;
};

View File

@@ -103,7 +103,7 @@ template <typename... Args>
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const Args&... args) {
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format,
fmt::make_args(args...));
fmt::make_format_args(args...));
}
} // namespace Log

View File

@@ -4,34 +4,28 @@
#include <cstddef>
#ifdef _WIN32
#include <windows.h>
#include <Windows.h>
#else
#include <cerrno>
#include <cstring>
#endif
// Neither Android nor OS X support TLS
#if defined(__APPLE__) || (ANDROID && __clang__)
#define __thread
#endif
#include "common/common_funcs.h"
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
const char* GetLastErrorMsg() {
std::string GetLastErrorMsg() {
static const size_t buff_size = 255;
char err_str[buff_size];
#ifdef _WIN32
static __declspec(thread) char err_str[buff_size] = {};
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
#else
static __thread char err_str[buff_size] = {};
// Thread safe (XSI-compliant)
strerror_r(errno, err_str, buff_size);
#endif
return err_str;
return std::string(err_str, buff_size);
}

View File

@@ -34,18 +34,6 @@ std::string ToUpper(std::string str) {
return str;
}
// faster than sscanf
bool AsciiToHex(const char* _szValue, u32& result) {
char* endptr = nullptr;
const u32 value = strtoul(_szValue, &endptr, 16);
if (!endptr || *endptr)
return false;
result = value;
return true;
}
// For Debugging. Read out an u8 array.
std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) {
std::ostringstream oss;
@@ -134,7 +122,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
size_t dir_end = full_path.find_last_of("/"
// windows needs the : included for something like just "C:" to be considered a directory
#ifdef _WIN32
":"
"\\:"
#endif
);
if (std::string::npos == dir_end)

View File

@@ -57,9 +57,6 @@ static bool TryParse(const std::string& str, N* const output) {
return false;
}
// TODO: kill this
bool AsciiToHex(const char* _szValue, u32& result);
std::string TabsToSpaces(int tab_size, const std::string& in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);

View File

@@ -167,7 +167,7 @@ inline double swapd(double f) {
template <typename T, typename F>
struct swap_struct_t {
typedef swap_struct_t<T, F> swapped_t;
using swapped_t = swap_struct_t;
protected:
T value = T();
@@ -177,7 +177,7 @@ protected:
}
public:
T const swap() const {
T swap() const {
return swap(value);
}
swap_struct_t() = default;
@@ -185,39 +185,39 @@ public:
template <typename S>
swapped_t& operator=(const S& source) {
value = swap((T)source);
value = swap(static_cast<T>(source));
return *this;
}
operator s8() const {
return (s8)swap();
return static_cast<s8>(swap());
}
operator u8() const {
return (u8)swap();
return static_cast<u8>(swap());
}
operator s16() const {
return (s16)swap();
return static_cast<s16>(swap());
}
operator u16() const {
return (u16)swap();
return static_cast<u16>(swap());
}
operator s32() const {
return (s32)swap();
return static_cast<s32>(swap());
}
operator u32() const {
return (u32)swap();
return static_cast<u32>(swap());
}
operator s64() const {
return (s64)swap();
return static_cast<s64>(swap());
}
operator u64() const {
return (u64)swap();
return static_cast<u64>(swap());
}
operator float() const {
return (float)swap();
return static_cast<float>(swap());
}
operator double() const {
return (double)swap();
return static_cast<double>(swap());
}
// +v
@@ -253,7 +253,7 @@ public:
}
template <typename S>
swapped_t operator+(const S& i) const {
return swap() + (T)i;
return swap() + static_cast<T>(i);
}
// v - 5
swapped_t operator-(const swapped_t& i) const {
@@ -261,7 +261,7 @@ public:
}
template <typename S>
swapped_t operator-(const S& i) const {
return swap() - (T)i;
return swap() - static_cast<T>(i);
}
// v += 5
@@ -271,7 +271,7 @@ public:
}
template <typename S>
swapped_t& operator+=(const S& i) {
value = swap(swap() + (T)i);
value = swap(swap() + static_cast<T>(i));
return *this;
}
// v -= 5
@@ -281,7 +281,7 @@ public:
}
template <typename S>
swapped_t& operator-=(const S& i) {
value = swap(swap() - (T)i);
value = swap(swap() - static_cast<T>(i));
return *this;
}
@@ -541,7 +541,7 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
template <typename S, typename T, typename F>
S operator&(const swap_struct_t<T, F> v, const S& i) {
return (S)(v.swap() & i);
return static_cast<S>(v.swap() & i);
}
// Comparaison
@@ -606,51 +606,51 @@ struct swap_double_t {
};
#if COMMON_LITTLE_ENDIAN
typedef u32 u32_le;
typedef u16 u16_le;
typedef u64 u64_le;
using u16_le = u16;
using u32_le = u32;
using u64_le = u64;
typedef s32 s32_le;
typedef s16 s16_le;
typedef s64 s64_le;
using s16_le = s16;
using s32_le = s32;
using s64_le = s64;
typedef float float_le;
typedef double double_le;
using float_le = float;
using double_le = double;
typedef swap_struct_t<u64, swap_64_t<u64>> u64_be;
typedef swap_struct_t<s64, swap_64_t<s64>> s64_be;
using u64_be = swap_struct_t<u64, swap_64_t<u64>>;
using s64_be = swap_struct_t<s64, swap_64_t<s64>>;
typedef swap_struct_t<u32, swap_32_t<u32>> u32_be;
typedef swap_struct_t<s32, swap_32_t<s32>> s32_be;
using u32_be = swap_struct_t<u32, swap_32_t<u32>>;
using s32_be = swap_struct_t<s32, swap_32_t<s32>>;
typedef swap_struct_t<u16, swap_16_t<u16>> u16_be;
typedef swap_struct_t<s16, swap_16_t<s16>> s16_be;
using u16_be = swap_struct_t<u16, swap_16_t<u16>>;
using s16_be = swap_struct_t<s16, swap_16_t<s16>>;
typedef swap_struct_t<float, swap_float_t<float>> float_be;
typedef swap_struct_t<double, swap_double_t<double>> double_be;
using float_be = swap_struct_t<float, swap_float_t<float>>;
using double_be = swap_struct_t<double, swap_double_t<double>>;
#else
typedef swap_struct_t<u64, swap_64_t<u64>> u64_le;
typedef swap_struct_t<s64, swap_64_t<s64>> s64_le;
using u64_le = swap_struct_t<u64, swap_64_t<u64>>;
using s64_le = swap_struct_t<s64, swap_64_t<s64>>;
typedef swap_struct_t<u32, swap_32_t<u32>> u32_le;
typedef swap_struct_t<s32, swap_32_t<s32>> s32_le;
using u32_le = swap_struct_t<u32, swap_32_t<u32>>;
using s32_le = swap_struct_t<s32, swap_32_t<s32>>;
typedef swap_struct_t<u16, swap_16_t<u16>> u16_le;
typedef swap_struct_t<s16, swap_16_t<s16>> s16_le;
using u16_le = swap_struct_t<u16, swap_16_t<u16>>;
using s16_le = swap_struct_t<s16, swap_16_t<s16>>;
typedef swap_struct_t<float, swap_float_t<float>> float_le;
typedef swap_struct_t<double, swap_double_t<double>> double_le;
using float_le = swap_struct_t<float, swap_float_t<float>>;
using double_le = swap_struct_t<double, swap_double_t<double>>;
typedef u32 u32_be;
typedef u16 u16_be;
typedef u64 u64_be;
using u16_be = u16;
using u32_be = u32;
using u64_be = u64;
typedef s32 s32_be;
typedef s16 s16_be;
typedef s64 s64_be;
using s16_be = s16;
using s32_be = s32;
using s64_be = s64;
typedef float float_be;
typedef double double_be;
using float_be = float;
using double_be = double;
#endif

View File

@@ -52,27 +52,14 @@ public:
template <typename T>
class Field : public FieldInterface {
public:
Field(FieldType type, std::string name, const T& value)
: name(std::move(name)), type(type), value(value) {}
Field(FieldType type, std::string name, T&& value)
Field(FieldType type, std::string name, T value)
: name(std::move(name)), type(type), value(std::move(value)) {}
Field(const Field& other) : Field(other.type, other.name, other.value) {}
Field(const Field&) = default;
Field& operator=(const Field&) = default;
Field& operator=(const Field& other) {
type = other.type;
name = other.name;
value = other.value;
return *this;
}
Field& operator=(Field&& other) {
type = other.type;
name = std::move(other.name);
value = std::move(other.value);
return *this;
}
Field(Field&&) = default;
Field& operator=(Field&& other) = default;
void Accept(VisitorInterface& visitor) const override;
@@ -94,11 +81,11 @@ public:
return value;
}
inline bool operator==(const Field<T>& other) {
bool operator==(const Field& other) const {
return (type == other.type) && (name == other.name) && (value == other.value);
}
inline bool operator!=(const Field<T>& other) {
bool operator!=(const Field& other) const {
return !(*this == other);
}

View File

@@ -8,27 +8,27 @@ add_library(core STATIC
core_cpu.h
core_timing.cpp
core_timing.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/directory.h
file_sys/disk_filesystem.cpp
file_sys/disk_filesystem.h
file_sys/errors.h
file_sys/filesystem.cpp
file_sys/filesystem.h
file_sys/mode.h
file_sys/partition_filesystem.cpp
file_sys/partition_filesystem.h
file_sys/path_parser.cpp
file_sys/path_parser.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/romfs_factory.cpp
file_sys/romfs_factory.h
file_sys/romfs_filesystem.cpp
file_sys/romfs_filesystem.h
file_sys/savedata_factory.cpp
file_sys/savedata_factory.h
file_sys/sdmc_factory.cpp
file_sys/sdmc_factory.h
file_sys/storage.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_offset.cpp
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
file_sys/vfs_real.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp

View File

@@ -116,6 +116,8 @@ public:
*/
virtual void LoadContext(const ThreadContext& ctx) = 0;
virtual void ClearExclusiveState() = 0;
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
};

View File

@@ -226,6 +226,10 @@ void ARM_Dynarmic::ClearInstructionCache() {
jit->ClearCache();
}
void ARM_Dynarmic::ClearExclusiveState() {
jit->ClearExclusiveState();
}
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit(cb);
current_page_table = Memory::GetCurrentPageTable();

View File

@@ -39,6 +39,7 @@ public:
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
void PageTableChanged() override;

View File

@@ -193,11 +193,11 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
if (last_bkpt_hit) {
if (last_bkpt_hit || (num_instructions == 1)) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
}
GDBStub::SendTrap(thread, 5);
}
}
@@ -263,6 +263,8 @@ void ARM_Unicorn::PrepareReschedule() {
CHECKED(uc_emu_stop(uc));
}
void ARM_Unicorn::ClearExclusiveState() {}
void ARM_Unicorn::ClearInstructionCache() {}
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {

View File

@@ -31,6 +31,7 @@ public:
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ExecuteInstructions(int num_instructions);
void Run() override;
void Step() override;

View File

@@ -19,17 +19,20 @@
#include "core/loader/loader.h"
#include "core/memory_setup.h"
#include "core/settings.h"
#include "file_sys/vfs_real.h"
#include "video_core/video_core.h"
namespace Core {
/*static*/ System System::s_instance;
System::System() = default;
System::~System() = default;
/// Runs a CPU core while the system is powered on
static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System().GetInstance().IsPoweredOn()) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
}
@@ -84,7 +87,7 @@ System::ResultStatus System::SingleStep() {
}
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(filepath);
app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);

View File

@@ -168,6 +168,8 @@ public:
}
private:
System();
/// Returns the currently running CPU core
Cpu& CurrentCpuCore();

View File

@@ -0,0 +1,170 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
namespace FileSys {
// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
constexpr u64 SECTION_HEADER_SIZE = 0x200;
constexpr u64 SECTION_HEADER_OFFSET = 0x400;
constexpr u32 IVFC_MAX_LEVEL = 6;
enum class NCASectionFilesystemType : u8 {
PFS0 = 0x2,
ROMFS = 0x3,
};
struct NCASectionHeaderBlock {
INSERT_PADDING_BYTES(3);
NCASectionFilesystemType filesystem_type;
u8 crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
struct PFS0Superblock {
NCASectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
u32_le size;
INSERT_PADDING_BYTES(4);
u64_le hash_table_offset;
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_PADDING_BYTES(432);
};
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
struct IVFCLevel {
u64_le offset;
u64_le size;
u32_le block_size;
u32_le reserved;
};
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
struct RomFSSuperblock {
NCASectionHeaderBlock header_block;
u32_le magic;
u32_le magic_number;
INSERT_PADDING_BYTES(8);
std::array<IVFCLevel, 6> levels;
INSERT_PADDING_BYTES(64);
};
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (!IsValidNCA(header)) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
std::ptrdiff_t number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
// Seek to beginning of this section.
NCASectionHeaderBlock block{};
if (sizeof(NCASectionHeaderBlock) !=
file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
RomFSSuperblock sb{};
if (sizeof(RomFSSuperblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
sb.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size;
files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
romfs = files.back();
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
PFS0Superblock sb{};
// Seek back to beginning of this section.
if (sizeof(PFS0Superblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
sb.pfs0_header_offset;
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
auto npfs = std::make_shared<PartitionFilesystem>(
std::make_shared<OffsetVfsFile>(file, size, offset));
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.emplace_back(npfs);
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
}
}
}
status = Loader::ResultStatus::Success;
}
Loader::ResultStatus NCA::GetStatus() const {
return status;
}
std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const {
if (status != Loader::ResultStatus::Success)
return {};
return files;
}
std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const {
if (status != Loader::ResultStatus::Success)
return {};
return dirs;
}
std::string NCA::GetName() const {
return file->GetName();
}
std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const {
return file->GetContainingDirectory();
}
NCAContentType NCA::GetType() const {
return header.content_type;
}
u64 NCA::GetTitleId() const {
if (status != Loader::ResultStatus::Success)
return {};
return header.title_id;
}
VirtualFile NCA::GetRomFS() const {
return romfs;
}
VirtualDir NCA::GetExeFS() const {
return exefs;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -0,0 +1,100 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <string>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/partition_filesystem.h"
namespace FileSys {
enum class NCAContentType : u8 {
Program = 0,
Meta = 1,
Control = 2,
Manual = 3,
Data = 4,
};
struct NCASectionTableEntry {
u32_le media_offset;
u32_le media_end_offset;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size.");
struct NCAHeader {
std::array<u8, 0x100> rsa_signature_1;
std::array<u8, 0x100> rsa_signature_2;
u32_le magic;
u8 is_system;
NCAContentType content_type;
u8 crypto_type;
u8 key_index;
u64_le size;
u64_le title_id;
INSERT_PADDING_BYTES(0x4);
u32_le sdk_version;
u8 crypto_type_2;
INSERT_PADDING_BYTES(15);
std::array<u8, 0x10> rights_id;
std::array<NCASectionTableEntry, 0x4> section_tables;
std::array<std::array<u8, 0x20>, 0x4> hash_tables;
std::array<std::array<u8, 0x10>, 0x4> key_area;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
// According to switchbrew, an exefs must only contain these two files:
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
inline bool IsValidNCA(const NCAHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file);
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
NCAContentType GetType() const;
u64 GetTitleId() const;
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualDir> dirs;
std::vector<VirtualFile> files;
VirtualFile romfs = nullptr;
VirtualDir exefs = nullptr;
VirtualFile file;
NCAHeader header{};
Loader::ResultStatus status{};
};
} // namespace FileSys

View File

@@ -8,13 +8,17 @@
#include <cstddef>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
enum EntryType : u8 {
Directory = 0,
File = 1,
};
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
const size_t FILENAME_LENGTH = 0x300;

View File

@@ -1,237 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/disk_filesystem.h"
#include "core/file_sys/errors.h"
namespace FileSys {
static std::string ModeFlagsToString(Mode mode) {
std::string mode_str;
u32 mode_flags = static_cast<u32>(mode);
// Calculate the correct open mode for the file.
if ((mode_flags & static_cast<u32>(Mode::Read)) &&
(mode_flags & static_cast<u32>(Mode::Write))) {
if (mode_flags & static_cast<u32>(Mode::Append))
mode_str = "a+";
else
mode_str = "r+";
} else {
if (mode_flags & static_cast<u32>(Mode::Read))
mode_str = "r";
else if (mode_flags & static_cast<u32>(Mode::Append))
mode_str = "a";
else if (mode_flags & static_cast<u32>(Mode::Write))
mode_str = "w";
}
mode_str += "b";
return mode_str;
}
std::string Disk_FileSystem::GetName() const {
return "Disk";
}
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
Mode mode) const {
// Calculate the correct open mode for the file.
std::string mode_str = ModeFlagsToString(mode);
std::string full_path = base_directory + path;
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str());
if (!file->IsOpen()) {
return ERROR_PATH_NOT_FOUND;
}
return MakeResult<std::unique_ptr<StorageBackend>>(
std::make_unique<Disk_Storage>(std::move(file)));
}
ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const {
if (!FileUtil::Exists(path)) {
return ERROR_PATH_NOT_FOUND;
}
FileUtil::Delete(path);
return RESULT_SUCCESS;
}
ResultCode Disk_FileSystem::RenameFile(const std::string& src_path,
const std::string& dest_path) const {
const std::string full_src_path = base_directory + src_path;
const std::string full_dest_path = base_directory + dest_path;
if (!FileUtil::Exists(full_src_path)) {
return ERROR_PATH_NOT_FOUND;
}
// TODO(wwylele): Use correct error code
return FileUtil::Rename(full_src_path, full_dest_path) ? RESULT_SUCCESS : ResultCode(-1);
}
ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
std::string full_path = base_directory + path;
if (size == 0) {
FileUtil::CreateEmptyFile(full_path);
return RESULT_SUCCESS;
}
FileUtil::IOFile file(full_path, "wb");
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
// We do this by seeking to the right size, then writing a single null byte.
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
return RESULT_SUCCESS;
}
LOG_ERROR(Service_FS, "Too large file");
// TODO(Subv): Find out the correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const {
// TODO(Subv): Perform path validation to prevent escaping the emulator sandbox.
std::string full_path = base_directory + path;
if (FileUtil::CreateDir(full_path)) {
return RESULT_SUCCESS;
}
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path);
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
const std::string& path) const {
std::string full_path = base_directory + path;
if (!FileUtil::IsDirectory(full_path)) {
// TODO(Subv): Find the correct error code for this.
return ResultCode(-1);
}
auto directory = std::make_unique<Disk_Directory>(full_path);
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
}
u64 Disk_FileSystem::GetFreeSpaceSize() const {
LOG_WARNING(Service_FS, "(STUBBED) called");
return 0;
}
ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const {
std::string full_path = base_directory + path;
if (!FileUtil::Exists(full_path)) {
return ERROR_PATH_NOT_FOUND;
}
if (FileUtil::IsDirectory(full_path))
return MakeResult(EntryType::Directory);
return MakeResult(EntryType::File);
}
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length);
file->Seek(offset, SEEK_SET);
return MakeResult<size_t>(file->ReadBytes(buffer, length));
}
ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush,
const u8* buffer) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
file->Seek(offset, SEEK_SET);
size_t written = file->WriteBytes(buffer, length);
if (flush) {
file->Flush();
}
return MakeResult<size_t>(written);
}
u64 Disk_Storage::GetSize() const {
return file->GetSize();
}
bool Disk_Storage::SetSize(const u64 size) const {
file->Resize(size);
file->Flush();
return true;
}
Disk_Directory::Disk_Directory(const std::string& path) {
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
directory.size = size;
directory.isDirectory = true;
children_iterator = directory.children.begin();
}
u64 Disk_Directory::Read(const u64 count, Entry* entries) {
u64 entries_read = 0;
while (entries_read < count && children_iterator != directory.children.cend()) {
const FileUtil::FSTEntry& file = *children_iterator;
const std::string& filename = file.virtualName;
Entry& entry = entries[entries_read];
LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory);
// TODO(Link Mauve): use a proper conversion to UTF-16.
for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
entry.filename[j] = filename[j];
if (!filename[j])
break;
}
if (file.isDirectory) {
entry.file_size = 0;
entry.type = EntryType::Directory;
} else {
entry.file_size = file.size;
entry.type = EntryType::File;
}
++entries_read;
++children_iterator;
}
return entries_read;
}
u64 Disk_Directory::GetEntryCount() const {
// We convert the children iterator into a const_iterator to allow template argument deduction
// in std::distance.
std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator;
return std::distance(current, directory.children.end());
}
} // namespace FileSys

View File

@@ -1,84 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <string>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/result.h"
namespace FileSys {
class Disk_FileSystem : public FileSystemBackend {
public:
explicit Disk_FileSystem(std::string base_directory)
: base_directory(std::move(base_directory)) {}
std::string GetName() const override;
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const override;
ResultCode DeleteFile(const std::string& path) const override;
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const std::string& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
const std::string& path) const override;
u64 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
protected:
std::string base_directory;
};
class Disk_Storage : public StorageBackend {
public:
explicit Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {}
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() const override {
return false;
}
void Flush() const override {}
private:
std::shared_ptr<FileUtil::IOFile> file;
};
class Disk_Directory : public DirectoryBackend {
public:
explicit Disk_Directory(const std::string& path);
~Disk_Directory() override {
Close();
}
u64 Read(const u64 count, Entry* entries) override;
u64 GetEntryCount() const override;
bool Close() const override {
return true;
}
protected:
FileUtil::FSTEntry directory;
// We need to remember the last entry we returned, so a subsequent call to Read will continue
// from the next one. This iterator will always point to the next unread entry.
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
};
} // namespace FileSys

View File

@@ -11,7 +11,9 @@ namespace FileSys {
namespace ErrCodes {
enum {
NotFound = 1,
SaveDataNotFound = 1002,
TitleNotFound = 1002,
SdCardNotFound = 2001,
RomFSNotFound = 2520,
};
}

View File

@@ -1,122 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstddef>
#include <iomanip>
#include <sstream>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/filesystem.h"
#include "core/memory.h"
namespace FileSys {
Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) {
switch (type) {
case Binary: {
binary.resize(size);
Memory::ReadBlock(pointer, binary.data(), binary.size());
break;
}
case Char: {
string.resize(size - 1); // Data is always null-terminated.
Memory::ReadBlock(pointer, &string[0], string.size());
break;
}
case Wchar: {
u16str.resize(size / 2 - 1); // Data is always null-terminated.
Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t));
break;
}
default:
break;
}
}
std::string Path::DebugStr() const {
switch (GetType()) {
case Invalid:
default:
return "[Invalid]";
case Empty:
return "[Empty]";
case Binary: {
std::stringstream res;
res << "[Binary: ";
for (unsigned byte : binary)
res << std::hex << std::setw(2) << std::setfill('0') << byte;
res << ']';
return res.str();
}
case Char:
return "[Char: " + AsString() + ']';
case Wchar:
return "[Wchar: " + AsString() + ']';
}
}
std::string Path::AsString() const {
switch (GetType()) {
case Char:
return string;
case Wchar:
return Common::UTF16ToUTF8(u16str);
case Empty:
return {};
case Invalid:
case Binary:
default:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
return {};
}
}
std::u16string Path::AsU16Str() const {
switch (GetType()) {
case Char:
return Common::UTF8ToUTF16(string);
case Wchar:
return u16str;
case Empty:
return {};
case Invalid:
case Binary:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
return {};
}
UNREACHABLE();
}
std::vector<u8> Path::AsBinary() const {
switch (GetType()) {
case Binary:
return binary;
case Char:
return std::vector<u8>(string.begin(), string.end());
case Wchar: {
// use two u8 for each character of u16str
std::vector<u8> to_return(u16str.size() * 2);
for (size_t i = 0; i < u16str.size(); ++i) {
u16 tmp_char = u16str.at(i);
to_return[i * 2] = (tmp_char & 0xFF00) >> 8;
to_return[i * 2 + 1] = (tmp_char & 0x00FF);
}
return to_return;
}
case Empty:
return {};
case Invalid:
default:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!");
return {};
}
}
} // namespace FileSys

View File

@@ -1,201 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
namespace FileSys {
class StorageBackend;
class DirectoryBackend;
// Path string type
enum LowPathType : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
Char = 3,
Wchar = 4,
};
enum EntryType : u8 {
Directory = 0,
File = 1,
};
enum class Mode : u32 {
Read = 1,
Write = 2,
Append = 4,
};
class Path {
public:
Path() : type(Invalid) {}
Path(const char* path) : type(Char), string(path) {}
Path(std::vector<u8> binary_data) : type(Binary), binary(std::move(binary_data)) {}
Path(LowPathType type, u32 size, u32 pointer);
LowPathType GetType() const {
return type;
}
/**
* Gets the string representation of the path for debugging
* @return String representation of the path for debugging
*/
std::string DebugStr() const;
std::string AsString() const;
std::u16string AsU16Str() const;
std::vector<u8> AsBinary() const;
private:
LowPathType type;
std::vector<u8> binary;
std::string string;
std::u16string u16str;
};
/// Parameters of the archive, as specified in the Create or Format call.
struct ArchiveFormatInfo {
u32_le total_size; ///< The pre-defined size of the archive.
u32_le number_directories; ///< The pre-defined number of directories in the archive.
u32_le number_files; ///< The pre-defined number of files in the archive.
u8 duplicate_data; ///< Whether the archive should duplicate the data.
};
static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
class FileSystemBackend : NonCopyable {
public:
virtual ~FileSystemBackend() {}
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
*/
virtual std::string GetName() const = 0;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
virtual ResultCode DeleteFile(const std::string& path) const = 0;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
virtual ResultCode CreateDirectory(const std::string& path) const = 0;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
virtual ResultCode DeleteDirectory(const Path& path) const = 0;
/**
* Delete a directory specified by its path and anything under it
* @param path Path relative to the archive
* @return Result of the operation
*/
virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
/**
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
virtual ResultCode RenameFile(const std::string& src_path,
const std::string& dest_path) const = 0;
/**
* Rename a Directory specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or error code
*/
virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const = 0;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or error code
*/
virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
const std::string& path) const = 0;
/**
* Get the free space
* @return The number of free bytes in the archive
*/
virtual u64 GetFreeSpaceSize() const = 0;
/**
* Get the type of the specified path
* @return The type of the specified path or error code
*/
virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0;
};
class FileSystemFactory : NonCopyable {
public:
virtual ~FileSystemFactory() {}
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
*/
virtual std::string GetName() const = 0;
/**
* Tries to open the archive of this type with the specified path
* @param path Path to the archive
* @return An ArchiveBackend corresponding operating specified archive path.
*/
virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0;
/**
* Deletes the archive contents and then re-creates the base folder
* @param path Path to the archive
* @return ResultCode of the operation, 0 on success
*/
virtual ResultCode Format(const Path& path) = 0;
/**
* Retrieves the format info about the archive with the specified path
* @param path Path to the archive
* @return Format information about the archive or error code
*/
virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0;
};
} // namespace FileSys

17
src/core/file_sys/mode.h Normal file
View File

@@ -0,0 +1,17 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace FileSys {
enum class Mode : u32 {
Read = 1,
Write = 2,
Append = 4,
};
} // namespace FileSys

View File

@@ -6,131 +6,108 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
namespace FileSys {
Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) {
FileUtil::IOFile file(file_path, "rb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;
bool PartitionFilesystem::Header::HasValidMagicValue() const {
return magic == Common::MakeMagic('H', 'F', 'S', '0') ||
magic == Common::MakeMagic('P', 'F', 'S', '0');
}
PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
// At least be as large as the header
if (file.GetSize() < sizeof(Header))
return Loader::ResultStatus::Error;
file.Seek(offset, SEEK_SET);
// For cartridges, HFSs can get very large, so we need to calculate the size up to
// the actual content itself instead of just blindly reading in the entire file.
Header pfs_header;
if (!file.ReadBytes(&pfs_header, sizeof(Header)))
return Loader::ResultStatus::Error;
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
return Loader::ResultStatus::ErrorInvalidFormat;
if (file->GetSize() < sizeof(Header)) {
status = Loader::ResultStatus::Error;
return;
}
bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
// For cartridges, HFSs can get very large, so we need to calculate the size up to
// the actual content itself instead of just blindly reading in the entire file.
if (sizeof(Header) != file->ReadObject(&pfs_header)) {
status = Loader::ResultStatus::Error;
return;
}
if (!pfs_header.HasValidMagicValue()) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
size_t metadata_size =
sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size;
// Actually read in now...
file.Seek(offset, SEEK_SET);
std::vector<u8> file_data(metadata_size);
std::vector<u8> file_data = file->ReadBytes(metadata_size);
const size_t total_size = file_data.size();
if (!file.ReadBytes(file_data.data(), metadata_size))
return Loader::ResultStatus::Error;
Loader::ResultStatus result = Load(file_data);
if (result != Loader::ResultStatus::Success)
LOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path);
return result;
}
Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) {
size_t total_size = file_data.size() - offset;
if (total_size < sizeof(Header))
return Loader::ResultStatus::Error;
memcpy(&pfs_header, &file_data[offset], sizeof(Header));
if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') &&
pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) {
return Loader::ResultStatus::ErrorInvalidFormat;
if (total_size != metadata_size) {
status = Loader::ResultStatus::Error;
return;
}
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
size_t entries_offset = offset + sizeof(Header);
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
size_t entries_offset = sizeof(Header);
size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
for (u16 i = 0; i < pfs_header.num_entries; i++) {
FileEntry entry;
memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry));
entry.name = std::string(reinterpret_cast<const char*>(
&file_data[strtab_offset + entry.fs_entry.strtab_offset]));
pfs_entries.push_back(std::move(entry));
}
content_offset = strtab_offset + pfs_header.strtab_size;
for (u16 i = 0; i < pfs_header.num_entries; i++) {
FSEntry entry;
return Loader::ResultStatus::Success;
}
memcpy(&entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry));
std::string name(
reinterpret_cast<const char*>(&file_data[strtab_offset + entry.strtab_offset]));
u32 PartitionFilesystem::GetNumEntries() const {
return pfs_header.num_entries;
}
u64 PartitionFilesystem::GetEntryOffset(u32 index) const {
if (index > GetNumEntries())
return 0;
return content_offset + pfs_entries[index].fs_entry.offset;
}
u64 PartitionFilesystem::GetEntrySize(u32 index) const {
if (index > GetNumEntries())
return 0;
return pfs_entries[index].fs_entry.size;
}
std::string PartitionFilesystem::GetEntryName(u32 index) const {
if (index > GetNumEntries())
return "";
return pfs_entries[index].name;
}
u64 PartitionFilesystem::GetFileOffset(const std::string& name) const {
for (u32 i = 0; i < pfs_header.num_entries; i++) {
if (pfs_entries[i].name == name)
return content_offset + pfs_entries[i].fs_entry.offset;
pfs_files.emplace_back(
std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name));
}
return 0;
status = Loader::ResultStatus::Success;
}
u64 PartitionFilesystem::GetFileSize(const std::string& name) const {
for (u32 i = 0; i < pfs_header.num_entries; i++) {
if (pfs_entries[i].name == name)
return pfs_entries[i].fs_entry.size;
}
return 0;
Loader::ResultStatus PartitionFilesystem::GetStatus() const {
return status;
}
void PartitionFilesystem::Print() const {
LOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic);
std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
return pfs_files;
}
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
return {};
}
std::string PartitionFilesystem::GetName() const {
return is_hfs ? "HFS0" : "PFS0";
}
std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const {
// TODO(DarkLordZach): Add support for nested containers.
return nullptr;
}
void PartitionFilesystem::PrintDebugInfo() const {
LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic);
LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries);
for (u32 i = 0; i < pfs_header.num_entries; i++) {
LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i,
pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size,
GetFileOffset(pfs_entries[i].name));
pfs_files[i]->GetName(), pfs_files[i]->GetSize(),
dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset());
}
}
bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
if (iter == pfs_files.end())
return false;
pfs_files[iter - pfs_files.begin()] = pfs_files.back();
pfs_files.pop_back();
pfs_dirs.emplace_back(dir);
return true;
}
} // namespace FileSys

View File

@@ -10,6 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus;
@@ -21,19 +22,19 @@ namespace FileSys {
* Helper which implements an interface to parse PFS/HFS filesystems.
* Data can either be loaded from a file path or data with an offset into it.
*/
class PartitionFilesystem {
class PartitionFilesystem : public ReadOnlyVfsDirectory {
public:
Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0);
Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0);
explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
Loader::ResultStatus GetStatus() const;
u32 GetNumEntries() const;
u64 GetEntryOffset(u32 index) const;
u64 GetEntrySize(u32 index) const;
std::string GetEntryName(u32 index) const;
u64 GetFileOffset(const std::string& name) const;
u64 GetFileSize(const std::string& name) const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
void PrintDebugInfo() const;
void Print() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
struct Header {
@@ -41,6 +42,8 @@ private:
u32_le num_entries;
u32_le strtab_size;
INSERT_PADDING_BYTES(0x4);
bool HasValidMagicValue() const;
};
static_assert(sizeof(Header) == 0x10, "PFS/HFS header structure size is wrong");
@@ -72,16 +75,14 @@ private:
#pragma pack(pop)
struct FileEntry {
FSEntry fs_entry;
std::string name;
};
Loader::ResultStatus status{};
Header pfs_header;
bool is_hfs;
size_t content_offset;
Header pfs_header{};
bool is_hfs = false;
size_t content_offset = 0;
std::vector<FileEntry> pfs_entries;
std::vector<VirtualFile> pfs_files;
std::vector<VirtualDir> pfs_dirs;
};
} // namespace FileSys

View File

@@ -1,98 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <set>
#include "common/file_util.h"
#include "common/string_util.h"
#include "core/file_sys/path_parser.h"
namespace FileSys {
PathParser::PathParser(const Path& path) {
if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) {
is_valid = false;
return;
}
auto path_string = path.AsString();
if (path_string.size() == 0 || path_string[0] != '/') {
is_valid = false;
return;
}
// Filter out invalid characters for the host system.
// Although some of these characters are valid on 3DS, they are unlikely to be used by games.
if (std::find_if(path_string.begin(), path_string.end(), [](char c) {
static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'};
return invalid_chars.find(c) != invalid_chars.end();
}) != path_string.end()) {
is_valid = false;
return;
}
Common::SplitString(path_string, '/', path_sequence);
auto begin = path_sequence.begin();
auto end = path_sequence.end();
end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; });
path_sequence = std::vector<std::string>(begin, end);
// checks if the path is out of bounds.
int level = 0;
for (auto& node : path_sequence) {
if (node == "..") {
--level;
if (level < 0) {
is_valid = false;
return;
}
} else {
++level;
}
}
is_valid = true;
is_root = level == 0;
}
PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const {
auto path = mount_point;
if (!FileUtil::IsDirectory(path))
return InvalidMountPoint;
if (path_sequence.empty()) {
return DirectoryFound;
}
for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) {
if (path.back() != '/')
path += '/';
path += *iter;
if (!FileUtil::Exists(path))
return PathNotFound;
if (FileUtil::IsDirectory(path))
continue;
return FileInPath;
}
path += "/" + path_sequence.back();
if (!FileUtil::Exists(path))
return NotFound;
if (FileUtil::IsDirectory(path))
return DirectoryFound;
return FileFound;
}
std::string PathParser::BuildHostPath(const std::string& mount_point) const {
std::string path = mount_point;
for (auto& node : path_sequence) {
if (path.back() != '/')
path += '/';
path += node;
}
return path;
}
} // namespace FileSys

View File

@@ -1,61 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <vector>
#include "core/file_sys/filesystem.h"
namespace FileSys {
/**
* A helper class parsing and verifying a string-type Path.
* Every archives with a sub file system should use this class to parse the path argument and check
* the status of the file / directory in question on the host file system.
*/
class PathParser {
public:
explicit PathParser(const Path& path);
/**
* Checks if the Path is valid.
* This function should be called once a PathParser is constructed.
* A Path is valid if:
* - it is a string path (with type LowPathType::Char or LowPathType::Wchar),
* - it starts with "/" (this seems a hard requirement in real 3DS),
* - it doesn't contain invalid characters, and
* - it doesn't go out of the root directory using "..".
*/
bool IsValid() const {
return is_valid;
}
/// Checks if the Path represents the root directory.
bool IsRootDirectory() const {
return is_root;
}
enum HostStatus {
InvalidMountPoint,
PathNotFound, // "/a/b/c" when "a" doesn't exist
FileInPath, // "/a/b/c" when "a" is a file
FileFound, // "/a/b/c" when "c" is a file
DirectoryFound, // "/a/b/c" when "c" is a directory
NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist
};
/// Checks the status of the specified file / directory by the Path on the host file system.
HostStatus GetHostStatus(const std::string& mount_point) const;
/// Builds a full path on the host file system.
std::string BuildHostPath(const std::string& mount_point) const;
private:
std::vector<std::string> path_sequence;
bool is_valid{};
bool is_root{};
};
} // namespace FileSys

View File

@@ -9,40 +9,29 @@
namespace FileSys {
Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
FileUtil::IOFile file(file_path, "rb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;
std::vector<u8> file_data(file.GetSize());
if (!file.ReadBytes(file_data.data(), file_data.size()))
return Loader::ResultStatus::Error;
Loader::ResultStatus result = Load(file_data);
if (result != Loader::ResultStatus::Success)
LOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path);
return result;
}
Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) {
size_t total_size = static_cast<size_t>(file_data.size() - offset);
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
size_t total_size = static_cast<size_t>(file->GetSize());
if (total_size < sizeof(Header))
return Loader::ResultStatus::Error;
size_t header_offset = offset;
memcpy(&npdm_header, &file_data[offset], sizeof(Header));
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
if (sizeof(Header) != npdm_header_data.size())
return Loader::ResultStatus::Error;
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
size_t aci_offset = header_offset + npdm_header.aci_offset;
size_t acid_offset = header_offset + npdm_header.acid_offset;
memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader));
memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader));
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
if (sizeof(AcidHeader) != acid_header_data.size())
return Loader::ResultStatus::Error;
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
size_t fac_offset = acid_offset + acid_header.fac_offset;
size_t fah_offset = aci_offset + aci_header.fah_offset;
memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl));
memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader));
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
return Loader::ResultStatus::Error;
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
return Loader::ResultStatus::Error;
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
return Loader::ResultStatus::Error;
return Loader::ResultStatus::Success;
}

View File

@@ -10,6 +10,7 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "partition_filesystem.h"
namespace Loader {
enum class ResultStatus;
@@ -37,8 +38,7 @@ enum class ProgramFilePermission : u64 {
*/
class ProgramMetadata {
public:
Loader::ResultStatus Load(const std::string& file_path);
Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0);
Loader::ResultStatus Load(VirtualFile file);
bool Is64BitProgram() const;
ProgramAddressSpaceType GetAddressSpaceType() const;
@@ -51,6 +51,7 @@ public:
void Print() const;
private:
// TODO(DarkLordZach): BitField is not trivially copyable.
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
@@ -77,6 +78,7 @@ private:
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
// TODO(DarkLordZach): BitField is not trivially copyable.
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;

View File

@@ -7,32 +7,19 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/romfs_filesystem.h"
namespace FileSys {
RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(file)) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) {
auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode RomFS_Factory::Format(const Path& path) {
LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id) {
// TODO(DarkLordZach): Use title id.
return MakeResult<VirtualFile>(file);
}
} // namespace FileSys

View File

@@ -5,31 +5,21 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/result.h"
#include "core/loader/loader.h"
namespace FileSys {
/// File system interface to the RomFS archive
class RomFS_Factory final : public FileSystemFactory {
class RomFSFactory {
public:
explicit RomFS_Factory(Loader::AppLoader& app_loader);
explicit RomFSFactory(Loader::AppLoader& app_loader);
std::string GetName() const override {
return "ArchiveFactory_RomFS";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
ResultVal<VirtualFile> Open(u64 title_id);
private:
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 data_offset;
u64 data_size;
VirtualFile file;
};
} // namespace FileSys

View File

@@ -1,110 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/romfs_filesystem.h"
namespace FileSys {
std::string RomFS_FileSystem::GetName() const {
return "RomFS";
}
ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path,
Mode mode) const {
return MakeResult<std::unique_ptr<StorageBackend>>(
std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size));
}
ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path,
const std::string& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).",
GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const {
LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName());
// TODO(bunnei): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).",
GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName());
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
const std::string& path) const {
LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive");
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
}
u64 RomFS_FileSystem::GetFreeSpaceSize() const {
LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive");
return 0;
}
ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const {
LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path);
// TODO(wwylele): Use correct error code
return ResultCode(-1);
}
ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length);
romfs_file->Seek(data_offset + offset, SEEK_SET);
size_t read_length = (size_t)std::min((u64)length, data_size - offset);
return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
}
ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush,
const u8* buffer) const {
LOG_ERROR(Service_FS, "Attempted to write to ROMFS file");
// TODO(Subv): Find error code
return MakeResult<size_t>(0);
}
u64 RomFS_Storage::GetSize() const {
return data_size;
}
bool RomFS_Storage::SetSize(const u64 size) const {
LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file");
return false;
}
} // namespace FileSys

View File

@@ -1,85 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/result.h"
namespace FileSys {
/**
* Helper which implements an interface to deal with Switch .istorage ROMFS images used in some
* archives This should be subclassed by concrete archive types, which will provide the input data
* (load the raw ROMFS archive) and override any required methods
*/
class RomFS_FileSystem : public FileSystemBackend {
public:
RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
std::string GetName() const override;
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
Mode mode) const override;
ResultCode DeleteFile(const std::string& path) const override;
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const std::string& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(
const std::string& path) const override;
u64 GetFreeSpaceSize() const override;
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
protected:
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 data_offset;
u64 data_size;
};
class RomFS_Storage : public StorageBackend {
public:
RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
bool SetSize(u64 size) const override;
bool Close() const override {
return false;
}
void Flush() const override {}
private:
std::shared_ptr<FileUtil::IOFile> romfs_file;
u64 data_offset;
u64 data_size;
};
class ROMFSDirectory : public DirectoryBackend {
public:
u64 Read(const u64 count, Entry* entries) override {
return 0;
}
u64 GetEntryCount() const override {
return 0;
}
bool Close() const override {
return false;
}
};
} // namespace FileSys

View File

@@ -6,58 +6,101 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/disk_filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/kernel/process.h"
namespace FileSys {
SaveData_Factory::SaveData_Factory(std::string nand_directory)
: nand_directory(std::move(nand_directory)) {}
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
}
ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
std::string save_directory = GetFullPath();
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
if (!FileUtil::Exists(save_directory)) {
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).",
meta.zero_1);
}
if (meta.zero_2 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).",
meta.zero_2);
}
if (meta.zero_3 != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is "
"SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).",
meta.zero_3);
}
}
if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is "
"non-zero ({:016X}).",
meta.title_id);
}
std::string save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
// TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
// But, user_ids don't match so this works for now.
auto out = dir->GetDirectoryRelative(save_directory);
if (out == nullptr) {
// TODO(bunnei): This is a work-around to always create a save data directory if it does not
// already exist. This is a hack, as we do not understand yet how this works on hardware.
// Without a save data directory, many games will assert on boot. This should not have any
// bad side-effects.
FileUtil::CreateFullPath(save_directory);
out = dir->CreateDirectoryRelative(save_directory);
}
// Return an error if the save data doesn't actually exist.
if (!FileUtil::IsDirectory(save_directory)) {
if (out == nullptr) {
// TODO(Subv): Find out correct error code.
return ResultCode(-1);
}
auto archive = std::make_unique<Disk_FileSystem>(save_directory);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
return MakeResult<VirtualDir>(std::move(out));
}
ResultCode SaveData_Factory::Format(const Path& path) {
LOG_WARNING(Service_FS, "Format archive {}", GetName());
// Create the save data directory.
if (!FileUtil::CreateFullPath(GetFullPath())) {
// TODO(Subv): Find the correct error code.
return ResultCode(-1);
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) const {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0)
title_id = Core::CurrentProcess()->program_id;
std::string out;
switch (space) {
case SaveDataSpaceId::NandSystem:
out = "/system/save/";
break;
case SaveDataSpaceId::NandUser:
out = "/user/save/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
}
return RESULT_SUCCESS;
}
ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
}
std::string SaveData_Factory::GetFullPath() const {
u64 title_id = Core::CurrentProcess()->program_id;
// TODO(Subv): Somehow obtain this value.
u32 user = 0;
return fmt::format("{}save/{:016X}/{:08X}/", nand_directory, title_id, user);
switch (type) {
case SaveDataType::SystemSaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
}
}
} // namespace FileSys

View File

@@ -7,27 +7,52 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/result.h"
namespace FileSys {
/// File system interface to the SaveData archive
class SaveData_Factory final : public FileSystemFactory {
public:
explicit SaveData_Factory(std::string nand_directory);
enum class SaveDataSpaceId : u8 {
NandSystem = 0,
NandUser = 1,
SdCard = 2,
TemporaryStorage = 3,
};
std::string GetName() const override {
return "SaveData_Factory";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
enum class SaveDataType : u8 {
SystemSaveData = 0,
SaveData = 1,
BcatDeliveryCacheStorage = 2,
DeviceSaveData = 3,
TemporaryStorage = 4,
CacheStorage = 5,
};
struct SaveDataDescriptor {
u64_le title_id;
u128 user_id;
u64_le save_id;
SaveDataType type;
INSERT_PADDING_BYTES(7);
u64_le zero_1;
u64_le zero_2;
u64_le zero_3;
std::string DebugInfo() const;
};
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
/// File system interface to the SaveData archive
class SaveDataFactory {
public:
explicit SaveDataFactory(VirtualDir dir);
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
private:
std::string nand_directory;
VirtualDir dir;
std::string GetFullPath() const;
std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id,
u64 save_id) const;
};
} // namespace FileSys

View File

@@ -3,37 +3,15 @@
// Refer to the license.txt file included.
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/disk_filesystem.h"
#include "core/file_sys/sdmc_factory.h"
namespace FileSys {
SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {}
SDMCFactory::SDMCFactory(VirtualDir dir) : dir(std::move(dir)) {}
ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) {
// Create the SD Card directory if it doesn't already exist.
if (!FileUtil::IsDirectory(sd_directory)) {
FileUtil::CreateFullPath(sd_directory);
}
auto archive = std::make_unique<Disk_FileSystem>(sd_directory);
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
ResultCode SDMC_Factory::Format(const Path& path) {
LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName());
// TODO(Subv): Find the right error code for this
return ResultCode(-1);
}
ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const {
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
ResultVal<VirtualDir> SDMCFactory::Open() {
return MakeResult<VirtualDir>(dir);
}
} // namespace FileSys

View File

@@ -4,28 +4,19 @@
#pragma once
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/filesystem.h"
#include "core/hle/result.h"
namespace FileSys {
/// File system interface to the SDCard archive
class SDMC_Factory final : public FileSystemFactory {
class SDMCFactory {
public:
explicit SDMC_Factory(std::string sd_directory);
explicit SDMCFactory(VirtualDir dir);
std::string GetName() const override {
return "SDMC_Factory";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
ResultVal<VirtualDir> Open();
private:
std::string sd_directory;
VirtualDir dir;
};
} // namespace FileSys

View File

@@ -1,63 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include "common/common_types.h"
#include "core/hle/result.h"
namespace FileSys {
class StorageBackend : NonCopyable {
public:
StorageBackend() {}
virtual ~StorageBackend() {}
/**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
* @param buffer Buffer to read data into
* @return Number of bytes read, or error code
*/
virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0;
/**
* Write data to the file
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to file
* @param flush The flush parameters (0 == do not flush)
* @param buffer Buffer to read data from
* @return Number of bytes written, or error code
*/
virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
const u8* buffer) const = 0;
/**
* Flushes the file
*/
virtual void Flush() const = 0;
/**
* Set the size of the file in bytes
* @param size New size of the file
* @return true if successful
*/
virtual bool SetSize(u64 size) const = 0;
/**
* Get the size of the file in bytes
* @return Size of the file in bytes
*/
virtual u64 GetSize() const = 0;
/**
* Close the file
* @return true if the file closed correctly
*/
virtual bool Close() const = 0;
};
} // namespace FileSys

238
src/core/file_sys/vfs.cpp Normal file
View File

@@ -0,0 +1,238 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <numeric>
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
VfsFile::~VfsFile() = default;
std::string VfsFile::GetExtension() const {
return FileUtil::GetExtensionFromFilename(GetName());
}
VfsDirectory::~VfsDirectory() = default;
boost::optional<u8> VfsFile::ReadByte(size_t offset) const {
u8 out{};
size_t size = Read(&out, 1, offset);
if (size == 1)
return out;
return boost::none;
}
std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const {
std::vector<u8> out(size);
size_t read_size = Read(out.data(), size, offset);
out.resize(read_size);
return out;
}
std::vector<u8> VfsFile::ReadAllBytes() const {
return ReadBytes(GetSize());
}
bool VfsFile::WriteByte(u8 data, size_t offset) {
return Write(&data, 1, offset) == 1;
}
size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) {
return Write(data.data(), data.size(), offset);
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
return nullptr;
if (vec.size() == 1)
return GetFile(vec[0]);
auto dir = GetSubdirectory(vec[0]);
for (size_t component = 1; component < vec.size() - 1; ++component) {
if (dir == nullptr)
return nullptr;
dir = dir->GetSubdirectory(vec[component]);
}
if (dir == nullptr)
return nullptr;
return dir->GetFile(vec.back());
}
std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const {
if (IsRoot())
return GetFileRelative(path);
return GetParentDirectory()->GetFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
// because of const-ness
return nullptr;
auto dir = GetSubdirectory(vec[0]);
for (size_t component = 1; component < vec.size(); ++component) {
if (dir == nullptr)
return nullptr;
dir = dir->GetSubdirectory(vec[component]);
}
return dir;
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const {
if (IsRoot())
return GetDirectoryRelative(path);
return GetParentDirectory()->GetDirectoryAbsolute(path);
}
std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const {
const auto& files = GetFiles();
const auto iter = std::find_if(files.begin(), files.end(),
[&name](const auto& file1) { return name == file1->GetName(); });
return iter == files.end() ? nullptr : *iter;
}
std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const {
const auto& subs = GetSubdirectories();
const auto iter = std::find_if(subs.begin(), subs.end(),
[&name](const auto& file1) { return name == file1->GetName(); });
return iter == subs.end() ? nullptr : *iter;
}
bool VfsDirectory::IsRoot() const {
return GetParentDirectory() == nullptr;
}
size_t VfsDirectory::GetSize() const {
const auto& files = GetFiles();
const auto sum_sizes = [](const auto& range) {
return std::accumulate(range.begin(), range.end(), 0ULL,
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
};
const auto file_total = sum_sizes(files);
const auto& sub_dir = GetSubdirectories();
const auto subdir_total = sum_sizes(sub_dir);
return file_total + subdir_total;
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& path) {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
return nullptr;
if (vec.size() == 1)
return CreateFile(vec[0]);
auto dir = GetSubdirectory(vec[0]);
if (dir == nullptr) {
dir = CreateSubdirectory(vec[0]);
if (dir == nullptr)
return nullptr;
}
return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path));
}
std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) {
if (IsRoot())
return CreateFileRelative(path);
return GetParentDirectory()->CreateFileAbsolute(path);
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& path) {
auto vec = FileUtil::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty())
return nullptr;
if (vec.size() == 1)
return CreateSubdirectory(vec[0]);
auto dir = GetSubdirectory(vec[0]);
if (dir == nullptr) {
dir = CreateSubdirectory(vec[0]);
if (dir == nullptr)
return nullptr;
}
return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path));
}
std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) {
if (IsRoot())
return CreateDirectoryRelative(path);
return GetParentDirectory()->CreateDirectoryAbsolute(path);
}
bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) {
auto dir = GetSubdirectory(name);
if (dir == nullptr)
return false;
bool success = true;
for (const auto& file : dir->GetFiles()) {
if (!DeleteFile(file->GetName()))
success = false;
}
for (const auto& sdir : dir->GetSubdirectories()) {
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName()))
success = false;
}
return success;
}
bool VfsDirectory::Copy(const std::string& src, const std::string& dest) {
const auto f1 = GetFile(src);
auto f2 = CreateFile(dest);
if (f1 == nullptr || f2 == nullptr)
return false;
if (!f2->Resize(f1->GetSize())) {
DeleteFile(dest);
return false;
}
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
}
bool ReadOnlyVfsDirectory::IsWritable() const {
return false;
}
bool ReadOnlyVfsDirectory::IsReadable() const {
return true;
}
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) {
return nullptr;
}
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) {
return nullptr;
}
bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) {
return false;
}
bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) {
return false;
}
bool ReadOnlyVfsDirectory::Rename(const std::string& name) {
return false;
}
} // namespace FileSys

237
src/core/file_sys/vfs.h Normal file
View File

@@ -0,0 +1,237 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/file_util.h"
namespace FileSys {
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>;
// A class representing a file in an abstract filesystem.
struct VfsFile : NonCopyable {
virtual ~VfsFile();
// Retrieves the file name.
virtual std::string GetName() const = 0;
// Retrieves the extension of the file name.
virtual std::string GetExtension() const;
// Retrieves the size of the file.
virtual size_t GetSize() const = 0;
// Resizes the file to new_size. Returns whether or not the operation was successful.
virtual bool Resize(size_t new_size) = 0;
// Gets a pointer to the directory containing this file, returning nullptr if there is none.
virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0;
// Returns whether or not the file can be written to.
virtual bool IsWritable() const = 0;
// Returns whether or not the file can be read from.
virtual bool IsReadable() const = 0;
// The primary method of reading from the file. Reads length bytes into data starting at offset
// into file. Returns number of bytes successfully read.
virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0;
// The primary method of writing to the file. Writes length bytes from data starting at offset
// into file. Returns number of bytes successfully written.
virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0;
// Reads exactly one byte at the offset provided, returning boost::none on error.
virtual boost::optional<u8> ReadByte(size_t offset = 0) const;
// Reads size bytes starting at offset in file into a vector.
virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const;
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
// 0)'
virtual std::vector<u8> ReadAllBytes() const;
// Reads an array of type T, size number_elements starting at offset.
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
template <typename T>
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
}
// Reads size bytes into the memory starting at data starting at offset into the file.
// Returns the number of bytes read successfully.
template <typename T>
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), size, offset);
}
// Reads one object of type T starting at offset in file.
// Returns the number of bytes read successfully (sizeof(T)).
template <typename T>
size_t ReadObject(T* data, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
}
// Writes exactly one byte to offset in file and retuns whether or not the byte was written
// successfully.
virtual bool WriteByte(u8 data, size_t offset = 0);
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
// written.
virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0);
// Writes an array of type T, size number_elements to offset in file.
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
template <typename T>
size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Write(data, number_elements * sizeof(T), offset);
}
// Writes size bytes starting at memory location data to offset in file.
// Returns the number of bytes written successfully.
template <typename T>
size_t WriteBytes(T* data, size_t size, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Write(reinterpret_cast<u8*>(data), size, offset);
}
// Writes one object of type T to offset in file.
// Returns the number of bytes written successfully (sizeof(T)).
template <typename T>
size_t WriteObject(const T& data, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Write(&data, sizeof(T), offset);
}
// Renames the file to name. Returns whether or not the operation was successsful.
virtual bool Rename(const std::string& name) = 0;
};
// A class representing a directory in an abstract filesystem.
struct VfsDirectory : NonCopyable {
virtual ~VfsDirectory();
// Retrives the file located at path as if the current directory was root. Returns nullptr if
// not found.
virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const;
// Calls GetFileRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const;
// Retrives the directory located at path as if the current directory was root. Returns nullptr
// if not found.
virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const;
// Calls GetDirectoryRelative(path) on the root of the current directory.
virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const;
// Returns a vector containing all of the files in this directory.
virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0;
// Returns the file with filename matching name. Returns nullptr if directory dosen't have a
// file with name.
virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const;
// Returns a vector containing all of the subdirectories in this directory.
virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0;
// Returns the directory with name matching name. Returns nullptr if directory dosen't have a
// directory with name.
virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const;
// Returns whether or not the directory can be written to.
virtual bool IsWritable() const = 0;
// Returns whether of not the directory can be read from.
virtual bool IsReadable() const = 0;
// Returns whether or not the directory is the root of the current file tree.
virtual bool IsRoot() const;
// Returns the name of the directory.
virtual std::string GetName() const = 0;
// Returns the total size of all files and subdirectories in this directory.
virtual size_t GetSize() const;
// Returns the parent directory of this directory. Returns nullptr if this directory is root or
// has no parent.
virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0;
// Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
// if the operation failed.
virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0;
// Creates a new file with name name. Returns a pointer to the new file or nullptr if the
// operation failed.
virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0;
// Creates a new file at the path relative to this directory. Also creates directories if
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& path);
// Creates a new file at the path relative to root of this directory. Also creates directories
// if they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& path);
// Creates a new directory at the path relative to this directory. Also creates directories if
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& path);
// Creates a new directory at the path relative to root of this directory. Also creates
// directories if they do not exist and is supported by this implementation. Returns nullptr on
// any failure.
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path);
// Deletes the subdirectory with name and returns true on success.
virtual bool DeleteSubdirectory(const std::string& name) = 0;
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
// the subdirectory. Returns true on success.
virtual bool DeleteSubdirectoryRecursive(const std::string& name);
// Returnes whether or not the file with name name was deleted successfully.
virtual bool DeleteFile(const std::string& name) = 0;
// Returns whether or not this directory was renamed to name.
virtual bool Rename(const std::string& name) = 0;
// Returns whether or not the file with name src was successfully copied to a new file with name
// dest.
virtual bool Copy(const std::string& src, const std::string& dest);
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
// subdirectory in one call.
template <typename Directory>
bool InterpretAsDirectory(const std::string& file) {
auto file_p = GetFile(file);
if (file_p == nullptr)
return false;
return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p));
}
protected:
// Backend for InterpretAsDirectory.
// Removes all references to file and adds a reference to dir in the directory's implementation.
virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
};
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
// if writable. This is to avoid redundant empty methods everywhere.
struct ReadOnlyVfsDirectory : public VfsDirectory {
bool IsWritable() const override;
bool IsReadable() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
bool DeleteSubdirectory(const std::string& name) override;
bool DeleteFile(const std::string& name) override;
bool Rename(const std::string& name) override;
};
} // namespace FileSys

View File

@@ -0,0 +1,92 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/file_sys/vfs_offset.h"
namespace FileSys {
OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
const std::string& name_)
: file(file_), offset(offset_), size(size_), name(name_) {}
std::string OffsetVfsFile::GetName() const {
return name.empty() ? file->GetName() : name;
}
size_t OffsetVfsFile::GetSize() const {
return size;
}
bool OffsetVfsFile::Resize(size_t new_size) {
if (offset + new_size < file->GetSize()) {
size = new_size;
} else {
auto res = file->Resize(offset + new_size);
if (!res)
return false;
size = new_size;
}
return true;
}
std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const {
return file->GetContainingDirectory();
}
bool OffsetVfsFile::IsWritable() const {
return file->IsWritable();
}
bool OffsetVfsFile::IsReadable() const {
return file->IsReadable();
}
size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const {
return file->Read(data, TrimToFit(length, r_offset), offset + r_offset);
}
size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) {
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
}
boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const {
if (r_offset < size)
return file->ReadByte(offset + r_offset);
return boost::none;
}
std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const {
return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset);
}
std::vector<u8> OffsetVfsFile::ReadAllBytes() const {
return file->ReadBytes(size, offset);
}
bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
if (r_offset < size)
return file->WriteByte(data, offset + r_offset);
return false;
}
size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) {
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
}
bool OffsetVfsFile::Rename(const std::string& name) {
return file->Rename(name);
}
size_t OffsetVfsFile::GetOffset() const {
return offset;
}
size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0);
}
} // namespace FileSys

View File

@@ -0,0 +1,46 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/file_sys/vfs.h"
namespace FileSys {
// An implementation of VfsFile that wraps around another VfsFile at a certain offset.
// Similar to seeking to an offset.
// If the file is writable, operations that would write past the end of the offset file will expand
// the size of this wrapper.
struct OffsetVfsFile : public VfsFile {
OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
const std::string& new_name = "");
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Read(u8* data, size_t length, size_t offset) const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
boost::optional<u8> ReadByte(size_t offset) const override;
std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
std::vector<u8> ReadAllBytes() const override;
bool WriteByte(u8 data, size_t offset) override;
size_t WriteBytes(std::vector<u8> data, size_t offset) override;
bool Rename(const std::string& name) override;
size_t GetOffset() const;
private:
size_t TrimToFit(size_t r_size, size_t r_offset) const;
std::shared_ptr<VfsFile> file;
size_t offset;
size_t size;
std::string name;
};
} // namespace FileSys

View File

@@ -0,0 +1,177 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
namespace FileSys {
static std::string PermissionsToCharArray(Mode perms) {
std::string out;
switch (perms) {
case Mode::Read:
out += "r";
break;
case Mode::Write:
out += "r+";
break;
case Mode::Append:
out += "a";
break;
}
return out + "b";
}
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
: backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_),
parent_path(FileUtil::GetParentPath(path_)),
path_components(FileUtil::SplitPathComponents(path_)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {}
std::string RealVfsFile::GetName() const {
return path_components.back();
}
size_t RealVfsFile::GetSize() const {
return backing.GetSize();
}
bool RealVfsFile::Resize(size_t new_size) {
return backing.Resize(new_size);
}
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
bool RealVfsFile::IsWritable() const {
return perms == Mode::Append || perms == Mode::Write;
}
bool RealVfsFile::IsReadable() const {
return perms == Mode::Read || perms == Mode::Write;
}
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
if (!backing.Seek(offset, SEEK_SET))
return 0;
return backing.ReadBytes(data, length);
}
size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
if (!backing.Seek(offset, SEEK_SET))
return 0;
return backing.WriteBytes(data, length);
}
bool RealVfsFile::Rename(const std::string& name) {
const auto out = FileUtil::Rename(GetName(), name);
path = parent_path + DIR_SEP + name;
path_components = parent_components;
path_components.push_back(name);
backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str());
return out;
}
bool RealVfsFile::Close() {
return backing.Close();
}
RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
: 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::Write || perms == Mode::Append))
FileUtil::CreateDir(path);
unsigned size;
if (perms == Mode::Append)
return;
FileUtil::ForeachDirectoryEntry(
&size, path,
[this](unsigned* 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::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
return std::vector<std::shared_ptr<VfsFile>>(files);
}
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories);
}
bool RealVfsDirectory::IsWritable() const {
return perms == Mode::Write || perms == Mode::Append;
}
bool RealVfsDirectory::IsReadable() const {
return perms == Mode::Read || perms == Mode::Write;
}
std::string RealVfsDirectory::GetName() const {
return path_components.back();
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
if (path_components.size() <= 1)
return nullptr;
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) {
if (!FileUtil::CreateDir(path + DIR_SEP + name))
return nullptr;
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms));
return subdirectories.back();
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) {
if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name))
return nullptr;
files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms));
return files.back();
}
bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) {
return FileUtil::DeleteDirRecursively(path + DIR_SEP + name);
}
bool RealVfsDirectory::DeleteFile(const std::string& name) {
auto file = GetFile(name);
if (file == nullptr)
return false;
files.erase(std::find(files.begin(), files.end(), file));
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
real_file->Close();
return FileUtil::Delete(path + DIR_SEP + name);
}
bool RealVfsDirectory::Rename(const std::string& name) {
return FileUtil::Rename(path, parent_path + DIR_SEP + name);
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
auto iter = std::find(files.begin(), files.end(), file);
if (iter == files.end())
return false;
files[iter - files.begin()] = files.back();
files.pop_back();
subdirectories.emplace_back(dir);
return true;
}
} // namespace FileSys

View File

@@ -0,0 +1,69 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
// An implmentation of VfsFile that represents a file on the user's computer.
struct RealVfsFile : public VfsFile {
friend struct RealVfsDirectory;
RealVfsFile(const std::string& name, Mode perms = Mode::Read);
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Read(u8* data, size_t length, size_t offset) const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(const std::string& name) override;
private:
bool Close();
FileUtil::IOFile backing;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
std::vector<std::string> parent_components;
Mode perms;
};
// An implementation of VfsDirectory that represents a directory on the user's computer.
struct RealVfsDirectory : public VfsDirectory {
RealVfsDirectory(const std::string& path, Mode perms);
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
bool IsReadable() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override;
std::shared_ptr<VfsFile> CreateFile(const std::string& name) override;
bool DeleteSubdirectory(const std::string& name) override;
bool DeleteFile(const std::string& name) override;
bool Rename(const std::string& name) override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
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

@@ -61,10 +61,16 @@ const u32 SIGTERM = 15;
const u32 MSG_WAITALL = 8;
#endif
const u32 X30_REGISTER = 30;
const u32 LR_REGISTER = 30;
const u32 SP_REGISTER = 31;
const u32 PC_REGISTER = 32;
const u32 CPSR_REGISTER = 33;
const u32 UC_ARM64_REG_Q0 = 34;
const u32 FPSCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
const u32 TODO_DUMMY_REG_997 = 997;
const u32 TODO_DUMMY_REG_998 = 998;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
@@ -130,6 +136,8 @@ static const char* target_xml =
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
</feature>
<feature name="org.gnu.gdb.aarch64.fpu">
</feature>
</target>
)";
@@ -144,6 +152,7 @@ static u32 latest_signal = 0;
static bool memory_break = false;
static Kernel::Thread* current_thread = nullptr;
static u32 current_core = 0;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
@@ -171,20 +180,41 @@ static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
struct Module {
std::string name;
PAddr beg;
PAddr end;
};
static std::vector<Module> modules;
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
Module module;
if (add_elf_ext) {
Common::SplitPath(name, nullptr, &module.name, nullptr);
module.name += ".elf";
} else {
module.name = std::move(name);
}
module.beg = beg;
module.end = end;
modules.push_back(std::move(module));
}
static Kernel::Thread* FindThreadById(int id) {
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto thread : threads) {
if (thread->GetThreadId() == id) {
current_thread = thread.get();
return current_thread;
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadId() == static_cast<u32>(id)) {
current_core = core;
return thread.get();
}
}
}
return nullptr;
}
static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
if (!thread) {
return 0;
}
@@ -197,12 +227,14 @@ static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {
return thread->context.pc;
} else if (id == CPSR_REGISTER) {
return thread->context.cpsr;
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0];
} else {
return 0;
}
}
static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) {
if (!thread) {
return;
}
@@ -215,6 +247,8 @@ static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {
thread->context.pc = val;
} else if (id == CPSR_REGISTER) {
thread->context.cpsr = val;
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val;
}
}
@@ -534,7 +568,11 @@ static void HandleQuery() {
SendReply("T0");
} else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
// PacketSize needs to be large enough for target xml
SendReply("PacketSize=2000;qXfer:features:read+");
std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+";
if (!modules.empty()) {
buffer += ";qXfer:libraries:read+";
}
SendReply(buffer.c_str());
} else if (strncmp(query, "Xfer:features:read:target.xml:",
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
@@ -543,9 +581,9 @@ static void HandleQuery() {
SendReply(buffer.c_str());
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
for (int core = 0; core < Core::NUM_CPU_CORES; core++) {
auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto thread : threads) {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x}", thread->GetThreadId());
val += ",";
}
@@ -554,6 +592,31 @@ static void HandleQuery() {
SendReply(val.c_str());
} else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) {
SendReply("l");
} else if (strncmp(query, "Xfer:threads:read", strlen("Xfer:threads:read")) == 0) {
std::string buffer;
buffer += "l<?xml version=\"1.0\"?>";
buffer += "<threads>";
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
thread->GetThreadId(), core, thread->GetThreadId());
}
}
buffer += "</threads>";
SendReply(buffer.c_str());
} else if (strncmp(query, "Xfer:libraries:read", strlen("Xfer:libraries:read")) == 0) {
std::string buffer;
buffer += "l<?xml version=\"1.0\"?>";
buffer += "<library-list>";
for (const auto& module : modules) {
buffer +=
fmt::format(R"*("<library name = "{}"><segment address = "0x{:x}"/></library>)*",
module.name, module.beg);
}
buffer += "</library-list>";
SendReply(buffer.c_str());
} else {
SendReply("");
}
@@ -561,33 +624,27 @@ static void HandleQuery() {
/// Handle set thread command from gdb client.
static void HandleSetThread() {
if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) {
int thread_id = -1;
if (command_buffer[2] != '-') {
thread_id = static_cast<int>(HexToInt(
command_buffer + 2,
command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 2)*/));
}
if (thread_id >= 1) {
current_thread = FindThreadById(thread_id);
}
if (!current_thread) {
thread_id = 1;
current_thread = FindThreadById(thread_id);
}
if (current_thread) {
SendReply("OK");
return;
}
int thread_id = -1;
if (command_buffer[2] != '-') {
thread_id = static_cast<int>(HexToInt(command_buffer + 2, command_length - 2));
}
if (thread_id >= 1) {
current_thread = FindThreadById(thread_id);
}
if (!current_thread) {
thread_id = 1;
current_thread = FindThreadById(thread_id);
}
if (current_thread) {
SendReply("OK");
return;
}
SendReply("E01");
}
/// Handle thread alive command from gdb client.
static void HandleThreadAlive() {
int thread_id = static_cast<int>(
HexToInt(command_buffer + 1,
command_length - 1 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/));
int thread_id = static_cast<int>(HexToInt(command_buffer + 1, command_length - 1));
if (thread_id == 0) {
thread_id = 1;
}
@@ -610,16 +667,23 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
latest_signal = signal;
std::string buffer;
if (full) {
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER,
Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
Common::swap64(RegRead(SP_REGISTER, thread)));
} else {
buffer = fmt::format("T{:02x};", latest_signal);
if (!thread) {
full = false;
}
buffer += fmt::format("thread:{:x};", thread->GetThreadId());
std::string buffer;
if (full) {
buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};{:02x}:{:016x}", latest_signal,
PC_REGISTER, Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER,
Common::swap64(RegRead(SP_REGISTER, thread)), LR_REGISTER,
Common::swap64(RegRead(LR_REGISTER, thread)));
} else {
buffer = fmt::format("T{:02x}", latest_signal);
}
if (thread) {
buffer += fmt::format(";thread:{:x};", thread->GetThreadId());
}
SendReply(buffer.c_str());
}
@@ -680,7 +744,7 @@ static bool IsDataAvailable() {
fd_set fd_socket;
FD_ZERO(&fd_socket);
FD_SET(gdbserver_socket, &fd_socket);
FD_SET(static_cast<u32>(gdbserver_socket), &fd_socket);
struct timeval t;
t.tv_sec = 0;
@@ -711,8 +775,12 @@ static void ReadRegister() {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == CPSR_REGISTER) {
IntToGdbHex(reply, (u32)RegRead(id, current_thread));
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
LongToGdbHex(reply, RegRead(id, current_thread));
} else if (id == FPSCR_REGISTER) {
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
} else {
return SendReply("E01");
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
}
SendReply(reinterpret_cast<char*>(reply));
@@ -725,11 +793,11 @@ static void ReadRegisters() {
u8* bufptr = buffer;
for (int reg = 0; reg <= SP_REGISTER; reg++) {
for (u32 reg = 0; reg <= SP_REGISTER; reg++) {
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
}
bufptr += (32 * 16);
bufptr += 32 * 16;
LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread));
@@ -739,6 +807,16 @@ static void ReadRegisters() {
bufptr += 8;
for (u32 reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) {
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
}
bufptr += 32 * 32;
LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread));
bufptr += 8;
SendReply(reinterpret_cast<char*>(buffer));
}
@@ -759,10 +837,17 @@ static void WriteRegister() {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == CPSR_REGISTER) {
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
} else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) {
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
} else if (id == FPSCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
} else {
return SendReply("E01");
RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
}
@@ -773,18 +858,25 @@ static void WriteRegisters() {
if (command_buffer[0] != 'G')
return SendReply("E01");
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
if (reg <= SP_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == PC_REGISTER) {
RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == CPSR_REGISTER) {
RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread);
} else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) {
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else if (reg == FPSCR_REGISTER) {
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
} else {
UNIMPLEMENTED();
}
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
}
@@ -806,6 +898,10 @@ static void ReadMemory() {
SendReply("E01");
}
if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) {
return SendReply("E00");
}
if (!Memory::IsValidVirtualAddress(addr)) {
return SendReply("E00");
}
@@ -840,16 +936,18 @@ static void WriteMemory() {
}
void Break(bool is_memory_break) {
if (!halt_loop) {
halt_loop = true;
send_trap = true;
}
send_trap = true;
memory_break = is_memory_break;
}
/// Tell the CPU that it should perform a single step.
static void Step() {
if (command_length > 1) {
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
}
step_loop = true;
halt_loop = true;
send_trap = true;
@@ -1090,6 +1188,8 @@ static void Init(u16 port) {
breakpoints_read.clear();
breakpoints_write.clear();
modules.clear();
// Start gdb server
LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port);
@@ -1192,8 +1292,12 @@ void SetCpuStepFlag(bool is_step) {
void SendTrap(Kernel::Thread* thread, int trap) {
if (send_trap) {
if (!halt_loop || current_thread == thread) {
current_thread = thread;
SendSignal(thread, trap);
}
halt_loop = true;
send_trap = false;
SendSignal(thread, trap);
}
}
}; // namespace GDBStub

View File

@@ -6,6 +6,7 @@
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/thread.h"
@@ -51,6 +52,9 @@ bool IsServerEnabled();
/// Returns true if there is an active socket connection.
bool IsConnected();
/// Register module.
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
/**
* Signal to the gdbstub server that it should halt CPU execution.
*
@@ -80,10 +84,10 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
*/
bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
// If set to true, the CPU will halt at the beginning of the next CPU loop.
/// If set to true, the CPU will halt at the beginning of the next CPU loop.
bool GetCpuHaltFlag();
// If set to true and the CPU is halted, the CPU will step one instruction.
/// If set to true and the CPU is halted, the CPU will step one instruction.
bool GetCpuStepFlag();
/**

View File

@@ -65,7 +65,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signal the waiting threads.
for (size_t i = 0; i < last; i++) {
ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
ASSERT(waiting_threads[i]->status == THREADSTATUS_WAIT_ARB);
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
waiting_threads[i]->arb_wait_address = 0;
waiting_threads[i]->ResumeFromWait();
@@ -115,7 +115,7 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
s32 updated_value;
if (waiting_threads.size() == 0) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;
} else {
updated_value = value;
@@ -140,7 +140,9 @@ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool
s32 cur_value = static_cast<s32>(Memory::Read32(address));
if (cur_value < value) {
Memory::Write32(address, static_cast<u32>(cur_value - 1));
if (should_decrement) {
Memory::Write32(address, static_cast<u32>(cur_value - 1));
}
} else {
return ERR_INVALID_STATE;
}

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
@@ -19,17 +21,18 @@ namespace Kernel {
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
server_session->SetHleHandler(shared_from_this());
connected_sessions.push_back(server_session);
connected_sessions.push_back(std::move(server_session));
}
void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) {
void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& server_session) {
server_session->SetHleHandler(nullptr);
boost::range::remove_erase(connected_sessions, server_session);
}
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
const std::string& reason, u64 timeout,
WakeupCallback&& callback) {
WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback =
@@ -41,7 +44,12 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
return true;
};
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
if (!event) {
// Create event if not provided
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
}
event->Clear();
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
thread->wait_objects = {event};
event->AddWaitingThread(thread);
@@ -214,8 +222,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
(sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32);
ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented");
ASSERT_MSG(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT_MSG(move_objects.size() == handle_descriptor_header->num_handles_to_move);
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
// We don't make a distinction between copy and move handles when translating since HLE
// services don't deal with handles directly. However, the guest applications might check

View File

@@ -59,12 +59,12 @@ public:
* associated ServerSession.
* @param server_session ServerSession associated with the connection.
*/
void ClientDisconnected(SharedPtr<ServerSession> server_session);
void ClientDisconnected(const SharedPtr<ServerSession>& server_session);
protected:
/// List of sessions that are connected to this handler.
/// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
// for the duration of the connection.
/// for the duration of the connection.
std::vector<SharedPtr<ServerSession>> connected_sessions;
};
@@ -118,10 +118,12 @@ public:
* @param callback Callback to be invoked when the thread is resumed. This callback must write
* the entire command response once again, regardless of the state of it before this function
* was called.
* @param event Event to use to wake up the thread. If unspecified, an event will be created.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
u64 timeout, WakeupCallback&& callback);
u64 timeout, WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event = nullptr);
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include "common/assert.h"
#include "core/hle/kernel/object_address_table.h"
@@ -11,7 +13,7 @@ ObjectAddressTable g_object_address_table;
void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) {
ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x{:X}", addr);
objects[addr] = obj;
objects[addr] = std::move(obj);
}
void ObjectAddressTable::Close(VAddr addr) {

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <utility>
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
@@ -85,6 +87,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
cpu_core->ClearExclusiveState();
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
@@ -112,7 +115,7 @@ void Scheduler::Reschedule() {
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
thread_list.push_back(thread);
thread_list.push_back(std::move(thread));
ready_queue.prepare(priority);
}

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <tuple>
#include <utility>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
@@ -158,7 +159,7 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n
std::shared_ptr<Session> parent(new Session);
parent->client = client_session.get();
parent->server = server_session.get();
parent->port = port;
parent->port = std::move(port);
client_session->parent = parent;
server_session->parent = parent;

View File

@@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <utility>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
@@ -21,7 +21,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->owner_process = owner_process;
shared_memory->owner_process = std::move(owner_process);
shared_memory->name = std::move(name);
shared_memory->size = size;
shared_memory->permissions = permissions;
@@ -87,7 +87,7 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vecto
shared_memory->size = size;
shared_memory->permissions = permissions;
shared_memory->other_permissions = other_permissions;
shared_memory->backing_block = heap_block;
shared_memory->backing_block = std::move(heap_block);
shared_memory->backing_block_offset = offset;
shared_memory->base_address = Memory::HEAP_VADDR + offset;

View File

@@ -165,7 +165,7 @@ void Thread::CancelWakeupTimer() {
static boost::optional<s32> GetNextProcessorId(u64 mask) {
for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
if (mask & (1ULL << index)) {
if (!Core::System().GetInstance().Scheduler(index)->GetCurrentThread()) {
if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) {
// Core is enabled and not running any threads, use this one
return index;
}
@@ -215,14 +215,14 @@ void Thread::ResumeFromWait() {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
@@ -325,7 +325,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
thread->owner_process = owner_process;
thread->scheduler = Core::System().GetInstance().Scheduler(processor_id);
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
// Find the next available TLS index, and mark it as used
@@ -400,7 +400,7 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, owner_process);
Memory::STACK_AREA_VADDR_END, std::move(owner_process));
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
@@ -481,14 +481,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <iterator>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
@@ -16,30 +17,20 @@
namespace Kernel {
static const char* GetMemoryStateName(MemoryState state) {
static const char* names[] = {
"Unmapped",
"Io",
"Normal",
"CodeStatic",
"CodeMutable",
"Heap",
"Shared",
"Unknown1"
"ModuleCodeStatic",
"ModuleCodeMutable",
"IpcBuffer0",
"Mapped",
"ThreadLocal",
"TransferMemoryIsolated",
"TransferMemory",
"ProcessMemory",
"Unknown2"
"IpcBuffer1",
"IpcBuffer3",
"KernelStack",
static constexpr const char* names[] = {
"Unmapped", "Io",
"Normal", "CodeStatic",
"CodeMutable", "Heap",
"Shared", "Unknown1",
"ModuleCodeStatic", "ModuleCodeMutable",
"IpcBuffer0", "Mapped",
"ThreadLocal", "TransferMemoryIsolated",
"TransferMemory", "ProcessMemory",
"Unknown2", "IpcBuffer1",
"IpcBuffer3", "KernelStack",
};
return names[(int)state];
return names[static_cast<int>(state)];
}
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
@@ -117,7 +108,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
final_vma.type = VMAType::AllocatedMemoryBlock;
final_vma.permissions = VMAPermission::ReadWrite;
final_vma.meminfo_state = state;
final_vma.backing_block = block;
final_vma.backing_block = std::move(block);
final_vma.offset = offset;
UpdatePageTableForVMA(final_vma);
@@ -160,7 +151,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u6
final_vma.permissions = VMAPermission::ReadWrite;
final_vma.meminfo_state = state;
final_vma.paddr = paddr;
final_vma.mmio_handler = mmio_handler;
final_vma.mmio_handler = std::move(mmio_handler);
UpdatePageTableForVMA(final_vma);
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));

View File

@@ -4,9 +4,10 @@
#include <cinttypes>
#include <stack>
#include "core/file_sys/filesystem.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
@@ -614,25 +615,14 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 uid = rp.PopRaw<u128>();
u128 uid = rp.PopRaw<u128>(); // What does this do?
LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 4};
FileSys::Path unused;
auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused);
if (savedata.Failed()) {
// Create the save data and return an error indicating that the operation was performed.
FileSystem::FormatFileSystem(FileSystem::Type::SaveData);
// TODO(Subv): Find out the correct error code for this.
rb.Push(ResultCode(ErrorModule::FS, 40));
} else {
rb.Push(RESULT_SUCCESS);
}
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
} // namespace Service::AM
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.

View File

@@ -2,71 +2,281 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <boost/container/flat_map.hpp>
#include <utility>
#include "common/assert.h"
#include "common/file_util.h"
#include "core/file_sys/filesystem.h"
#include "core/core.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
namespace Service::FileSystem {
// Size of emulated sd card free space, reported in bytes.
// Just using 32GB because thats reasonable
// TODO(DarkLordZach): Eventually make this configurable in settings.
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
const std::string& dir_name) {
if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
return base;
return base->GetDirectoryRelative(dir_name);
}
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
: backing(std::move(backing_)) {}
std::string VfsDirectoryServiceWrapper::GetName() const {
return backing->GetName();
}
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const {
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
auto file = dir->CreateFile(FileUtil::GetFilename(path));
if (file == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
if (!file->Resize(size)) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path == "/" || path == "\\") {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (!backing->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty())
dir = backing;
auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path));
if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path,
const std::string& dest_path) const {
auto src = backing->GetFileRelative(src_path);
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (!src->Rename(FileUtil::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
// Move by hand -- TODO(DarkLordZach): Optimize
auto c_res = CreateFile(dest_path, src->GetSize());
if (c_res != RESULT_SUCCESS)
return c_res;
auto dest = backing->GetFileRelative(dest_path);
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
"Could not write all of the bytes but everything else has succeded.");
if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path,
const std::string& dest_path) const {
auto src = GetDirectoryRelativeWrapped(backing, src_path);
if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (!src->Rename(FileUtil::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
// TODO(DarkLordZach): Implement renaming across the tree (move).
ASSERT_MSG(false,
"Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
"don't match -- UNIMPLEMENTED",
src_path, dest_path);
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path,
FileSys::Mode mode) const {
auto npath = path;
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
npath = npath.substr(1);
auto file = backing->GetFileRelative(npath);
if (file == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
if (mode == FileSys::Mode::Append) {
return MakeResult<FileSys::VirtualFile>(
std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()));
}
return MakeResult<FileSys::VirtualFile>(file);
}
ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) {
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return MakeResult(dir);
}
u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
if (backing->IsWritable())
return EMULATED_SD_REPORTED_SIZE;
return 0;
}
ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
auto filename = FileUtil::GetFilename(path);
if (dir->GetFile(filename) != nullptr)
return MakeResult(FileSys::EntryType::File);
if (dir->GetSubdirectory(filename) != nullptr)
return MakeResult(FileSys::EntryType::Directory);
return FileSys::ERROR_PATH_NOT_FOUND;
}
/**
* Map of registered file systems, identified by type. Once an file system is registered here, it
* is never removed until UnregisterFileSystems is called.
*/
static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) {
auto result = filesystem_map.emplace(type, std::move(factory));
bool inserted = result.second;
ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
auto& filesystem = result.first->second;
LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(),
static_cast<u32>(type));
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
romfs_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered RomFS");
return RESULT_SUCCESS;
}
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path) {
LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type));
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
// TODO(bunnei): Find a better error code for this
return ResultCode(-1);
}
return itr->second->Open(path);
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data");
save_data_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered save data");
return RESULT_SUCCESS;
}
ResultCode FormatFileSystem(Type type) {
LOG_TRACE(Service_FS, "Formatting FileSystem with type={}", static_cast<u32>(type));
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return RESULT_SUCCESS;
}
auto itr = filesystem_map.find(type);
if (itr == filesystem_map.end()) {
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id) {
LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id);
if (romfs_factory == nullptr) {
// TODO(bunnei): Find a better error code for this
return ResultCode(-1);
}
FileSys::Path unused;
return itr->second->Format(unused);
return romfs_factory->Open(title_id);
}
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct) {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), save_struct.DebugInfo());
if (save_data_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
}
return save_data_factory->Open(space, save_struct);
}
ResultVal<FileSys::VirtualDir> OpenSDMC() {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
}
return sdmc_factory->Open();
}
void RegisterFileSystems() {
filesystem_map.clear();
romfs_factory = nullptr;
save_data_factory = nullptr;
sdmc_factory = nullptr;
std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX);
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write);
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write);
auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
RegisterFileSystem(std::move(savedata), Type::SaveData);
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
save_data_factory = std::move(savedata);
auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory));
RegisterFileSystem(std::move(sdcard), Type::SDMC);
auto sdcard = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
sdmc_factory = std::move(sdcard);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {

View File

@@ -6,14 +6,13 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/hle/result.h"
namespace FileSys {
class FileSystemBackend;
class FileSystemFactory;
class Path;
} // namespace FileSys
namespace Service {
namespace SM {
@@ -22,38 +21,117 @@ class ServiceManager;
namespace FileSystem {
/// Supported FileSystem types
enum class Type {
RomFS = 1,
SaveData = 2,
SDMC = 3,
};
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
/**
* Registers a FileSystem, instances of which can later be opened using its IdCode.
* @param factory FileSystem backend interface to use
* @param type Type used to access this type of FileSystem
*/
ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type);
// TODO(DarkLordZach): BIS Filesystem
// ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id);
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct);
ResultVal<FileSys::VirtualDir> OpenSDMC();
/**
* Opens a file system
* @param type Type of the file system to open
* @param path Path to the file system, used with Binary paths
* @return FileSys::FileSystemBackend interface to the file system
*/
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path);
/**
* Formats a file system
* @param type Type of the file system to format
* @return ResultCode of the operation
*/
ResultCode FormatFileSystem(Type type);
// TODO(DarkLordZach): BIS Filesystem
// ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS();
/// Registers all Filesystem services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
// 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
// avoids repetitive code.
class VfsDirectoryServiceWrapper {
public:
explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing);
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
*/
std::string GetName() const;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
ResultCode CreateFile(const std::string& path, u64 size) const;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode DeleteFile(const std::string& path) const;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode CreateDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode DeleteDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path and anything under it
* @param path Path relative to the archive
* @return Result of the operation
*/
ResultCode DeleteDirectoryRecursively(const std::string& path) const;
/**
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
/**
* Rename a Directory specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or error code
*/
ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or error code
*/
ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
/**
* Get the free space
* @return The number of free bytes in the archive
*/
u64 GetFreeSpaceSize() const;
/**
* Get the type of the specified path
* @return The type of the specified path or error code
*/
ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const;
private:
FileSys::VirtualDir backing;
};
} // namespace FileSystem
} // namespace Service

View File

@@ -8,20 +8,26 @@
#include "core/core.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/filesystem.h"
#include "core/file_sys/storage.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
namespace Service::FileSystem {
enum class StorageId : u8 {
None = 0,
Host = 1,
GameCard = 2,
NandSystem = 3,
NandUser = 4,
SdCard = 5,
};
class IStorage final : public ServiceFramework<IStorage> {
public:
IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend)
: ServiceFramework("IStorage"), backend(std::move(backend)) {
IStorage(FileSys::VirtualFile backend_)
: ServiceFramework("IStorage"), backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
{3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"},
@@ -30,7 +36,7 @@ public:
}
private:
std::unique_ptr<FileSys::StorageBackend> backend;
FileSys::VirtualFile backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -52,14 +58,7 @@ private:
}
// Read the data from the Storage backend
std::vector<u8> output(length);
ResultVal<size_t> res = backend->Read(offset, length, output.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
std::vector<u8> output = backend->ReadBytes(length, offset);
// Write the data to memory
ctx.WriteBuffer(output);
@@ -70,8 +69,8 @@ private:
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
: ServiceFramework("IFile"), backend(std::move(backend)) {
explicit IFile(FileSys::VirtualFile backend_)
: ServiceFramework("IFile"), backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"},
{2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"},
@@ -81,7 +80,7 @@ public:
}
private:
std::unique_ptr<FileSys::StorageBackend> backend;
FileSys::VirtualFile backend;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -104,20 +103,14 @@ private:
}
// Read the data from the Storage backend
std::vector<u8> output(length);
ResultVal<size_t> res = backend->Read(offset, length, output.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
std::vector<u8> output = backend->ReadBytes(length, offset);
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(*res));
rb.Push(static_cast<u64>(output.size()));
}
void Write(Kernel::HLERequestContext& ctx) {
@@ -140,14 +133,21 @@ private:
return;
}
// Write the data to the Storage backend
std::vector<u8> data = ctx.ReadBuffer();
ResultVal<size_t> res = backend->Write(offset, length, true, data.data());
if (res.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
std::vector<u8> actual_data(length);
ASSERT_MSG(
data.size() <= length,
"Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
length, data.size());
std::copy(data.begin(), data.end(), actual_data.begin());
// Write the data to the Storage backend
auto written = backend->WriteBytes(data, offset);
ASSERT_MSG(written == length,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
written);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -155,7 +155,8 @@ private:
void Flush(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
backend->Flush();
// Exists for SDK compatibiltity -- No need to flush file.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -164,7 +165,7 @@ private:
void SetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
backend->SetSize(size);
backend->Resize(size);
LOG_DEBUG(Service_FS, "called, size={}", size);
IPC::ResponseBuilder rb{ctx, 2};
@@ -181,19 +182,39 @@ private:
}
};
template <typename T>
static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data,
FileSys::EntryType type) {
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));
}
}
class IDirectory final : public ServiceFramework<IDirectory> {
public:
explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend)
: ServiceFramework("IDirectory"), backend(std::move(backend)) {
explicit IDirectory(FileSys::VirtualDir backend_)
: ServiceFramework("IDirectory"), backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
};
RegisterHandlers(functions);
// TODO(DarkLordZach): Verify that this is the correct behavior.
// Build entry index now to save time later.
BuildEntryIndex(entries, backend->GetFiles(), FileSys::File);
BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory);
}
private:
std::unique_ptr<FileSys::DirectoryBackend> backend;
FileSys::VirtualDir backend;
std::vector<FileSys::Entry> entries;
u64 next_entry_index = 0;
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -204,26 +225,31 @@ private:
// Calculate how many entries we can fit in the output buffer
u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry);
// Cap at total number of entries.
u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
// Read the data from the Directory backend
std::vector<FileSys::Entry> entries(count_entries);
u64 read_entries = backend->Read(count_entries, entries.data());
std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index,
entries.begin() + next_entry_index + actual_entries);
next_entry_index += actual_entries;
// Convert the data into a byte array
std::vector<u8> output(entries.size() * sizeof(FileSys::Entry));
std::memcpy(output.data(), entries.data(), output.size());
std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry));
std::memcpy(output.data(), entry_data.data(), output.size());
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(read_entries);
rb.Push(actual_entries);
}
void GetEntryCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
u64 count = backend->GetEntryCount();
u64 count = entries.size() - next_entry_index;
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -233,7 +259,7 @@ private:
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
explicit IFileSystem(FileSys::VirtualDir backend)
: ServiceFramework("IFileSystem"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
@@ -268,7 +294,7 @@ public:
LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->CreateFile(name, size));
rb.Push(backend.CreateFile(name, size));
}
void DeleteFile(Kernel::HLERequestContext& ctx) {
@@ -280,7 +306,7 @@ public:
LOG_DEBUG(Service_FS, "called file {}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->DeleteFile(name));
rb.Push(backend.DeleteFile(name));
}
void CreateDirectory(Kernel::HLERequestContext& ctx) {
@@ -292,7 +318,7 @@ public:
LOG_DEBUG(Service_FS, "called directory {}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->CreateDirectory(name));
rb.Push(backend.CreateDirectory(name));
}
void RenameFile(Kernel::HLERequestContext& ctx) {
@@ -310,7 +336,7 @@ public:
LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend->RenameFile(src_name, dst_name));
rb.Push(backend.RenameFile(src_name, dst_name));
}
void OpenFile(Kernel::HLERequestContext& ctx) {
@@ -323,14 +349,14 @@ public:
LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
auto result = backend->OpenFile(name, mode);
auto result = backend.OpenFile(name, mode);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
auto file = std::move(result.Unwrap());
IFile file(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -348,14 +374,14 @@ public:
LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
auto result = backend->OpenDirectory(name);
auto result = backend.OpenDirectory(name);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
auto directory = std::move(result.Unwrap());
IDirectory directory(result.Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -370,7 +396,7 @@ public:
LOG_DEBUG(Service_FS, "called file {}", name);
auto result = backend->GetEntryType(name);
auto result = backend.GetEntryType(name);
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
@@ -390,7 +416,7 @@ public:
}
private:
std::unique_ptr<FileSys::FileSystemBackend> backend;
VfsDirectoryServiceWrapper backend;
};
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
@@ -487,17 +513,6 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
RegisterHandlers(functions);
}
void FSP_SRV::TryLoadRomFS() {
if (romfs) {
return;
}
FileSys::Path unused;
auto res = OpenFileSystem(Type::RomFS, unused);
if (res.Succeeded()) {
romfs = std::move(res.Unwrap());
}
}
void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
@@ -508,8 +523,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap();
IFileSystem filesystem(OpenSDMC().Unwrap());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -519,33 +533,38 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<std::array<u8, 0x40>>();
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_WARNING(Service_FS, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}",
save_struct.DebugInfo(), uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::RequestParser rp{ctx};
// TODO(Subv): Read the input parameters and mount the requested savedata instead of always
// mounting the current process' savedata.
FileSys::Path unused;
auto filesystem = OpenFileSystem(Type::SaveData, unused);
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
auto unk = rp.Pop<u32>();
LOG_INFO(Service_FS, "called with unknown={:08X}", unk);
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
if (filesystem.Failed()) {
auto dir = OpenSaveData(space_id, save_struct);
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound));
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
return;
}
IFileSystem filesystem(std::move(dir.Unwrap()));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IFileSystem>(std::move(filesystem.Unwrap()));
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -559,8 +578,8 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
TryLoadRomFS();
if (!romfs) {
auto romfs = OpenRomFS(Core::System::GetInstance().CurrentProcess()->program_id);
if (romfs.Failed()) {
// TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::ResponseBuilder rb{ctx, 2};
@@ -568,23 +587,24 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
return;
}
// Attempt to open a StorageBackend interface to the RomFS
auto storage = romfs->OpenFile({}, {});
if (storage.Failed()) {
LOG_CRITICAL(Service_FS, "no storage interface available!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(storage.Code());
return;
}
IStorage storage(std::move(romfs.Unwrap()));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));
rb.PushIpcInterface<IStorage>(std::move(storage));
}
void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess");
OpenDataStorageByCurrentProcess(ctx);
IPC::RequestParser rp{ctx};
auto storage_id = rp.PopRaw<StorageId>();
auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}",
static_cast<u8>(storage_id), title_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
}
} // namespace Service::FileSystem

View File

@@ -19,8 +19,6 @@ public:
~FSP_SRV() = default;
private:
void TryLoadRomFS();
void Initialize(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);
@@ -29,7 +27,7 @@ private:
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenRomStorage(Kernel::HLERequestContext& ctx);
std::unique_ptr<FileSys::FileSystemBackend> romfs;
FileSys::VirtualFile romfs;
};
} // namespace Service::FileSystem

View File

@@ -5,6 +5,7 @@
#include <atomic>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/frontend/input.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
@@ -18,9 +19,9 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(shinyquagsire23): These need better values.
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
@@ -63,7 +64,8 @@ private:
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
// TODO(shinyquagsire23): gyro, touch, mouse, keyboard
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
// TODO(shinyquagsire23): gyro, mouse, keyboard
}
void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -151,8 +153,6 @@ private:
}
}
// TODO(bunnei): Properly implement the touch screen, the below will just write empty data
TouchScreen& touchscreen = mem.touchscreen;
const u64 last_entry = touchscreen.header.latest_entry;
const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
@@ -164,7 +164,26 @@ private:
touchscreen.header.max_entry_index = touchscreen.entries.size();
touchscreen.header.timestamp = timestamp;
touchscreen.entries[curr_entry].header.timestamp = sample_counter;
touchscreen.entries[curr_entry].header.num_touches = 0;
TouchScreenEntryTouch touch_entry{};
auto [x, y, pressed] = touch_device->GetStatus();
touch_entry.timestamp = timestamp;
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
touch_entry.touch_index = 0;
// TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
touch_entry.diameter_x = 15;
touch_entry.diameter_y = 15;
touch_entry.angle = 0;
// TODO(DarkLordZach): Implement multi-touch support
if (pressed) {
touchscreen.entries[curr_entry].header.num_touches = 1;
touchscreen.entries[curr_entry].touches[0] = touch_entry;
} else {
touchscreen.entries[curr_entry].header.num_touches = 0;
}
// TODO(shinyquagsire23): Properly implement mouse
Mouse& mouse = mem.mouse;
@@ -250,6 +269,7 @@ private:
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
std::unique_ptr<Input::TouchDevice> touch_device;
};
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {

View File

@@ -18,7 +18,8 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector
}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect) {
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_WARNING(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
@@ -26,7 +27,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
const Tegra::FramebufferConfig framebuffer{
addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform};
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
transform, crop_rect};
Core::System::GetInstance().perf_stats.EndGameFrame();

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/math_util.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
@@ -23,7 +24,8 @@ public:
/// Performs a screen flip, drawing the buffer pointed to by the handle.
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
NVFlinger::BufferQueue::BufferTransformFlags transform);
NVFlinger::BufferQueue::BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
private:
std::shared_ptr<nvmap> nvmap_dev;

View File

@@ -13,8 +13,8 @@ namespace Service {
namespace NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
native_handle->Signal();
buffer_wait_event =
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
@@ -26,10 +26,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
queue.emplace_back(buffer);
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
buffer_wait_event->Signal();
}
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
@@ -48,8 +45,6 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
return boost::none;
}
buffer_wait_event = nullptr;
itr->status = Buffer::Status::Dequeued;
return itr->slot;
}
@@ -62,13 +57,15 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
return itr->igbp_buffer;
}
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform) {
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Dequeued);
itr->status = Buffer::Status::Queued;
itr->transform = transform;
itr->crop_rect = crop_rect;
}
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
@@ -88,9 +85,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
if (buffer_wait_event) {
buffer_wait_event->Signal();
}
buffer_wait_event->Signal();
}
u32 BufferQueue::Query(QueryType type) {
@@ -106,10 +101,5 @@ u32 BufferQueue::Query(QueryType type) {
return 0;
}
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
buffer_wait_event = std::move(wait_event);
}
} // namespace NVFlinger
} // namespace Service

View File

@@ -6,6 +6,7 @@
#include <vector>
#include <boost/optional.hpp>
#include "common/math_util.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
@@ -68,23 +69,24 @@ public:
Status status = Status::Free;
IGBPBuffer igbp_buffer;
BufferTransformFlags transform;
MathUtil::Rectangle<int> crop_rect;
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform);
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
boost::optional<const Buffer&> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
u32 GetId() const {
return id;
}
Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
return native_handle;
Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const {
return buffer_wait_event;
}
private:
@@ -92,9 +94,6 @@ private:
u64 layer_id;
std::vector<Buffer> queue;
Kernel::SharedPtr<Kernel::Event> native_handle;
/// Used to signal waiting thread when no buffers are available
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
};

View File

@@ -149,12 +149,10 @@ void NVFlinger::Compose() {
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform);
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform,
buffer->crop_rect);
buffer_queue->ReleaseBuffer(buffer->slot);
// TODO(Subv): Figure out when we should actually signal this event.
buffer_queue->GetNativeHandle()->Signal();
}
}

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include "core/hle/kernel/event.h"

View File

@@ -19,10 +19,9 @@ void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // bsd errno
}
void BSD::Socket(Kernel::HLERequestContext& ctx) {

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <boost/optional.hpp>
#include "common/alignment.h"
#include "common/math_util.h"
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
@@ -27,8 +28,8 @@ struct DisplayInfo {
char display_name[0x40]{"Default"};
u64 unknown_1{1};
u64 unknown_2{1};
u64 width{1920};
u64 height{1080};
u64 width{1280};
u64 height{720};
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
@@ -327,8 +328,8 @@ public:
protected:
void SerializeData() override {
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try
// to read an IGBPBuffer object from the parcel.
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
// try to read an IGBPBuffer object from the parcel.
Write<u32_le>(1);
WriteObject(buffer);
Write<u32_le>(0);
@@ -360,8 +361,8 @@ public:
INSERT_PADDING_WORDS(3);
u32_le timestamp;
s32_le is_auto_timestamp;
s32_le crop_left;
s32_le crop_top;
s32_le crop_left;
s32_le crop_right;
s32_le crop_bottom;
s32_le scaling_mode;
@@ -370,6 +371,10 @@ public:
INSERT_PADDING_WORDS(2);
u32_le fence_is_valid;
std::array<Fence, 2> fences;
MathUtil::Rectangle<int> GetCropRect() const {
return {crop_left, crop_top, crop_right, crop_bottom};
}
};
static_assert(sizeof(Data) == 80, "ParcelData has wrong size");
@@ -495,7 +500,7 @@ private:
ctx.WriteBuffer(response.Serialize());
} else {
// Wait the current thread until a buffer becomes available
auto wait_event = ctx.SleepClientThread(
ctx.SleepClientThread(
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
ThreadWakeupReason reason) {
@@ -506,8 +511,8 @@ private:
ctx.WriteBuffer(response.Serialize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
});
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
},
buffer_queue->GetBufferWaitEvent());
}
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
@@ -519,7 +524,8 @@ private:
} else if (transaction == TransactionId::QueueBuffer) {
IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
buffer_queue->QueueBuffer(request.data.slot, request.data.transform,
request.data.GetCropRect());
IGBPQueueBufferResponseParcel response{1280, 720};
ctx.WriteBuffer(response.Serialize());
@@ -532,7 +538,7 @@ private:
IGBPQueryResponseParcel response{value};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
} else {
ASSERT_MSG(false, "Unimplemented");
}
@@ -565,7 +571,7 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(buffer_queue->GetNativeHandle());
rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent());
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;

View File

@@ -4,11 +4,11 @@
#include <cinttypes>
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/content_archive.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -46,55 +46,11 @@ static std::string FindRomFS(const std::string& directory) {
return filepath_romfs;
}
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file,
std::string filepath)
: AppLoader(std::move(file)), filepath(std::move(filepath)) {}
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
: AppLoader(std::move(file)) {}
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file,
const std::string& filepath) {
bool is_main_found{};
bool is_npdm_found{};
bool is_rtld_found{};
bool is_sdk_found{};
const auto callback = [&](unsigned* num_entries_out, const std::string& directory,
const std::string& virtual_name) -> bool {
// Skip directories
std::string physical_name = directory + virtual_name;
if (FileUtil::IsDirectory(physical_name)) {
return true;
}
// Verify filename
if (Common::ToLower(virtual_name) == "main") {
is_main_found = true;
} else if (Common::ToLower(virtual_name) == "main.npdm") {
is_npdm_found = true;
return true;
} else if (Common::ToLower(virtual_name) == "rtld") {
is_rtld_found = true;
} else if (Common::ToLower(virtual_name) == "sdk") {
is_sdk_found = true;
} else {
// Continue searching
return true;
}
// Verify file is an NSO
FileUtil::IOFile file(physical_name, "rb");
if (AppLoader_NSO::IdentifyType(file, physical_name) != FileType::NSO) {
return false;
}
// We are done if we've found and verified all required NSOs
return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found);
};
// Search the directory recursively, looking for the required modules
const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);
if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) {
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
return FileType::DeconstructedRomDirectory;
}
@@ -106,14 +62,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
if (!file.IsOpen()) {
return ResultStatus::Error;
}
const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
const std::string npdm_path = directory + DIR_SEP + "main.npdm";
const FileSys::VirtualDir dir = file->GetContainingDirectory();
const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
if (npdm == nullptr)
return ResultStatus::ErrorInvalidFormat;
ResultStatus result = metadata.Load(npdm_path);
ResultStatus result = metadata.Load(npdm);
if (result != ResultStatus::Success) {
return result;
}
@@ -128,11 +83,14 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const std::string path = directory + DIR_SEP + module;
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
const FileSys::VirtualFile module_file = dir->GetFile(module);
if (module_file != nullptr)
next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
} else {
next_load_addr = load_addr;
}
@@ -147,41 +105,26 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
metadata.GetMainThreadStackSize());
// Find the RomFS by searching for a ".romfs" file in this directory
filepath_romfs = FindRomFS(directory);
const auto& files = dir->GetFiles();
const auto romfs_iter =
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
return file->GetName().find(".romfs") != std::string::npos;
});
// Register the RomFS if a ".romfs" file was found
if (!filepath_romfs.empty()) {
Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
Service::FileSystem::Type::RomFS);
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
romfs = *romfs_iter;
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
}
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(
std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {
if (filepath_romfs.empty()) {
LOG_DEBUG(Loader, "No RomFS available");
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
if (romfs == nullptr)
return ResultStatus::ErrorNotUsed;
}
// We reopen the file, to allow its position to be independent
romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb");
if (!romfs_file->IsOpen()) {
return ResultStatus::Error;
}
offset = 0;
size = romfs_file->GetSize();
LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
// Reset read pointer
file.Seek(0, SEEK_SET);
dir = romfs;
return ResultStatus::Success;
}

View File

@@ -20,29 +20,26 @@ namespace Loader {
*/
class AppLoader_DeconstructedRomDirectory final : public AppLoader {
public:
AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, std::string filepath);
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @param file std::shared_ptr<VfsFile> open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file, filepath);
return IdentifyType(file);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
private:
std::string filepath_romfs;
std::string filepath;
FileSys::ProgramMetadata metadata;
FileSys::VirtualFile romfs;
};
} // namespace Loader

View File

@@ -365,20 +365,17 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
namespace Loader {
AppLoader_ELF::AppLoader_ELF(FileUtil::IOFile&& file, std::string filename)
: AppLoader(std::move(file)), filename(std::move(filename)) {}
AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file, const std::string&) {
FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
static constexpr u16 ELF_MACHINE_ARM{0x28};
u32 magic = 0;
file.Seek(0, SEEK_SET);
if (1 != file.ReadArray<u32>(&magic, 1))
if (4 != file->ReadObject(&magic))
return FileType::Error;
u16 machine = 0;
file.Seek(18, SEEK_SET);
if (1 != file.ReadArray<u16>(&machine, 1))
if (2 != file->ReadObject(&machine, 18))
return FileType::Error;
if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine)
@@ -391,20 +388,13 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
if (!file.IsOpen())
return ResultStatus::Error;
// Reset read pointer in case this file has been read before.
file.Seek(0, SEEK_SET);
size_t size = file.GetSize();
std::unique_ptr<u8[]> buffer(new u8[size]);
if (file.ReadBytes(&buffer[0], size) != size)
std::vector<u8> buffer = file->ReadAllBytes();
if (buffer.size() != file->GetSize())
return ResultStatus::Error;
ElfReader elf_reader(&buffer[0]);
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
codeset->name = filename;
codeset->name = file->GetName();
process->LoadModule(codeset, codeset->entrypoint);
process->svc_access_mask.set();

View File

@@ -16,24 +16,20 @@ namespace Loader {
/// Loads an ELF/AXF file
class AppLoader_ELF final : public AppLoader {
public:
AppLoader_ELF(FileUtil::IOFile&& file, std::string filename);
explicit AppLoader_ELF(FileSys::VirtualFile file);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @param file std::shared_ptr<VfsFile> open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file, filename);
return IdentifyType(file);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
private:
std::string filename;
};
} // namespace Loader

View File

@@ -6,6 +6,7 @@
#include <string>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h"
@@ -21,11 +22,11 @@ const std::initializer_list<Kernel::AddressMapping> default_address_mappings = {
{0x1F000000, 0x600000, false}, // entire VRAM
};
FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) {
FileType IdentifyFile(FileSys::VirtualFile file) {
FileType type;
#define CHECK_TYPE(loader) \
type = AppLoader_##loader::IdentifyType(file, filepath); \
type = AppLoader_##loader::IdentifyType(file); \
if (FileType::Error != type) \
return type;
@@ -41,25 +42,22 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) {
}
FileType IdentifyFile(const std::string& file_name) {
FileUtil::IOFile file(file_name, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Loader, "Failed to load file {}", file_name);
return FileType::Unknown;
}
return IdentifyFile(file, file_name);
return IdentifyFile(FileSys::VirtualFile(std::make_shared<FileSys::RealVfsFile>(file_name)));
}
FileType GuessFromExtension(const std::string& extension_) {
std::string extension = Common::ToLower(extension_);
FileType GuessFromFilename(const std::string& name) {
if (name == "main")
return FileType::DeconstructedRomDirectory;
if (extension == ".elf")
const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name));
if (extension == "elf")
return FileType::ELF;
else if (extension == ".nro")
if (extension == "nro")
return FileType::NRO;
else if (extension == ".nso")
if (extension == "nso")
return FileType::NSO;
else if (extension == ".nca")
if (extension == "nca")
return FileType::NCA;
return FileType::Unknown;
@@ -93,58 +91,47 @@ const char* GetFileTypeString(FileType type) {
* @param filepath the file full path (with name)
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
*/
static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type,
const std::string& filename,
const std::string& filepath) {
static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) {
switch (type) {
// Standard ELF file format.
case FileType::ELF:
return std::make_unique<AppLoader_ELF>(std::move(file), filename);
return std::make_unique<AppLoader_ELF>(std::move(file));
// NX NSO file format.
case FileType::NSO:
return std::make_unique<AppLoader_NSO>(std::move(file), filepath);
return std::make_unique<AppLoader_NSO>(std::move(file));
// NX NRO file format.
case FileType::NRO:
return std::make_unique<AppLoader_NRO>(std::move(file), filepath);
return std::make_unique<AppLoader_NRO>(std::move(file));
// NX NCA file format.
case FileType::NCA:
return std::make_unique<AppLoader_NCA>(std::move(file), filepath);
return std::make_unique<AppLoader_NCA>(std::move(file));
// NX deconstructed ROM directory.
case FileType::DeconstructedRomDirectory:
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath);
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
default:
return nullptr;
}
}
std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
FileUtil::IOFile file(filename, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Loader, "Failed to load file {}", filename);
return nullptr;
}
std::string filename_filename, filename_extension;
Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension);
FileType type = IdentifyFile(file, filename);
FileType filename_type = GuessFromExtension(filename_extension);
std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) {
FileType type = IdentifyFile(file);
FileType filename_type = GuessFromFilename(file->GetName());
if (type != filename_type) {
LOG_WARNING(Loader, "File {} has a different type than its extension.", filename);
LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
if (FileType::Unknown == type)
type = filename_type;
}
LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type));
LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
return GetFileLoader(std::move(file), type, filename_filename, filename);
return GetFileLoader(std::move(file), type);
}
} // namespace Loader

View File

@@ -13,6 +13,7 @@
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
@@ -36,10 +37,9 @@ enum class FileType {
/**
* Identifies the type of a bootable file based on the magic value in its header.
* @param file open file
* @param filepath Path of the file that we are opening.
* @return FileType of file
*/
FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath);
FileType IdentifyFile(FileSys::VirtualFile file);
/**
* Identifies the type of a bootable file based on the magic value in its header.
@@ -50,12 +50,12 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath);
FileType IdentifyFile(const std::string& file_name);
/**
* Guess the type of a bootable file from its extension
* @param extension String extension of bootable file
* Guess the type of a bootable file from its name
* @param name String name of bootable 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 GuessFromExtension(const std::string& extension);
FileType GuessFromFilename(const std::string& name);
/**
* Convert a FileType into a string which can be displayed to the user.
@@ -79,7 +79,7 @@ enum class ResultStatus {
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {}
AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
virtual ~AppLoader() {}
/**
@@ -154,26 +154,20 @@ public:
/**
* Get the RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
* @param romfs_file The file containing the RomFS
* @param offset The offset the romfs begins on
* @param size The size of the romfs
* @param file The file containing the RomFS
* @return ResultStatus result of function
*/
virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) {
virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) {
return ResultStatus::ErrorNotImplemented;
}
/**
* Get the update RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
* @param romfs_file The file containing the RomFS
* @param offset The offset the romfs begins on
* @param size The size of the romfs
* @param file The file containing the RomFS
* @return ResultStatus result of function
*/
virtual ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) {
virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) {
return ResultStatus::ErrorNotImplemented;
}
@@ -187,7 +181,7 @@ public:
}
protected:
FileUtil::IOFile file;
FileSys::VirtualFile file;
bool is_loaded = false;
};
@@ -202,6 +196,6 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi
* @param filename String filename of bootable file
* @return best loader for this file
*/
std::unique_ptr<AppLoader> GetLoader(const std::string& filename);
std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file);
} // namespace Loader

View File

@@ -4,13 +4,14 @@
#include <vector>
#include "common/common_funcs.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -20,208 +21,15 @@
namespace Loader {
// Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(file) {}
constexpr u64 SECTION_HEADER_SIZE = 0x200;
constexpr u64 SECTION_HEADER_OFFSET = 0x400;
enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
struct NcaSectionTableEntry {
u32_le media_offset;
u32_le media_end_offset;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size.");
struct NcaHeader {
std::array<u8, 0x100> rsa_signature_1;
std::array<u8, 0x100> rsa_signature_2;
u32_le magic;
u8 is_system;
NcaContentType content_type;
u8 crypto_type;
u8 key_index;
u64_le size;
u64_le title_id;
INSERT_PADDING_BYTES(0x4);
u32_le sdk_version;
u8 crypto_type_2;
INSERT_PADDING_BYTES(15);
std::array<u8, 0x10> rights_id;
std::array<NcaSectionTableEntry, 0x4> section_tables;
std::array<std::array<u8, 0x20>, 0x4> hash_tables;
std::array<std::array<u8, 0x10>, 0x4> key_area;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size.");
struct NcaSectionHeaderBlock {
INSERT_PADDING_BYTES(3);
NcaSectionFilesystemType filesystem_type;
u8 crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size.");
struct Pfs0Superblock {
NcaSectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
u32_le size;
INSERT_PADDING_BYTES(4);
u64_le hash_table_offset;
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_PADDING_BYTES(432);
};
static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size.");
static bool IsValidNca(const NcaHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
// TODO(DarkLordZach): Add support for encrypted.
class Nca final {
std::vector<FileSys::PartitionFilesystem> pfs;
std::vector<u64> pfs_offset;
u64 romfs_offset = 0;
u64 romfs_size = 0;
boost::optional<u8> exefs_id = boost::none;
FileUtil::IOFile file;
std::string path;
u64 GetExeFsFileOffset(const std::string& file_name) const;
u64 GetExeFsFileSize(const std::string& file_name) const;
public:
ResultStatus Load(FileUtil::IOFile&& file, std::string path);
FileSys::PartitionFilesystem GetPfs(u8 id) const;
u64 GetRomFsOffset() const;
u64 GetRomFsSize() const;
std::vector<u8> GetExeFsFile(const std::string& file_name);
};
static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) {
// According to switchbrew, an exefs must only contain these two files:
return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0;
}
ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) {
file = std::move(in_file);
path = in_path;
file.Seek(0, SEEK_SET);
std::array<u8, sizeof(NcaHeader)> header_array{};
if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader)))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
NcaHeader header{};
std::memcpy(&header, header_array.data(), sizeof(NcaHeader));
if (!IsValidNca(header))
return ResultStatus::ErrorInvalidFormat;
int number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NcaSectionTableEntry entry) { return entry.media_offset > 0; });
for (int i = 0; i < number_sections; ++i) {
// Seek to beginning of this section.
file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
std::array<u8, sizeof(NcaSectionHeaderBlock)> array{};
if (sizeof(NcaSectionHeaderBlock) !=
file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock)))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
NcaSectionHeaderBlock block{};
std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock));
if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) {
romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
romfs_size =
header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset;
} else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) {
Pfs0Superblock sb{};
// Seek back to beginning of this section.
file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock)))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
sb.pfs0_header_offset;
FileSys::PartitionFilesystem npfs{};
ResultStatus status = npfs.Load(path, offset);
if (status == ResultStatus::Success) {
pfs.emplace_back(std::move(npfs));
pfs_offset.emplace_back(offset);
}
}
}
for (size_t i = 0; i < pfs.size(); ++i) {
if (IsPfsExeFs(pfs[i]))
exefs_id = i;
}
return ResultStatus::Success;
}
FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const {
return pfs[id];
}
u64 Nca::GetExeFsFileOffset(const std::string& file_name) const {
if (exefs_id == boost::none)
return 0;
return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id];
}
u64 Nca::GetExeFsFileSize(const std::string& file_name) const {
if (exefs_id == boost::none)
return 0;
return pfs[*exefs_id].GetFileSize(file_name);
}
u64 Nca::GetRomFsOffset() const {
return romfs_offset;
}
u64 Nca::GetRomFsSize() const {
return romfs_size;
}
std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) {
std::vector<u8> out(GetExeFsFileSize(file_name));
file.Seek(GetExeFsFileOffset(file_name), SEEK_SET);
file.ReadBytes(out.data(), GetExeFsFileSize(file_name));
return out;
}
AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath)
: AppLoader(std::move(file)), filepath(std::move(filepath)) {}
FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) {
file.Seek(0, SEEK_SET);
std::array<u8, 0x400> header_enc_array{};
if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400))
FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
// TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
FileSys::NCAHeader header{};
if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header))
return FileType::Error;
// TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
NcaHeader header{};
std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader));
if (IsValidNca(header) && header.content_type == NcaContentType::Program)
if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program)
return FileType::NCA;
return FileType::Error;
@@ -231,17 +39,22 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
if (!file.IsOpen()) {
return ResultStatus::Error;
}
nca = std::make_unique<Nca>();
ResultStatus result = nca->Load(std::move(file), filepath);
nca = std::make_unique<FileSys::NCA>(file);
ResultStatus result = nca->GetStatus();
if (result != ResultStatus::Success) {
return result;
}
result = metadata.Load(nca->GetExeFsFile("main.npdm"));
if (nca->GetType() != FileSys::NCAContentType::Program)
return ResultStatus::ErrorInvalidFormat;
auto exefs = nca->GetExeFS();
if (exefs == nullptr)
return ResultStatus::ErrorInvalidFormat;
result = metadata.Load(exefs->GetFile("main.npdm"));
if (result != ResultStatus::Success) {
return result;
}
@@ -256,9 +69,12 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr);
next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
} else {
next_load_addr = load_addr;
}
@@ -272,29 +88,18 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
if (nca->GetRomFsSize() > 0)
Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
Service::FileSystem::Type::RomFS);
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) {
if (nca->GetRomFsSize() == 0) {
LOG_DEBUG(Loader, "No RomFS available");
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
if (nca == nullptr || nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0)
return ResultStatus::ErrorNotUsed;
}
romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
offset = nca->GetRomFsOffset();
size = nca->GetRomFsSize();
LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
dir = nca->GetRomFS();
return ResultStatus::Success;
}

View File

@@ -6,44 +6,39 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/loader.h"
namespace Loader {
class Nca;
/// Loads an NCA file
class AppLoader_NCA final : public AppLoader {
public:
AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath);
explicit AppLoader_NCA(FileSys::VirtualFile file);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @param file std::shared_ptr<VfsFile> open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file, filepath);
return IdentifyType(file);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
u64& size) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
~AppLoader_NCA();
private:
std::string filepath;
FileSys::ProgramMetadata metadata;
std::unique_ptr<Nca> nca;
std::unique_ptr<FileSys::NCA> nca;
};
} // namespace Loader

View File

@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nro.h"
@@ -47,14 +48,12 @@ struct ModHeader {
};
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
AppLoader_NRO::AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath)
: AppLoader(std::move(file)), filepath(std::move(filepath)) {}
AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {}
FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file, const std::string&) {
FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
// Read NSO header
NroHeader nro_header{};
file.Seek(0, SEEK_SET);
if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
return FileType::Error;
}
if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -67,16 +66,10 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
FileUtil::IOFile file(path, "rb");
if (!file.IsOpen()) {
return {};
}
bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
// Read NSO header
NroHeader nro_header{};
file.Seek(0, SEEK_SET);
if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
return {};
}
if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -85,12 +78,11 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
program_image.resize(PageAlignSize(nro_header.file_size));
file.Seek(0, SEEK_SET);
file.ReadBytes(program_image.data(), nro_header.file_size);
std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
if (program_image.size() != PageAlignSize(nro_header.file_size))
return {};
for (int i = 0; i < nro_header.segments.size(); ++i) {
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
codeset->segments[i].addr = nro_header.segments[i].offset;
codeset->segments[i].offset = nro_header.segments[i].offset;
codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size);
@@ -111,10 +103,13 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
codeset->name = path;
codeset->name = file->GetName();
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
// Register module with GDBStub
GDBStub::RegisterModule(codeset->name, load_base, load_base);
return true;
}
@@ -122,14 +117,11 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
if (!file.IsOpen()) {
return ResultStatus::Error;
}
// Load NRO
static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
if (!LoadNro(filepath, base_addr)) {
if (!LoadNro(file, base_addr)) {
return ResultStatus::ErrorInvalidFormat;
}

View File

@@ -15,26 +15,23 @@ namespace Loader {
/// Loads an NRO file
class AppLoader_NRO final : public AppLoader, Linker {
public:
AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath);
AppLoader_NRO(FileSys::VirtualFile file);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @param file std::shared_ptr<VfsFile> open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file, filepath);
return IdentifyType(file);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
private:
bool LoadNro(const std::string& path, VAddr load_base);
std::string filepath;
bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
};
} // namespace Loader

View File

@@ -10,6 +10,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nso.h"
@@ -37,6 +38,7 @@ struct NsoHeader {
std::array<u32_le, 3> segments_compressed_size;
};
static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
struct ModHeader {
u32_le magic;
@@ -49,15 +51,11 @@ struct ModHeader {
};
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
AppLoader_NSO::AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath)
: AppLoader(std::move(file)), filepath(std::move(filepath)) {}
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&) {
FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
u32 magic = 0;
file.Seek(0, SEEK_SET);
if (1 != file.ReadArray<u32>(&magic, 1)) {
return FileType::Error;
}
file->ReadObject(&magic);
if (Common::MakeMagic('N', 'S', 'O', '0') == magic) {
return FileType::NSO;
@@ -68,8 +66,7 @@ FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&)
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NsoSegmentHeader& header) {
std::vector<u8> uncompressed_data;
uncompressed_data.resize(header.size);
std::vector<u8> uncompressed_data(header.size);
const int bytes_uncompressed = LZ4_decompress_safe(
reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()), compressed_data.size(), header.size);
@@ -82,8 +79,7 @@ static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header,
size_t compressed_size) {
std::vector<u8> compressed_data;
compressed_data.resize(compressed_size);
std::vector<u8> compressed_data(compressed_size);
file.Seek(header.offset, SEEK_SET);
if (compressed_size != file.ReadBytes(compressed_data.data(), compressed_size)) {
@@ -98,13 +94,16 @@ static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data,
VAddr load_base) {
if (file_data.size() < sizeof(NsoHeader))
VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
if (file == nullptr)
return {};
NsoHeader nso_header;
std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader));
if (file->GetSize() < sizeof(NsoHeader))
return {};
NsoHeader nso_header{};
if (sizeof(NsoHeader) != file->ReadObject(&nso_header))
return {};
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return {};
@@ -112,10 +111,9 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]);
for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j)
compressed_data[j] = file_data[nso_header.segments[i].offset + j];
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
const std::vector<u8> compressed_data =
file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
@@ -143,64 +141,12 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
program_image.resize(image_size);
// Load codeset for current process
codeset->name = name;
codeset->name = file->GetName();
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
return load_base + image_size;
}
VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
FileUtil::IOFile file(path, "rb");
if (!file.IsOpen()) {
return {};
}
// Read NSO header
NsoHeader nso_header{};
file.Seek(0, SEEK_SET);
if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) {
return {};
}
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
return {};
}
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> data =
ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]);
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
codeset->segments[i].addr = nso_header.segments[i].location;
codeset->segments[i].offset = nso_header.segments[i].location;
codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
}
// MOD header pointer is at .text offset + 4
u32 module_offset;
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
// Read MOD header
ModHeader mod_header{};
// Default .bss to size in segment header if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
codeset->data.size += bss_size;
const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
program_image.resize(image_size);
// Load codeset for current process
codeset->name = path;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
// Register module with GDBStub
GDBStub::RegisterModule(codeset->name, load_base, load_base);
return load_base + image_size;
}
@@ -209,13 +155,10 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
if (!file.IsOpen()) {
return ResultStatus::Error;
}
// Load module
LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR);
LoadModule(file, Memory::PROCESS_IMAGE_VADDR);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR);
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;

View File

@@ -15,29 +15,22 @@ namespace Loader {
/// Loads an NSO file
class AppLoader_NSO final : public AppLoader, Linker {
public:
AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath);
explicit AppLoader_NSO(FileSys::VirtualFile file);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @param file std::shared_ptr<VfsFile> open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file, filepath);
return IdentifyType(file);
}
static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data,
VAddr load_base);
static VAddr LoadModule(const std::string& path, VAddr load_base);
static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base);
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
private:
std::string filepath;
};
} // namespace Loader

View File

@@ -5,6 +5,8 @@
#include <algorithm>
#include <array>
#include <cstring>
#include <utility>
#include <boost/optional.hpp>
#include "common/assert.h"
#include "common/common_types.h"
@@ -74,7 +76,7 @@ void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
SpecialRegion region{SpecialRegion::Type::IODevice, mmio_handler};
SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)};
page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
}
@@ -89,13 +91,13 @@ void UnmapRegion(PageTable& page_table, VAddr base, u64 size) {
void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
SpecialRegion region{SpecialRegion::Type::DebugHook, hook};
SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
}
void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
SpecialRegion region{SpecialRegion::Type::DebugHook, hook};
SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region}));
}
@@ -115,11 +117,6 @@ static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_tabl
return result;
}
static std::set<MemoryHookPointer> GetSpecialHandlers(VAddr vaddr, u64 size) {
const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
return GetSpecialHandlers(page_table, vaddr, size);
}
/**
* Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
* using a VMA from the current process
@@ -587,8 +584,6 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size
size_t page_index = dest_addr >> PAGE_BITS;
size_t page_offset = dest_addr & PAGE_MASK;
static const std::array<u8, PAGE_SIZE> zeros = {};
while (remaining_size > 0) {
const size_t copy_amount =
std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size);

View File

@@ -3,7 +3,6 @@ add_executable(tests
core/arm/arm_test_common.cpp
core/arm/arm_test_common.h
core/core_timing.cpp
core/file_sys/path_parser.cpp
core/memory/memory.cpp
glad.cpp
tests.cpp

View File

@@ -1,38 +0,0 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <catch.hpp>
#include "common/file_util.h"
#include "core/file_sys/path_parser.h"
namespace FileSys {
TEST_CASE("PathParser", "[core][file_sys]") {
REQUIRE(!PathParser(Path(std::vector<u8>{})).IsValid());
REQUIRE(!PathParser(Path("a")).IsValid());
REQUIRE(!PathParser(Path("/|")).IsValid());
REQUIRE(PathParser(Path("/a")).IsValid());
REQUIRE(!PathParser(Path("/a/b/../../c/../../d")).IsValid());
REQUIRE(PathParser(Path("/a/b/../c/../../d")).IsValid());
REQUIRE(PathParser(Path("/")).IsRootDirectory());
REQUIRE(!PathParser(Path("/a")).IsRootDirectory());
REQUIRE(PathParser(Path("/a/..")).IsRootDirectory());
}
TEST_CASE("PathParser - Host file system", "[core][file_sys]") {
std::string test_dir = "./test";
FileUtil::CreateDir(test_dir);
FileUtil::CreateDir(test_dir + "/z");
FileUtil::CreateEmptyFile(test_dir + "/a");
REQUIRE(PathParser(Path("/a")).GetHostStatus(test_dir) == PathParser::FileFound);
REQUIRE(PathParser(Path("/b")).GetHostStatus(test_dir) == PathParser::NotFound);
REQUIRE(PathParser(Path("/z")).GetHostStatus(test_dir) == PathParser::DirectoryFound);
REQUIRE(PathParser(Path("/a/c")).GetHostStatus(test_dir) == PathParser::FileInPath);
REQUIRE(PathParser(Path("/b/c")).GetHostStatus(test_dir) == PathParser::PathNotFound);
FileUtil::DeleteDirRecursively(test_dir);
}
} // namespace FileSys

View File

@@ -520,7 +520,18 @@ public:
u32 enable[NumRenderTargets];
} blend;
INSERT_PADDING_WORDS(0xB);
struct {
u32 enable;
u32 front_op_fail;
u32 front_op_zfail;
u32 front_op_zpass;
u32 front_func_func;
u32 front_func_ref;
u32 front_func_mask;
u32 front_mask;
} stencil;
INSERT_PADDING_WORDS(0x3);
union {
BitField<4, 1, u32> triangle_rast_flip;
@@ -556,7 +567,17 @@ public:
}
} tic;
INSERT_PADDING_WORDS(0x21);
INSERT_PADDING_WORDS(0x5);
struct {
u32 enable;
u32 back_op_fail;
u32 back_op_zfail;
u32 back_op_zpass;
u32 back_func_func;
} stencil_two_side;
INSERT_PADDING_WORDS(0x17);
union {
BitField<2, 1, u32> coord_origin;
@@ -851,10 +872,12 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
ASSERT_REG_POSITION(blend, 0x4CF);
ASSERT_REG_POSITION(stencil, 0x4E0);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(stencil_two_side, 0x565);
ASSERT_REG_POSITION(point_coord_replace, 0x581);
ASSERT_REG_POSITION(code_address, 0x582);
ASSERT_REG_POSITION(draw, 0x585);

View File

@@ -67,6 +67,7 @@ struct FramebufferConfig {
using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
TransformFlags transform_flags;
MathUtil::Rectangle<int> crop_rect;
};
namespace Engines {

View File

@@ -15,6 +15,7 @@
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/hle/kernel/process.h"
#include "core/settings.h"
#include "video_core/engines/maxwell_3d.h"
@@ -22,6 +23,7 @@
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/maxwell_to_gl.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/video_core.h"
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using PixelFormat = SurfaceParams::PixelFormat;
@@ -126,8 +128,8 @@ RasterizerOpenGL::~RasterizerOpenGL() {
std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
GLintptr buffer_offset) {
MICROPROFILE_SCOPE(OpenGL_VAO);
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& memory_manager = Core::System().GetInstance().GPU().memory_manager;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager;
state.draw.vertex_array = hw_vao.handle;
state.draw.vertex_buffer = stream_buffer->GetHandle();
@@ -182,7 +184,7 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
}
static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program) {
auto& gpu = Core::System().GetInstance().GPU().Maxwell3D();
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
// Fetch program code from memory
GLShader::ProgramCode program_code;
@@ -205,7 +207,7 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
}
};
auto& gpu = Core::System().GetInstance().GPU().Maxwell3D();
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
@@ -295,7 +297,7 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
}
size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
size_t size = 0;
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -320,10 +322,7 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb,
bool using_depth_fb) {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
// Sync the depth test state before configuring the framebuffer surfaces.
SyncDepthTestState();
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(bunnei): Implement this
const bool has_stencil = false;
@@ -375,7 +374,7 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
}
void RasterizerOpenGL::Clear() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
bool use_color_fb = false;
bool use_depth_fb = false;
@@ -389,11 +388,20 @@ void RasterizerOpenGL::Clear() {
if (regs.clear_buffers.Z) {
clear_mask |= GL_DEPTH_BUFFER_BIT;
use_depth_fb = true;
// Always enable the depth write when clearing the depth buffer. The depth write mask is
// ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true.
state.depth.test_enabled = true;
state.depth.write_mask = GL_TRUE;
state.depth.test_func = GL_ALWAYS;
state.Apply();
}
if (clear_mask == 0)
return;
ScopeAcquireGLContext acquire_context;
auto [dirty_color_surface, dirty_depth_surface] =
ConfigureFramebuffers(use_color_fb, use_depth_fb);
@@ -418,11 +426,14 @@ void RasterizerOpenGL::DrawArrays() {
return;
MICROPROFILE_SCOPE(OpenGL_Drawing);
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
ScopeAcquireGLContext acquire_context;
auto [dirty_color_surface, dirty_depth_surface] =
ConfigureFramebuffers(true, regs.zeta.Address() != 0);
SyncDepthTestState();
SyncBlendState();
SyncCullMode();
@@ -462,7 +473,7 @@ void RasterizerOpenGL::DrawArrays() {
// If indexed mode, copy the index buffer
GLintptr index_buffer_offset = 0;
if (is_indexed) {
const auto& memory_manager = Core::System().GetInstance().GPU().memory_manager;
const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager;
const boost::optional<VAddr> index_data_addr{
memory_manager->GpuToCpuAddress(regs.index_array.StartAddress())};
Memory::ReadBlock(*index_data_addr, offseted_buffer, index_buffer_size);
@@ -764,7 +775,7 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
}
void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left;
@@ -782,9 +793,11 @@ void RasterizerOpenGL::SyncClipCoef() {
}
void RasterizerOpenGL::SyncCullMode() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
state.cull.enabled = regs.cull.enabled != 0;
// 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;
if (state.cull.enabled) {
state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
@@ -814,7 +827,7 @@ void RasterizerOpenGL::SyncDepthOffset() {
}
void RasterizerOpenGL::SyncDepthTestState() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
state.depth.test_enabled = regs.depth_test_enable != 0;
state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
@@ -826,7 +839,7 @@ void RasterizerOpenGL::SyncDepthTestState() {
}
void RasterizerOpenGL::SyncBlendState() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(Subv): Support more than just render target 0.
state.blend.enabled = regs.blend.enable[0] != 0;

View File

@@ -105,6 +105,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{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
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8
// DepthStencil formats
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
@@ -112,6 +113,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
false}, // S8Z24
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, ComponentType::UNorm,
false}, // Z16
}};
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
@@ -194,8 +197,9 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::Z24S8>,
MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
MortonCopy<true, PixelFormat::Z16>,
};
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
@@ -215,10 +219,12 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::ABGR8>,
nullptr,
MortonCopy<false, PixelFormat::G8R8>,
MortonCopy<false, PixelFormat::Z24S8>,
MortonCopy<false, PixelFormat::S8Z24>,
MortonCopy<false, PixelFormat::Z32F>,
MortonCopy<false, PixelFormat::Z16>,
};
// Allocate an uninitialized texture of appropriate size and format for the surface
@@ -271,9 +277,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
S8Z24 input_pixel{};
Z24S8 output_pixel{};
const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
const size_t offset{y * width + x};
const size_t offset{bpp * (y * width + x)};
std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24));
output_pixel.s8.Assign(input_pixel.s8);
output_pixel.z24.Assign(input_pixel.z24);
@@ -281,6 +288,19 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
}
}
}
static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8)};
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
const size_t offset{bpp * (y * width + x)};
const u8 temp{data[offset]};
data[offset] = data[offset + 1];
data[offset + 1] = temp;
}
}
}
/**
* Helper function to perform software conversion (as needed) when loading a buffer from Switch
* memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
@@ -301,6 +321,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
// Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
ConvertS8Z24ToZ24S8(data, width, height);
break;
case PixelFormat::G8R8:
// Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8.
ConvertG8R8ToR8G8(data, width, height);
break;
}
}
@@ -480,7 +505,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(bunnei): This is hard corded to use just the first render buffer
LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!");

View File

@@ -37,13 +37,15 @@ struct SurfaceParams {
DXN1 = 11, // This is also known as BC4
BC7U = 12,
ASTC_2D_4X4 = 13,
G8R8 = 14,
MaxColorFormat,
// DepthStencil formats
Z24S8 = 14,
S8Z24 = 15,
Z32F = 16,
Z24S8 = 15,
S8Z24 = 16,
Z32F = 17,
Z16 = 18,
MaxDepthStencilFormat,
@@ -95,9 +97,11 @@ struct SurfaceParams {
4, // DXN1
4, // BC7U
4, // ASTC_2D_4X4
1, // G8R8
1, // Z24S8
1, // S8Z24
1, // Z32F
1, // Z16
}};
ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
@@ -123,9 +127,11 @@ struct SurfaceParams {
64, // DXN1
128, // BC7U
32, // ASTC_2D_4X4
16, // G8R8
32, // Z24S8
32, // S8Z24
32, // Z32F
16, // Z16
}};
ASSERT(static_cast<size_t>(format) < bpp_table.size());
@@ -143,6 +149,8 @@ struct SurfaceParams {
return PixelFormat::Z24S8;
case Tegra::DepthFormat::Z32_FLOAT:
return PixelFormat::Z32F;
case Tegra::DepthFormat::Z16_UNORM:
return PixelFormat::Z16;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
@@ -181,6 +189,8 @@ struct SurfaceParams {
return PixelFormat::A1B5G5R5;
case Tegra::Texture::TextureFormat::R8:
return PixelFormat::R8;
case Tegra::Texture::TextureFormat::G8R8:
return PixelFormat::G8R8;
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
return PixelFormat::RGBA16F;
case Tegra::Texture::TextureFormat::BF10GF11RF11:
@@ -218,6 +228,8 @@ struct SurfaceParams {
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:
@@ -249,6 +261,8 @@ struct SurfaceParams {
return Tegra::DepthFormat::Z24_S8_UNORM;
case PixelFormat::Z32F:
return Tegra::DepthFormat::Z32_FLOAT;
case PixelFormat::Z16:
return Tegra::DepthFormat::Z16_UNORM;
default:
UNREACHABLE();
}
@@ -295,6 +309,7 @@ struct SurfaceParams {
static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
switch (format) {
case Tegra::DepthFormat::Z16_UNORM:
case Tegra::DepthFormat::S8_Z24_UNORM:
case Tegra::DepthFormat::Z24_S8_UNORM:
return ComponentType::UNorm;

View File

@@ -35,7 +35,7 @@ void SetShaderUniformBlockBindings(GLuint shader) {
} // namespace Impl
void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(bunnei): Support more than one viewport
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;

Some files were not shown because too many files have changed in this diff Show More