Compare commits

..

90 Commits

Author SHA1 Message Date
Zach Hilman
d10fc2d727 glue: Correct missing bytes in ApplicationLaunchParameter 2019-06-25 22:25:10 -04:00
Zach Hilman
d40a38df8d core: Keep track of ARPManager and register current application on boot 2019-06-24 19:27:35 -04:00
Zach Hilman
ce21973022 glue: Implement arp:w and arp:r services
These keep track of running process' launch properties and control properties and allows for issuing and reading them by process and title ID.
2019-06-24 19:26:16 -04:00
Zach Hilman
df3ee4f444 glue: Add errors for glue/arp services 2019-06-24 19:20:28 -04:00
Zach Hilman
4fab0d392b glue: Add scaffolding for bgtc:t and bgtc:sc services 2019-06-24 19:20:06 -04:00
Zach Hilman
9003e19797 arp: Move to glue services
Glue is the name of the sysmodule that contains both arp and bgtc.
2019-06-24 19:17:48 -04:00
Zach Hilman
5f8d2a2044 glue: Add manager to keep track of application registry
Manages mapping between title IDs and application launch and control properties.
2019-06-24 19:17:13 -04:00
Zach Hilman
db2e5e5fa6 registered_cache: Add getter to determine source slot in content provider union
Used to determine StorageId source for application data.
2019-06-24 19:10:17 -04:00
Zach Hilman
a468273221 patch_manager: Add getter for title version 2019-06-24 19:05:50 -04:00
Zach Hilman
221996a194 Merge pull request #2603 from WamWooWam/master
HexVectorToString -> HexToString
2019-06-22 13:26:56 -04:00
Thomas May
334e6370f9 Update reporter.cpp 2019-06-22 14:39:52 +01:00
bunnei
a9f3c54871 Merge pull request #2579 from ReinUsesLisp/fix-aoffi-test
gl_device: Fix TestVariableAoffi test
2019-06-21 15:28:55 -04:00
bunnei
69e490b303 Merge pull request #2602 from lioncash/cast
service/acc: Silence truncation warnings
2019-06-21 15:27:25 -04:00
bunnei
8f8fa82c83 Merge pull request #2575 from DarkLordZach/process-id-types
kernel: Differentiate kernel and user processes when picking ID
2019-06-21 15:25:50 -04:00
bunnei
e2f7933b3f Merge pull request #2546 from DarkLordZach/kips
loader, file_sys: Add support for parsing and loading KIP (Kernel Internal Process) files
2019-06-21 14:28:18 -04:00
bunnei
96412848a9 Merge pull request #2482 from DarkLordZach/prepo
core: Add detailed local reporting feature for development
2019-06-21 14:05:18 -04:00
bunnei
100ed88e15 Merge pull request #2291 from DarkLordZach/homebrew-testing
yuzu_tester: Add and implement testing utility for homebrew
2019-06-21 11:42:38 -04:00
Lioncash
82cd87f6c2 service/acc: Silence truncation warnings
The sanitizing function ensures that the returned type is always the
correct type. This eliminates warnings without extra casts.
2019-06-21 10:04:31 -04:00
bunnei
80a8456af8 Merge pull request #2596 from FernandoS27/revert-2590
Revert PR 2590.
2019-06-20 13:04:06 -04:00
Hexagon12
908a5a00c5 Merge pull request #2595 from jonsn0w/patch-1
Update content_archive.cpp
2019-06-20 10:03:24 +03:00
Fernando Sahmkow
26fcdf087d Revert PR 2590.
Even though it has been proven that IAudioRenderer:SystemEvent is 
actually an automatic event. The current implementation of such event is 
all thought to be manual. Thus it's implementation needs to be corrected 
when doing such change. As it is right now this PR introduced a series 
of regressions on softlocks on multiple games. Therefore, this pr 
reverts such change until a correct implementation is made.
2019-06-19 23:19:19 -04:00
bunnei
c28694d907 Merge pull request #2591 from lioncash/record
core: Remove unused CiTrace source files
2019-06-19 22:28:26 -04:00
bunnei
ca470890a3 Merge pull request #2590 from lioncash/event
service/audio/audren_u: Correct event reset type for the system event
2019-06-19 22:27:52 -04:00
Frederic L
5cef446f42 CMake: Get Git submodule dependencies via CMake (#2474)
* CMake: Get Git submodule dependencies via CMake

* CMakeLists: Fixed unintentional line break

* travis: Bring parity between linux-mingw and linux build script

* CMakeLists: Fixed typo in error message
2019-06-19 22:26:12 -04:00
jonsn0w
e78d069a81 Update content_archive.cpp
log clutter in debug logs when theres really no need
2019-06-19 22:01:41 -04:00
Zach Hilman
8893d63612 Merge pull request #2594 from FearlessTobi/very-important-change
yuzu/configure_input: Add missing space in window title
2019-06-19 19:15:37 -04:00
Tobias
0c64a6f0f2 Change to a more descriptive name 2019-06-19 23:55:13 +02:00
Tobias
c5b20a108d yuzu/configure_input: Add missing space in window name 2019-06-19 23:32:34 +02:00
Mat M
908ca1fc72 Merge pull request #2593 from deadmeu/patch-1
Added missing space between two words
2019-06-19 14:11:10 -04:00
Alex Subaric
f375e10411 Added missing space between two words
Added missing whitespace character between two words in the "Warning Missing Derivation Components" warning message box.
2019-06-20 02:42:56 +10:00
Zach Hilman
5c665fcc5b Merge pull request #2584 from ogniK5377/cadence
Impl'd IsUserAccountSwitchLocked, SetAudioOutVolume, GetAudioOutVolume & Partial impl of GetAccumulatedSuspendedTickChangedEvent
2019-06-19 10:29:42 -04:00
Lioncash
61d2498f00 core: Remove unused CiTrace source files
These source files have been unused for the entire lifecycle of the
project. They're a hold-over from Citra and only add to the build time
of the project, so they can be removed.

There's also likely no way this would ever work in yuzu in its current
form without revamping quite a bit of it, given how different the GPU on
the Switch is compared to the 3DS.
2019-06-18 16:57:59 -04:00
Lioncash
5799404b78 service/audio/audren_u: Correct event reset type for the system event
This is actually an auto-reset event in the audio service itself, not a
manual one.
2019-06-18 09:23:20 -04:00
bunnei
c7b5c245e1 Merge pull request #2562 from ReinUsesLisp/split-cbuf-upload
video_core/engines: Move ConstBufferInfo out of Maxwell3D
2019-06-17 22:35:04 -04:00
David Marcec
6ca20ad7ba Addressed issues 2019-06-17 08:17:26 +10:00
David Marcec
50e3269f3b Signalled accumulated_suspended_tick_changed_event on creation based on RE 2019-06-16 22:18:54 +10:00
David Marcec
5fb6781c61 Cleanup 2019-06-16 20:18:35 +10:00
David Marcec
335127af69 Impl'd IsUserAccountSwitchLocked, SetAudioOutVolume, GetAudioOutVolume & Partial impl of GetAccumulatedSuspendedTickChangedEvent
IPC-100 was changed to InitializeApplicationInfoOld instead of InitializeApplicationInfo. IPC-150 makes an indentical call to IPC-100 however does extra processing. They should not have the same name as it's quite confusing to debug.
2019-06-16 19:06:33 +10:00
Zach Hilman
c0e7b91145 Merge pull request #2538 from ReinUsesLisp/ssy-pbk
shader: Split SSY and PBK stack
2019-06-15 20:30:13 -04:00
Zach Hilman
c140b6ae2c Merge pull request #2581 from lioncash/hex
common/hex_util: Combine HexVectorToString() and HexArrayToString()
2019-06-15 16:47:13 -04:00
bunnei
0360c40e90 Merge pull request #2582 from lioncash/reserved
file_sys/ips_layer: Remove unnecessary reserve() call
2019-06-14 11:24:18 -04:00
Zach Hilman
a9521c983b Merge pull request #2580 from lioncash/redundant
kernel/vm_manager: Remove redundant Reset call in destructor
2019-06-12 19:26:07 -04:00
Zach Hilman
0f08f2d562 Merge pull request #2577 from lioncash/fs
file_sys/card_image: Minor cleanup
2019-06-12 19:23:33 -04:00
Lioncash
18c1d91920 file_sys/ips_layer: Remove unnecessary reserve() call
Given 'replace' is assigned to on the following line, this isn't
necessary, given the underlying data is going to be overwritten
entirely.
2019-06-12 18:12:45 -04:00
Lioncash
969cd6dc1d common/hex_util: Reserve std::string memory ahead of time
Avoids potentially performing multiple reallocations (depending on the
size of the input data) by reserving the necessary amount of memory
ahead of time.

This is trivially doable, so there's no harm in it.
2019-06-12 17:54:11 -04:00
Lioncash
a62088539e common/hex_util: Combine HexVectorToString() and HexArrayToString()
These can be generified together by using a concept type to designate
them. This also has the benefit of not making copies of potentially very
large arrays.
2019-06-12 17:54:05 -04:00
Lioncash
c7daddb715 file_sys/card_image: Remove obsolete TODO
We already support Rev 1+.
2019-06-12 16:52:19 -04:00
Lioncash
0af3b4d9f4 kernel/vm_manager: Remove redundant Reset call in destructor
This is performing more work than would otherwise be necessary during
VMManager's destruction. All we actually want to occur in this scenario
is for any allocated memory to be freed, which will happen automatically
as the VMManager instance goes out of scope.

Anything else being done is simply unnecessary work.
2019-06-12 16:10:00 -04:00
ReinUsesLisp
ee81fb94cd gl_device: Fix TestVariableAoffi test
This test is intended to be invalid GLSL, but it was being invalid in
two points instead of one. The intention is to use a non-immediate
parameter in a textureOffset like function.

The problem is that this shader was being compiled as a separable
shader object and the text was writting to gl_Position without a
redeclaration, being invalid GLSL.

Address that issue by using a user-defined output attribute.
2019-06-11 23:02:50 -03:00
bunnei
7e2bcf04b4 Merge pull request #2578 from lioncash/cnmt
file_sys/nca_metadata: Update CNMT structures
2019-06-11 21:13:05 -04:00
bunnei
f981efdf8d Merge pull request #2572 from FernandoS27/gpu-mem
GPUVM: Correct GPU VM virtual address space
2019-06-11 21:09:57 -04:00
Lioncash
a602bcaaf8 file_sys/nca_metadata: Update CNMT structures
Names a few more entries in relevant structures. Information based off
SwitchBrew and my own RE.
2019-06-10 23:51:06 -04:00
Lioncash
7bdef6106e file_sys/card_image: Deduplicate casts within AddNCAFromPartition()
Makes for nicer reading.
2019-06-10 23:27:14 -04:00
Lioncash
81d361d9f8 file_sys/card_image: Make bracing consistent
Makes for more consistent reading.
2019-06-10 23:27:13 -04:00
Lioncash
e34368249f file_sys/card_image: Assign collapsed NCA contents directly to ncas member
Same thing, significantly less noisy.
2019-06-10 23:27:13 -04:00
Lioncash
288d027e89 file_sys/card_image: Deduplicate type cast
Same thing, less duplication. We can also std::move raw into the
PartitionFilesystem constructor.
2019-06-10 23:27:05 -04:00
Lioncash
825ffd7b1f file_sys/card_image: Get rid of a magic number
We can just use the size of the array to dehardcode it.
2019-06-10 22:58:08 -04:00
Lioncash
bf35138d1d file_sys/card_image: Use std::array deduction guides
Same thing, less code.
2019-06-10 22:57:53 -04:00
Zach Hilman
3a26b49c2c yuzutest: Add minor comments 2019-06-10 00:31:54 -04:00
Zach Hilman
fc0bf91a96 kernel: Differentiate kernel and user processes when picking ID
This allows kernel internal type processes to be assigned IDs in the KIP range while userland processes are assigned in the user range.
2019-06-10 00:28:33 -04:00
Zach Hilman
511bf3435d yuzu_tester: Display results in table format 2019-06-10 00:03:11 -04:00
Zach Hilman
f279e792b7 yuzutest: Support multiple tests per executable 2019-06-10 00:03:11 -04:00
Zach Hilman
5ddc9cede5 yuzu_tester: Add 'yuzutest' service 2019-06-10 00:03:11 -04:00
Zach Hilman
ae5a46256e yuzu_tester: Add SDL2-based EmuWindow that doesn't show the window 2019-06-10 00:03:11 -04:00
Zach Hilman
819006d0d3 yuzu_tester: Use config, icon, and main from yuzu-cmd 2019-06-10 00:03:11 -04:00
Zach Hilman
c508a8d82a yuzu_tester: Add project subdirectory 2019-06-10 00:03:11 -04:00
Fernando Sahmkow
f79823fda7 GPUVM: Correct GPU VM virtual address space 2019-06-09 17:47:15 -04:00
ReinUsesLisp
5669ff3cbd gl_rasterizer: Remove unused parameters in descriptor uploads 2019-06-07 19:52:16 -03:00
ReinUsesLisp
2f2a61887a video_core/engines: Move ConstBufferInfo out of Maxwell3D 2019-06-07 19:47:15 -03:00
ReinUsesLisp
fe8e6618f2 shader: Split SSY and PBK stack
Hardware testing revealed that SSY and PBK push to a different stack,
allowing code like this:

        SSY label1;
        PBK label2;
        SYNC;
label1: PBK;
label2: EXIT;
2019-06-07 02:18:27 -03:00
Zach Hilman
9db119f8a2 kernel_executable: Optimize BLZ decompression 2019-06-06 19:20:15 -04:00
Zach Hilman
c417b4fe28 game_list: Accept *.kip as a file extension of executables 2019-06-05 00:33:05 -04:00
Zach Hilman
ce9f7ac4f2 loader: Add recognition for KIP file type 2019-06-05 00:22:07 -04:00
Zach Hilman
a76bd49268 loader: Add KIP and INI file parser-specific errors 2019-06-05 00:21:44 -04:00
Zach Hilman
c3548967b1 loader: Add AppLoader_KIP for KIP files 2019-06-05 00:21:17 -04:00
Zach Hilman
b550a01f74 program_metadata: Add function to load meta from raw parameters
Needed for KIP loading as KIPs do not have an NPDM but do have the essential parts of the data within.
2019-06-05 00:20:26 -04:00
Zach Hilman
0f37096820 partition_data_manager: Remove KIP processing and use FileSys
Previously, this TU contained the necessary headers to parse KIP/INI but now it should just use the FileSys class.
2019-06-05 00:19:29 -04:00
Zach Hilman
421c3e831a file_sys: Add classes to parse KIP1 and INI1 files 2019-06-05 00:18:25 -04:00
Zach Hilman
b77fde7c5c loader: Move NSO module tracking to AppLoader
Also cleanup of general stuff
2019-05-26 11:40:46 -04:00
Zach Hilman
cdf52b9374 prepo: Save reports from PlayReport service
Logs a lot of seemingly innocuous telemetry games generate.
2019-05-25 16:09:20 -04:00
Zach Hilman
34e9736910 fatal: Save report on fatal:u call
Matches offical behavior with creport and replaces old log/text based report system.
2019-05-25 16:09:20 -04:00
Zach Hilman
fc309bf893 service: Save report on unimplemented function call 2019-05-25 16:09:20 -04:00
Zach Hilman
24392c8ec8 applets/error: Save report on error applet
This matches official behavior with the erpt/eclct/eupld service chain.
2019-05-25 16:09:20 -04:00
Zach Hilman
bdc47693f1 applets: Save report on stubbed applet
This also reworks the applet data storage to be peekable.
2019-05-25 16:09:20 -04:00
Zach Hilman
186db894b9 svc: Save report on call to svcBreak 2019-05-25 16:09:20 -04:00
Zach Hilman
2dde8f5cfe core: Add Reporter class to take/save reports 2019-05-25 16:09:20 -04:00
Zach Hilman
6f0ee45b5c qt: Make UI option for 'Reporting Services' temporary
Reports are unnecessary for normal users and this is to prevent 'power' users from enabling the feature by accident.
2019-05-25 16:09:20 -04:00
Zach Hilman
658d2ee97d settings: Add 'Reporting Services' config option
Full enable/disable for all reports.
2019-05-25 16:09:20 -04:00
Zach Hilman
5574be21cc arm_interface: Expand backtrace generation
Returns results as a vector of entries for further processing. Logs addresses, offsets, and mangled name.
2019-05-25 16:06:53 -04:00
Zach Hilman
2179ad7483 core: Track load offsets of NSO modules
Needed for backtrace decomposition
2019-05-25 16:06:53 -04:00
124 changed files with 3493 additions and 899 deletions

View File

@@ -1,3 +1,4 @@
#!/bin/bash -ex
mkdir "$HOME/.ccache" || true
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh
mkdir -p "$HOME/.ccache"
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-mingw /bin/bash /yuzu/.travis/linux-mingw/docker.sh

View File

@@ -7,6 +7,18 @@ include(CMakeDependentOption)
project(yuzu)
# Get Git submodule dependencies
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, "
"please checkout submodules manually with \"git submodule update --init --recursive\"")
endif()
endif()
# Set bundled sdl2/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
@@ -33,22 +45,6 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
endif()
# Sanity check : Check that all submodules are present
# =======================================================================
function(check_submodules_present)
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
foreach(module ${gitmodules})
string(REGEX REPLACE "path *= *" "" module ${module})
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
message(FATAL_ERROR "Git submodule ${module} not found. "
"Please run: git submodule update --init --recursive")
endif()
endforeach()
endfunction()
check_submodules_present()
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY)

View File

@@ -88,6 +88,7 @@ add_subdirectory(tests)
if (ENABLE_SDL2)
add_subdirectory(yuzu_cmd)
add_subdirectory(yuzu_tester)
endif()
if (ENABLE_QT)

View File

@@ -51,6 +51,10 @@ void Stream::Stop() {
UNIMPLEMENTED();
}
void Stream::SetVolume(float volume) {
game_volume = volume;
}
Stream::State Stream::GetState() const {
return state;
}
@@ -62,8 +66,8 @@ s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
return Core::Timing::usToCycles(us);
}
static void VolumeAdjustSamples(std::vector<s16>& samples) {
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)};
if (volume == 1.0f) {
return;
@@ -97,7 +101,7 @@ void Stream::PlayNextBuffer() {
active_buffer = queued_buffers.front();
queued_buffers.pop();
VolumeAdjustSamples(active_buffer->GetSamples());
VolumeAdjustSamples(active_buffer->GetSamples(), game_volume);
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());

View File

@@ -61,6 +61,12 @@ public:
/// Returns a vector of recently released buffers specified by tag
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
void SetVolume(float volume);
float GetVolume() const {
return game_volume;
}
/// Returns true if the stream is currently playing
bool IsPlaying() const {
return state == State::Playing;
@@ -94,6 +100,7 @@ private:
u32 sample_rate; ///< Sample rate of the stream
Format format; ///< Format of the stream
float game_volume = 1.0f; ///< The volume the game currently has set
ReleaseCallback release_callback; ///< Buffer release callback for the stream
State state{State::Stopped}; ///< Playback state of the stream
Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream

View File

@@ -30,13 +30,6 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
std::string out;
for (u8 c : vector)
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
return out;
}
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
if (len != 32) {
LOG_ERROR(Common,

View File

@@ -7,6 +7,7 @@
#include <array>
#include <cstddef>
#include <string>
#include <type_traits>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
@@ -30,13 +31,20 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
template <typename ContiguousContainer>
std::string HexToString(const ContiguousContainer& data, bool upper = true) {
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
"Underlying type within the contiguous container must be u8.");
constexpr std::size_t pad_width = 2;
template <std::size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;
for (u8 c : array)
out.reserve(std::size(data) * pad_width);
for (const u8 c : data) {
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
}
return out;
}

View File

@@ -45,6 +45,8 @@ add_library(core STATIC
file_sys/fsmitm_romfsbuild.h
file_sys/ips_layer.cpp
file_sys/ips_layer.h
file_sys/kernel_executable.cpp
file_sys/kernel_executable.h
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
@@ -207,8 +209,6 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/arp/arp.cpp
hle/service/arp/arp.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp
@@ -274,6 +274,15 @@ add_library(core STATIC
hle/service/friend/friend.h
hle/service/friend/interface.cpp
hle/service/friend/interface.h
hle/service/glue/arp.cpp
hle/service/glue/arp.h
hle/service/glue/bgtc.cpp
hle/service/glue/bgtc.h
hle/service/glue/errors.h
hle/service/glue/glue.cpp
hle/service/glue/glue.h
hle/service/glue/manager.cpp
hle/service/glue/manager.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
@@ -440,6 +449,8 @@ add_library(core STATIC
loader/deconstructed_rom_directory.h
loader/elf.cpp
loader/elf.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp
loader/loader.h
loader/nax.cpp
@@ -459,19 +470,18 @@ add_library(core STATIC
memory_setup.h
perf_stats.cpp
perf_stats.h
reporter.cpp
reporter.h
settings.cpp
settings.h
telemetry_session.cpp
telemetry_session.h
tracer/citrace.h
tracer/recorder.cpp
tracer/recorder.h
)
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)

View File

@@ -2,26 +2,213 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
#include <optional>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/loader/loader.h"
#include "core/memory.h"
namespace Core {
void ARM_Interface::LogBacktrace() const {
VAddr fp = GetReg(29);
VAddr lr = GetReg(30);
const VAddr sp = GetReg(13);
const VAddr pc = GetPC();
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
namespace {
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
enum class ELFSymbolType : u8 {
None = 0,
Object = 1,
Function = 2,
Section = 3,
File = 4,
Common = 5,
TLS = 6,
};
enum class ELFSymbolBinding : u8 {
Local = 0,
Global = 1,
Weak = 2,
};
enum class ELFSymbolVisibility : u8 {
Default = 0,
Internal = 1,
Hidden = 2,
Protected = 3,
};
struct ELFSymbol {
u32 name_index;
union {
u8 info;
BitField<0, 4, ELFSymbolType> type;
BitField<4, 4, ELFSymbolBinding> binding;
};
ELFSymbolVisibility visibility;
u16 sh_index;
u64 value;
u64 size;
};
static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
Symbols GetSymbols(VAddr text_offset) {
const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
return {};
}
const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
VAddr string_table_offset{};
VAddr symbol_table_offset{};
u64 symbol_entry_size{};
VAddr dynamic_index = dynamic_offset;
while (true) {
LOG_ERROR(Core_ARM, "{:016X}", lr);
const auto tag = Memory::Read64(dynamic_index);
const auto value = Memory::Read64(dynamic_index + 0x8);
dynamic_index += 0x10;
if (tag == ELF_DYNAMIC_TAG_NULL) {
break;
}
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
string_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
symbol_table_offset = value;
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
symbol_entry_size = value;
}
}
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
return {};
}
const auto string_table_address = text_offset + string_table_offset;
const auto symbol_table_address = text_offset + symbol_table_offset;
Symbols out;
VAddr symbol_index = symbol_table_address;
while (symbol_index < string_table_address) {
ELFSymbol symbol{};
Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
VAddr string_offset = string_table_address + symbol.name_index;
std::string name;
for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
name += static_cast<char>(c);
}
symbol_index += symbol_entry_size;
out.push_back({symbol, name});
}
return out;
}
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
const auto iter =
std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
const auto& [symbol, name] = pair;
const auto end_address = symbol.value + symbol.size;
return func_address >= symbol.value && func_address < end_address;
});
if (iter == symbols.end()) {
return std::nullopt;
}
return iter->second;
}
} // Anonymous namespace
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
std::vector<BacktraceEntry> out;
auto fp = GetReg(29);
auto lr = GetReg(30);
while (true) {
out.push_back({"", 0, lr, 0});
if (!fp) {
break;
}
lr = Memory::Read64(fp + 8) - 4;
fp = Memory::Read64(fp);
}
std::map<VAddr, std::string> modules;
auto& loader{System::GetInstance().GetAppLoader()};
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
return {};
}
std::map<std::string, Symbols> symbols;
for (const auto& module : modules) {
symbols.insert_or_assign(module.second, GetSymbols(module.first));
}
for (auto& entry : out) {
VAddr base = 0;
for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
const auto& module{*iter};
if (entry.original_address >= module.first) {
entry.module = module.second;
base = module.first;
break;
}
}
entry.offset = entry.original_address - base;
entry.address = SEGMENT_BASE + entry.offset;
if (entry.module.empty())
entry.module = "unknown";
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) {
// TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol;
}
}
}
return out;
}
void ARM_Interface::LogBacktrace() const {
const VAddr sp = GetReg(13);
const VAddr pc = GetPC();
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
"Offset", "Symbol");
LOG_ERROR(Core_ARM, "");
const auto backtrace = GetBacktrace();
for (const auto& entry : backtrace) {
LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
entry.original_address, entry.offset, entry.name);
}
}
} // namespace Core

View File

@@ -5,6 +5,7 @@
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
namespace Common {
@@ -152,6 +153,16 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
struct BacktraceEntry {
std::string module;
u64 address;
u64 original_address;
u64 offset;
std::string name;
};
std::vector<BacktraceEntry> GetBacktrace() const;
/// fp (= r29) points to the last frame record.
/// Note that this is the frame record for the *previous* frame, not the current one.
/// Note we need to subtract 4 from our last read to get the proper address

View File

@@ -257,9 +257,6 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
}
void ARM_Dynarmic::PrepareReschedule() {
if (jit == nullptr)
return;
jit->HaltExecution();
}

View File

@@ -25,19 +25,46 @@
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "file_sys/cheat_engine.h"
#include "file_sys/patch_manager.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
namespace {
FileSys::StorageId GetStorageIdForFrontendSlot(
std::optional<FileSys::ContentProviderUnionSlot> slot) {
if (!slot.has_value()) {
return FileSys::StorageId::None;
}
switch (*slot) {
case FileSys::ContentProviderUnionSlot::UserNAND:
return FileSys::StorageId::NandUser;
case FileSys::ContentProviderUnionSlot::SysNAND:
return FileSys::StorageId::NandSystem;
case FileSys::ContentProviderUnionSlot::SDMC:
return FileSys::StorageId::SdCard;
case FileSys::ContentProviderUnionSlot::FrontendManual:
return FileSys::StorageId::Host;
default:
return FileSys::StorageId::None;
}
}
} // Anonymous namespace
/*static*/ System System::s_instance;
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
@@ -74,7 +101,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
struct System::Impl {
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -109,6 +136,9 @@ struct System::Impl {
/// Create default implementations of applets if one is not provided.
applet_manager.SetDefaultAppletsIfMissing();
/// Reset all glue registrations
arp_manager.ResetAll();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -150,7 +180,8 @@ struct System::Impl {
}
telemetry_session->AddInitialInfo(*app_loader);
auto main_process = Kernel::Process::Create(system, "main");
auto main_process =
Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
@@ -159,6 +190,7 @@ struct System::Impl {
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
AddGlueRegistrationForProcess(*app_loader, *main_process);
kernel.MakeCurrentProcess(main_process.get());
// Main process has been loaded and been made current.
@@ -217,6 +249,31 @@ struct System::Impl {
return app_loader->ReadTitle(out);
}
void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::Process& process) {
std::vector<u8> nacp_data;
FileSys::NACP nacp;
if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) {
nacp_data = nacp.GetRawBytes();
} else {
nacp_data.resize(sizeof(FileSys::RawNACP));
}
Service::Glue::ApplicationLaunchProperty launch{};
launch.title_id = process.GetTitleID();
FileSys::PatchManager pm{launch.title_id};
launch.version = pm.GetGameVersion().value_or(0);
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
// current_process_game_card use correct StorageId
launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
launch.title_id, FileSys::ContentRecordType::Program));
launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
arp_manager.Register(launch.title_id, launch, std::move(nacp_data));
}
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
@@ -247,12 +304,17 @@ struct System::Impl {
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
/// Glue services
Service::Glue::ARPManager arp_manager;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
Reporter reporter;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
@@ -492,6 +554,18 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
impl->content_provider->ClearSlot(slot);
}
const Reporter& System::GetReporter() const {
return impl->reporter;
}
Service::Glue::ARPManager& System::GetARPManager() {
return impl->arp_manager;
}
const Service::Glue::ARPManager& System::GetARPManager() const {
return impl->arp_manager;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
}

View File

@@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include <map>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/kernel/object.h"
@@ -42,6 +43,10 @@ struct AppletFrontendSet;
class AppletManager;
} // namespace AM::Applets
namespace Glue {
class ARPManager;
}
namespace SM {
class ServiceManager;
} // namespace SM
@@ -68,6 +73,7 @@ class Cpu;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
class Reporter;
class TelemetrySession;
struct PerfStatsResults;
@@ -284,6 +290,12 @@ public:
void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
const Reporter& GetReporter() const;
Service::Glue::ARPManager& GetARPManager();
const Service::Glue::ARPManager& GetARPManager() const;
private:
System();

View File

@@ -572,7 +572,7 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
<< "# If you are experiencing issues involving keys, it may help to delete this file\n";
}
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
file << fmt::format("\n{} = {}", keyname, Common::HexToString(key));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
}
@@ -583,7 +583,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
WriteKeyToFile(KeyCategory::Title, Common::HexToString(rights_id), key);
}
auto category = KeyCategory::Standard;

View File

@@ -22,8 +22,10 @@
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
#include "core/crypto/xts_encryption_layer.h"
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
using namespace Common;
@@ -45,36 +47,6 @@ struct Package2Header {
};
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
struct INIHeader {
u32_le magic;
u32_le size;
u32_le process_count;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
struct SectionHeader {
u32_le offset;
u32_le size_decompressed;
u32_le size_compressed;
u32_le attribute;
};
static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
struct KIPHeader {
u32_le magic;
std::array<char, 12> name;
u64_le title_id;
u32_le category;
u8 priority;
u8 core;
INSERT_PADDING_BYTES(1);
u8 flags;
std::array<SectionHeader, 6> sections;
std::array<u32, 0x20> capabilities;
};
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
const std::array<SHA256Hash, 0x10> source_hashes{
"B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
"7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
@@ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
};
static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
const auto data_size = in.size() - 0xC;
u32 compressed_size{};
u32 init_index{};
u32 additional_size{};
std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
std::vector<u8> out(in.size() + additional_size);
if (compressed_size == in.size())
std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
else
std::memcpy(out.data(), in.data(), compressed_size);
auto index = in.size() - init_index;
auto out_index = out.size();
while (out_index > 0) {
--index;
auto control = in[index];
for (size_t i = 0; i < 8; ++i) {
if ((control & 0x80) > 0) {
ASSERT(index >= 2);
index -= 2;
u64 segment_offset = in[index] | in[index + 1] << 8;
u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
segment_offset &= 0xFFF;
segment_offset += 3;
if (out_index < segment_size)
segment_size = out_index;
ASSERT(out_index >= segment_size);
out_index -= segment_size;
for (size_t j = 0; j < segment_size; ++j) {
ASSERT(out_index + j + segment_offset < out.size());
out[out_index + j] = out[out_index + j + segment_offset];
}
} else {
ASSERT(out_index >= 1);
--out_index;
--index;
out[out_index] = in[index];
}
control <<= 1;
if (out_index == 0)
return out;
}
}
return out;
}
static u8 CalculateMaxKeyblobSourceHash() {
for (s8 i = 0x1F; i >= 0; --i) {
if (keyblob_source_hashes[i] != SHA256Hash{})
@@ -478,37 +391,22 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
INIHeader ini;
std::memcpy(&ini, c.data(), sizeof(INIHeader));
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
const FileSys::INI ini{ini_file};
if (ini.GetStatus() != Loader::ResultStatus::Success)
return;
u64 offset = sizeof(INIHeader);
for (size_t i = 0; i < ini.process_count; ++i) {
KIPHeader kip;
std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
for (const auto& kip : ini.GetKIPs()) {
if (kip.GetStatus() != Loader::ResultStatus::Success)
return;
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
if (name != "FS" && name != "spl") {
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
if (kip.GetName() != "FS" && kip.GetName() != "spl") {
continue;
}
const u64 initial_offset = sizeof(KIPHeader) + offset;
const auto text_begin = c.cbegin() + initial_offset;
const auto text_end = text_begin + kip.sections[0].size_compressed;
const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
const auto rodata_end = text_end + kip.sections[1].size_compressed;
const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
const auto data_end = rodata_end + kip.sections[2].size_compressed;
const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
const auto& text = kip.GetTextSection();
const auto& rodata = kip.GetRODataSection();
const auto& data = kip.GetDataSection();
std::vector<u8> out;
out.reserve(text.size() + rodata.size() + data.size());
@@ -516,12 +414,9 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
out.insert(out.end(), rodata.begin(), rodata.end());
out.insert(out.end(), data.begin(), data.end());
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
if (name == "FS")
if (kip.GetName() == "FS")
package2_fs[static_cast<size_t>(type)] = std::move(out);
else if (name == "spl")
else if (kip.GetName() == "spl")
package2_spl[static_cast<size_t>(type)] = std::move(out);
}
}

View File

@@ -18,11 +18,16 @@
namespace FileSys {
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
constexpr std::array partition_names{
"update",
"normal",
"secure",
"logo",
};
XCI::XCI(VirtualFile file_)
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
partitions(0x4) {
partitions(partition_names.size()) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorBadXCIHeader;
return;
@@ -43,23 +48,24 @@ XCI::XCI(VirtualFile file_)
for (XCIPartition partition :
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
if (raw != nullptr)
partitions[static_cast<std::size_t>(partition)] =
std::make_shared<PartitionFilesystem>(raw);
const auto partition_idx = static_cast<std::size_t>(partition);
auto raw = main_hfs.GetFile(partition_names[partition_idx]);
if (raw != nullptr) {
partitions[partition_idx] = std::make_shared<PartitionFilesystem>(std::move(raw));
}
}
secure_partition = std::make_shared<NSP>(
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
ncas = secure_partition->GetNCAsCollapsed();
program =
secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
}
auto result = AddNCAFromPartition(XCIPartition::Update);
if (result != Loader::ResultStatus::Success) {
@@ -147,8 +153,9 @@ std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
auto nca = GetNCAByType(type);
if (nca != nullptr)
if (nca != nullptr) {
return nca->GetBaseFile();
}
return nullptr;
}
@@ -169,17 +176,22 @@ VirtualDir XCI::GetParentDirectory() const {
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
const auto partition_index = static_cast<std::size_t>(part);
const auto& partition = partitions[partition_index];
if (partition == nullptr) {
return Loader::ResultStatus::ErrorXCIMissingPartition;
}
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
for (const VirtualFile& file : partition->GetFiles()) {
if (file->GetExtension() != "nca") {
continue;
}
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
// TODO(DarkLordZach): Add proper Rev1+ Support
if (nca->IsUpdate())
if (nca->IsUpdate()) {
continue;
}
if (nca->GetType() == NCAContentType::Program) {
program_nca_status = nca->GetStatus();
}
@@ -188,7 +200,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
} else {
const u16 error_id = static_cast<u16>(nca->GetStatus());
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
partition_names[partition_index], nca->GetName(), error_id,
nca->GetStatus());
}
}

View File

@@ -452,13 +452,13 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
switch (s_header.raw.header.crypto_type) {
case NCASectionCryptoType::NONE:
LOG_DEBUG(Crypto, "called with mode=NONE");
LOG_TRACE(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType::BKTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
std::optional<Core::Crypto::Key128> key = {};
if (has_rights_id) {

View File

@@ -87,6 +87,10 @@ u64 NACP::GetDefaultJournalSaveSize() const {
return raw.user_account_save_data_journal_size;
}
bool NACP::GetUserAccountSwitchLock() const {
return raw.user_account_switch_lock != 0;
}
u32 NACP::GetSupportedLanguages() const {
return raw.supported_languages;
}

View File

@@ -30,7 +30,8 @@ struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
std::array<u8, 0x25> isbn;
u8 startup_user_account;
INSERT_PADDING_BYTES(2);
u8 user_account_switch_lock;
u8 addon_content_registration_type;
u32_le application_attribute;
u32_le supported_languages;
u32_le parental_control;
@@ -111,6 +112,7 @@ public:
u64 GetDefaultJournalSaveSize() const;
u32 GetSupportedLanguages() const;
std::vector<u8> GetRawBytes() const;
bool GetUserAccountSwitchLock() const;
private:
RawNACP raw{};

View File

@@ -287,7 +287,6 @@ void IPSwitchCompiler::Parse() {
} else {
// hex replacement
const auto value = patch_line.substr(9);
replace.reserve(value.size() / 2);
replace = Common::HexStringToVector(value, is_little_endian);
}
@@ -295,7 +294,7 @@ void IPSwitchCompiler::Parse() {
LOG_INFO(Loader,
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
"with byte string '{}'",
patch_text->GetName(), offset, Common::HexVectorToString(replace));
patch_text->GetName(), offset, Common::HexToString(replace));
}
patch.records.insert_or_assign(offset, std::move(replace));

View File

@@ -0,0 +1,228 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/string_util.h"
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/vfs_offset.h"
namespace FileSys {
constexpr u32 INI_MAX_KIPS = 0x50;
namespace {
bool DecompressBLZ(std::vector<u8>& data) {
if (data.size() < 0xC)
return {};
const auto data_size = data.size() - 0xC;
u32 compressed_size{};
u32 init_index{};
u32 additional_size{};
std::memcpy(&compressed_size, data.data() + data_size, sizeof(u32));
std::memcpy(&init_index, data.data() + data_size + 0x4, sizeof(u32));
std::memcpy(&additional_size, data.data() + data_size + 0x8, sizeof(u32));
const auto start_offset = data.size() - compressed_size;
data.resize(compressed_size + additional_size + start_offset);
std::size_t index = compressed_size - init_index;
std::size_t out_index = compressed_size + additional_size;
while (out_index > 0) {
--index;
auto control = data[index + start_offset];
for (size_t i = 0; i < 8; ++i) {
if (((control << i) & 0x80) > 0) {
if (index < 2) {
return false;
}
index -= 2;
std::size_t segment_offset =
data[index + start_offset] | data[index + start_offset + 1] << 8;
std::size_t segment_size = ((segment_offset >> 12) & 0xF) + 3;
segment_offset &= 0xFFF;
segment_offset += 3;
if (out_index < segment_size)
segment_size = out_index;
if (out_index < segment_size) {
return false;
}
out_index -= segment_size;
for (size_t j = 0; j < segment_size; ++j) {
if (out_index + j + segment_offset + start_offset >= data.size()) {
return false;
}
data[out_index + j + start_offset] =
data[out_index + j + segment_offset + start_offset];
}
} else {
if (out_index < 1) {
return false;
}
--out_index;
--index;
data[out_index + start_offset] = data[index + start_offset];
}
if (out_index == 0)
break;
}
}
return true;
}
} // Anonymous namespace
KIP::KIP(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
if (file == nullptr) {
status = Loader::ResultStatus::ErrorNullFile;
return;
}
if (file->GetSize() < sizeof(KIPHeader) || file->ReadObject(&header) != sizeof(KIPHeader)) {
status = Loader::ResultStatus::ErrorBadKIPHeader;
return;
}
if (header.magic != Common::MakeMagic('K', 'I', 'P', '1')) {
status = Loader::ResultStatus::ErrorBadKIPHeader;
return;
}
u64 offset = sizeof(KIPHeader);
for (std::size_t i = 0; i < header.sections.size(); ++i) {
auto compressed = file->ReadBytes(header.sections[i].compressed_size, offset);
offset += header.sections[i].compressed_size;
if (header.sections[i].compressed_size == 0 && header.sections[i].decompressed_size != 0) {
decompressed_sections[i] = std::vector<u8>(header.sections[i].decompressed_size);
} else if (header.sections[i].compressed_size == header.sections[i].decompressed_size) {
decompressed_sections[i] = std::move(compressed);
} else {
decompressed_sections[i] = compressed;
if (!DecompressBLZ(decompressed_sections[i])) {
status = Loader::ResultStatus::ErrorBLZDecompressionFailed;
return;
}
}
}
}
Loader::ResultStatus KIP::GetStatus() const {
return status;
}
std::string KIP::GetName() const {
return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size());
}
u64 KIP::GetTitleID() const {
return header.title_id;
}
std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
return decompressed_sections[index];
}
bool KIP::Is64Bit() const {
return (header.flags & 0x8) != 0;
}
bool KIP::Is39BitAddressSpace() const {
return (header.flags & 0x10) != 0;
}
bool KIP::IsService() const {
return (header.flags & 0x20) != 0;
}
std::vector<u32> KIP::GetKernelCapabilities() const {
return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
}
s32 KIP::GetMainThreadPriority() const {
return header.main_thread_priority;
}
u32 KIP::GetMainThreadStackSize() const {
return header.sections[1].attribute;
}
u32 KIP::GetMainThreadCpuCore() const {
return header.default_core;
}
const std::vector<u8>& KIP::GetTextSection() const {
return decompressed_sections[0];
}
const std::vector<u8>& KIP::GetRODataSection() const {
return decompressed_sections[1];
}
const std::vector<u8>& KIP::GetDataSection() const {
return decompressed_sections[2];
}
u32 KIP::GetTextOffset() const {
return header.sections[0].offset;
}
u32 KIP::GetRODataOffset() const {
return header.sections[1].offset;
}
u32 KIP::GetDataOffset() const {
return header.sections[2].offset;
}
u32 KIP::GetBSSSize() const {
return header.sections[3].decompressed_size;
}
u32 KIP::GetBSSOffset() const {
return header.sections[3].offset;
}
INI::INI(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
if (file->GetSize() < sizeof(INIHeader) || file->ReadObject(&header) != sizeof(INIHeader)) {
status = Loader::ResultStatus::ErrorBadINIHeader;
return;
}
if (header.magic != Common::MakeMagic('I', 'N', 'I', '1')) {
status = Loader::ResultStatus::ErrorBadINIHeader;
return;
}
if (header.kip_count > INI_MAX_KIPS) {
status = Loader::ResultStatus::ErrorINITooManyKIPs;
return;
}
u64 offset = sizeof(INIHeader);
for (std::size_t i = 0; i < header.kip_count; ++i) {
const auto kip_file =
std::make_shared<OffsetVfsFile>(file, file->GetSize() - offset, offset);
KIP kip(kip_file);
if (kip.GetStatus() == Loader::ResultStatus::Success) {
kips.push_back(std::move(kip));
}
}
}
Loader::ResultStatus INI::GetStatus() const {
return status;
}
const std::vector<KIP>& INI::GetKIPs() const {
return kips;
}
} // namespace FileSys

View File

@@ -0,0 +1,99 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_funcs.h"
#include "common/swap.h"
#include "core/file_sys/vfs_types.h"
#include "core/loader/loader.h"
namespace FileSys {
struct KIPSectionHeader {
u32_le offset;
u32_le decompressed_size;
u32_le compressed_size;
u32_le attribute;
};
static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size.");
struct KIPHeader {
u32_le magic;
std::array<char, 0xC> name;
u64_le title_id;
u32_le process_category;
u8 main_thread_priority;
u8 default_core;
INSERT_PADDING_BYTES(1);
u8 flags;
std::array<KIPSectionHeader, 6> sections;
std::array<u32, 0x20> capabilities;
};
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
struct INIHeader {
u32_le magic;
u32_le size;
u32_le kip_count;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
// Kernel Internal Process
class KIP {
public:
explicit KIP(const VirtualFile& file);
Loader::ResultStatus GetStatus() const;
std::string GetName() const;
u64 GetTitleID() const;
std::vector<u8> GetSectionDecompressed(u8 index) const;
// Executable Flags
bool Is64Bit() const;
bool Is39BitAddressSpace() const;
bool IsService() const;
std::vector<u32> GetKernelCapabilities() const;
s32 GetMainThreadPriority() const;
u32 GetMainThreadStackSize() const;
u32 GetMainThreadCpuCore() const;
const std::vector<u8>& GetTextSection() const;
const std::vector<u8>& GetRODataSection() const;
const std::vector<u8>& GetDataSection() const;
u32 GetTextOffset() const;
u32 GetRODataOffset() const;
u32 GetDataOffset() const;
u32 GetBSSSize() const;
u32 GetBSSOffset() const;
private:
Loader::ResultStatus status;
KIPHeader header{};
std::array<std::vector<u8>, 6> decompressed_sections;
};
class INI {
public:
explicit INI(const VirtualFile& file);
Loader::ResultStatus GetStatus() const;
const std::vector<KIP>& GetKIPs() const;
private:
Loader::ResultStatus status;
INIHeader header{};
std::vector<KIP> kips;
};
} // namespace FileSys

View File

@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "common/common_funcs.h"
@@ -69,11 +70,15 @@ struct CNMTHeader {
u64_le title_id;
u32_le title_version;
TitleType type;
INSERT_PADDING_BYTES(1);
u8 reserved;
u16_le table_offset;
u16_le number_content_entries;
u16_le number_meta_entries;
INSERT_PADDING_BYTES(12);
u8 attributes;
std::array<u8, 2> reserved2;
u8 is_committed;
u32_le required_download_system_version;
std::array<u8, 4> reserved3;
};
static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");

View File

@@ -142,7 +142,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
if (!compiler.IsValid())
continue;
auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
auto this_build_id = Common::HexToString(compiler.GetBuildID());
this_build_id =
this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
@@ -168,7 +168,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
return nso;
}
const auto build_id_raw = Common::HexArrayToString(header.build_id);
const auto build_id_raw = Common::HexToString(header.build_id);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
if (Settings::values.dump_nso) {
@@ -219,7 +219,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
}
bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
const auto build_id_raw = Common::HexArrayToString(build_id_);
const auto build_id_raw = Common::HexToString(build_id_);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
@@ -235,7 +235,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
const auto build_id_raw = Common::HexToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
@@ -493,6 +493,16 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
return out;
}
std::optional<u32> PatchManager::GetGameVersion() const {
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto update_tid = GetUpdateTitleID(title_id);
if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
return installed.GetEntryVersion(update_tid);
}
return installed.GetEntryVersion(title_id);
}
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed = Core::System::GetInstance().GetContentProvider();

View File

@@ -66,8 +66,13 @@ public:
std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
VirtualFile update_raw = nullptr) const;
// Given title_id of the program, attempts to get the control data of the update and parse it,
// falling back to the base control data.
// If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
// std::nullopt
std::optional<u32> GetGameVersion() const;
// Given title_id of the program, attempts to get the control data of the update and parse
// it, falling back to the base control data.
std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA

View File

@@ -51,6 +51,21 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
return Loader::ResultStatus::Success;
}
void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
u8 main_thread_prio, u8 main_thread_core,
u32 main_thread_stack_size, u64 title_id,
u64 filesystem_permissions,
KernelCapabilityDescriptors capabilities) {
npdm_header.has_64_bit_instructions.Assign(is_64_bit);
npdm_header.address_space_type.Assign(address_space);
npdm_header.main_thread_priority = main_thread_prio;
npdm_header.main_thread_cpu = main_thread_core;
npdm_header.main_stack_size = main_thread_stack_size;
aci_header.title_id = title_id;
aci_file_access.permissions = filesystem_permissions;
aci_kernel_capabilities = std ::move(capabilities);
}
bool ProgramMetadata::Is64BitProgram() const {
return npdm_header.has_64_bit_instructions;
}

View File

@@ -46,6 +46,11 @@ public:
Loader::ResultStatus Load(VirtualFile file);
// Load from parameters instead of NPDM file, used for KIP
void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
bool Is64BitProgram() const;
ProgramAddressSpaceType GetAddressSpaceType() const;
u8 GetMainThreadPriority() const;

View File

@@ -53,13 +53,14 @@ static bool FollowsNcaIdFormat(std::string_view name) {
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
bool within_two_digit) {
if (!within_two_digit)
return fmt::format("/{}.nca", Common::HexArrayToString(nca_id, second_hex_upper));
if (!within_two_digit) {
return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
return fmt::format("/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, second_hex_upper));
Common::HexToString(nca_id, second_hex_upper));
}
static std::string GetCNMTName(TitleType type, u64 title_id) {
@@ -376,10 +377,11 @@ std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
}
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
if (file == nullptr)
auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false)));
if (file == nullptr) {
return nullptr;
return std::make_shared<NCA>(file);
}
return std::make_shared<NCA>(std::move(file));
}
InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
@@ -643,6 +645,20 @@ ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnion
return out;
}
std::optional<ContentProviderUnionSlot> ContentProviderUnion::GetSlotForEntry(
u64 title_id, ContentRecordType type) const {
const auto iter =
std::find_if(providers.begin(), providers.end(), [title_id, type](const auto& provider) {
return provider.second != nullptr && provider.second->HasEntry(title_id, type);
});
if (iter == providers.end()) {
return std::nullopt;
}
return iter->first;
}
ManualContentProvider::~ManualContentProvider() = default;
void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,

View File

@@ -199,6 +199,9 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
std::optional<ContentProviderUnionSlot> GetSlotForEntry(u64 title_id,
ContentRecordType type) const;
private:
std::map<ContentProviderUnionSlot, ContentProvider*> providers;
};

View File

@@ -235,16 +235,18 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& inner_file : section0->GetFiles()) {
if (inner_file->GetExtension() != "cnmt")
if (inner_file->GetExtension() != "cnmt") {
continue;
}
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
const auto id_string = Common::HexToString(rec.nca_id, false);
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
@@ -253,9 +255,10 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
continue;
}
auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
if (next_nca->GetType() == NCAContentType::Program)
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys);
if (next_nca->GetType() == NCAContentType::Program) {
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
}
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
(cnmt.GetTitleID() & 0x800) != 0)) {

View File

@@ -66,7 +66,7 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, false)));
Common::HexToString(nca_id, false)));
}
NAX::~NAX() = default;

View File

@@ -99,7 +99,8 @@ struct KernelCore::Impl {
void Shutdown() {
next_object_id = 0;
next_process_id = Process::ProcessIDMin;
next_kernel_process_id = Process::InitialKIPIDMin;
next_user_process_id = Process::ProcessIDMin;
next_thread_id = 1;
process_list.clear();
@@ -132,7 +133,8 @@ struct KernelCore::Impl {
}
std::atomic<u32> next_object_id{0};
std::atomic<u64> next_process_id{Process::ProcessIDMin};
std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
std::atomic<u64> next_thread_id{1};
// Lists all processes that exist in the current session.
@@ -226,8 +228,12 @@ u64 KernelCore::CreateNewThreadID() {
return impl->next_thread_id++;
}
u64 KernelCore::CreateNewProcessID() {
return impl->next_process_id++;
u64 KernelCore::CreateNewKernelProcessID() {
return impl->next_kernel_process_id++;
}
u64 KernelCore::CreateNewUserProcessID() {
return impl->next_user_process_id++;
}
Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {

View File

@@ -96,7 +96,10 @@ private:
u32 CreateNewObjectID();
/// Creates a new process ID, incrementing the internal process ID counter;
u64 CreateNewProcessID();
u64 CreateNewKernelProcessID();
/// Creates a new process ID, incrementing the internal process ID counter;
u64 CreateNewUserProcessID();
/// Creates a new thread ID, incrementing the internal thread ID counter.
u64 CreateNewThreadID();

View File

@@ -48,7 +48,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
}
} // Anonymous namespace
SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
SharedPtr<Process> Process::Create(Core::System& system, std::string name,
Process::ProcessType type) {
auto& kernel = system.Kernel();
SharedPtr<Process> process(new Process(system));
@@ -56,7 +57,8 @@ SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
: kernel.CreateNewUserProcessID();
process->capabilities.InitializeForMetadatalessProcess();
std::mt19937 rng(Settings::values.rng_seed.value_or(0));

View File

@@ -73,9 +73,15 @@ public:
ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
};
// Used to determine how process IDs are assigned.
enum class ProcessType {
KernelInternal,
Userland,
};
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
static SharedPtr<Process> Create(Core::System& system, std::string name);
static SharedPtr<Process> Create(Core::System& system, std::string name, ProcessType type);
std::string GetTypeName() const override {
return "Process";

View File

@@ -38,6 +38,7 @@
#include "core/hle/result.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
#include "core/reporter.h"
namespace Kernel {
namespace {
@@ -594,6 +595,7 @@ struct BreakReason {
static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
bool has_dumped_buffer{};
std::vector<u8> debug_buffer;
const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
if (sz == 0 || addr == 0 || has_dumped_buffer) {
@@ -605,7 +607,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
} else {
// We don't know what's in here so we'll hexdump it
std::vector<u8> debug_buffer(sz);
debug_buffer.resize(sz);
Memory::ReadBlock(addr, debug_buffer.data(), sz);
std::string hexdump;
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
@@ -664,6 +666,10 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
break;
}
system.GetReporter().SaveSvcBreakReport(
static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1,
info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
if (!break_reason.signal_debugger) {
LOG_CRITICAL(
Debug_Emulated,

View File

@@ -68,9 +68,7 @@ VMManager::VMManager(Core::System& system) : system{system} {
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
}
VMManager::~VMManager() {
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
}
VMManager::~VMManager() = default;
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
Clear();

View File

@@ -12,13 +12,17 @@
#include "common/swap.h"
#include "core/constants.h"
#include "core/core_timing.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/acc/acc_aa.h"
#include "core/hle/service/acc/acc_su.h"
#include "core/hle/service/acc/acc_u0.h"
#include "core/hle/service/acc/acc_u1.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/loader/loader.h"
namespace Service::Account {
@@ -90,7 +94,7 @@ private:
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
return;
}
@@ -112,9 +116,9 @@ private:
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
} else {
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
rb.Push(SanitizeJPEGSize(image.GetSize()));
}
}
@@ -213,7 +217,7 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon
rb.Push(profile_manager->CanSystemRegisterUser());
}
void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
void Module::Interface::InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -226,6 +230,31 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
rb.PushIpcInterface<IManagerForApplication>();
}
void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called");
FileSys::NACP nacp;
const auto res = system.GetAppLoader().ReadControlData(nacp);
bool is_locked = false;
if (res != Loader::ResultStatus::Success) {
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
auto nacp_unique = pm.GetControlMetadata().first;
if (nacp_unique != nullptr) {
is_locked = nacp_unique->GetUserAccountSwitchLock();
} else {
LOG_ERROR(Service_ACC, "nacp_unique is null!");
}
} else {
is_locked = nacp.GetUserAccountSwitchLock();
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(is_locked);
}
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called");
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
@@ -251,19 +280,25 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
}
Module::Interface::Interface(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager, const char* name)
std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
const char* name)
: ServiceFramework(name), module(std::move(module)),
profile_manager(std::move(profile_manager)) {}
profile_manager(std::move(profile_manager)), system(system) {}
Module::Interface::~Interface() = default;
void InstallInterfaces(SM::ServiceManager& service_manager) {
void InstallInterfaces(Core::System& system) {
auto module = std::make_shared<Module>();
auto profile_manager = std::make_shared<ProfileManager>();
std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
std::make_shared<ACC_AA>(module, profile_manager, system)
->InstallAsService(system.ServiceManager());
std::make_shared<ACC_SU>(module, profile_manager, system)
->InstallAsService(system.ServiceManager());
std::make_shared<ACC_U0>(module, profile_manager, system)
->InstallAsService(system.ServiceManager());
std::make_shared<ACC_U1>(module, profile_manager, system)
->InstallAsService(system.ServiceManager());
}
} // namespace Service::Account

View File

@@ -15,7 +15,8 @@ public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager, const char* name);
std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
const char* name);
~Interface() override;
void GetUserCount(Kernel::HLERequestContext& ctx);
@@ -24,18 +25,20 @@ public:
void ListOpenUsers(Kernel::HLERequestContext& ctx);
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
void GetProfile(Kernel::HLERequestContext& ctx);
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
void InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx);
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
std::shared_ptr<ProfileManager> profile_manager;
Core::System& system;
};
};
/// Registers all ACC services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
void InstallInterfaces(Core::System& system);
} // namespace Service::Account

View File

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

View File

@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_AA final : public Module::Interface {
public:
explicit ACC_AA(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
explicit ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
Core::System& system);
~ACC_AA() override;
};

View File

@@ -6,8 +6,9 @@
namespace Service::Account {
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
: Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
Core::System& system)
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:su") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_SU::GetUserCount, "GetUserCount"},

View File

@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_SU final : public Module::Interface {
public:
explicit ACC_SU(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
explicit ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
Core::System& system);
~ACC_SU() override;
};

View File

@@ -6,8 +6,9 @@
namespace Service::Account {
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
Core::System& system)
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u0") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_U0::GetUserCount, "GetUserCount"},
@@ -21,7 +22,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{99, nullptr, "DebugActivateOpenContextRetention"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
{100, &ACC_U0::InitializeApplicationInfoOld, "InitializeApplicationInfoOld"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
{102, nullptr, "AuthenticateApplicationAsync"},
{103, nullptr, "CheckNetworkServiceAvailabilityAsync"},
@@ -32,7 +33,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{131, nullptr, "ListOpenContextStoredUsers"},
{140, nullptr, "InitializeApplicationInfo"},
{141, nullptr, "ListQualifiedUsers"},
{150, nullptr, "IsUserAccountSwitchLocked"},
{150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
};
// clang-format on

View File

@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_U0 final : public Module::Interface {
public:
explicit ACC_U0(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
explicit ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
Core::System& system);
~ACC_U0() override;
};

View File

@@ -6,8 +6,9 @@
namespace Service::Account {
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
Core::System& system)
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u1") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_U1::GetUserCount, "GetUserCount"},

View File

@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_U1 final : public Module::Interface {
public:
explicit ACC_U1(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager);
explicit ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
Core::System& system);
~ACC_U1() override;
};

View File

@@ -271,7 +271,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{71, nullptr, "GetCurrentIlluminanceEx"},
{80, nullptr, "SetWirelessPriorityMode"},
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
{91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
{91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
{100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
{1000, nullptr, "GetDebugStorageChannel"},
};
@@ -282,6 +282,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
auto& kernel = Core::System::GetInstance().Kernel();
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"ISelfController:LaunchableEvent");
// TODO(ogniK): Figure out where, when and why this event gets signalled
accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
accumulated_suspended_tick_changed_event.writable->Signal(); // Is signalled on creation
}
ISelfController::~ISelfController() = default;
@@ -444,6 +449,17 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) {
// The implementation of this function is fine as is, the reason we're labelling it as stubbed
// is because we're currently unsure when and where accumulated_suspended_tick_changed_event is
// actually signalled for the time being.
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
}
AppletMessageQueue::AppletMessageQueue() {
auto& kernel = Core::System::GetInstance().Kernel();
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,

View File

@@ -133,9 +133,12 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
Kernel::EventPair accumulated_suspended_tick_changed_event;
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
};

View File

@@ -35,12 +35,28 @@ AppletDataBroker::AppletDataBroker() {
AppletDataBroker::~AppletDataBroker() = default;
AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const {
std::vector<std::vector<u8>> out_normal;
for (const auto& storage : in_channel) {
out_normal.push_back(storage->GetData());
}
std::vector<std::vector<u8>> out_interactive;
for (const auto& storage : in_interactive_channel) {
out_interactive.push_back(storage->GetData());
}
return {std::move(out_normal), std::move(out_interactive)};
}
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
if (out_channel.empty())
return nullptr;
auto out = std::move(out_channel.front());
out_channel.pop();
out_channel.pop_front();
return out;
}
@@ -49,7 +65,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
return nullptr;
auto out = std::move(in_channel.front());
in_channel.pop();
in_channel.pop_front();
return out;
}
@@ -58,7 +74,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
return nullptr;
auto out = std::move(out_interactive_channel.front());
out_interactive_channel.pop();
out_interactive_channel.pop_front();
return out;
}
@@ -67,25 +83,25 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
return nullptr;
auto out = std::move(in_interactive_channel.front());
in_interactive_channel.pop();
in_interactive_channel.pop_front();
return out;
}
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
in_channel.push(std::make_unique<IStorage>(storage));
in_channel.push_back(std::make_unique<IStorage>(storage));
}
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
out_channel.push(std::make_unique<IStorage>(storage));
out_channel.push_back(std::make_unique<IStorage>(storage));
pop_out_data_event.writable->Signal();
}
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
in_interactive_channel.push(std::make_unique<IStorage>(storage));
in_interactive_channel.push_back(std::make_unique<IStorage>(storage));
}
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
out_interactive_channel.push(std::make_unique<IStorage>(storage));
out_interactive_channel.push_back(std::make_unique<IStorage>(storage));
pop_interactive_out_data_event.writable->Signal();
}
@@ -204,7 +220,7 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
UNIMPLEMENTED_MSG(
"No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
static_cast<u8>(id));
return std::make_shared<StubApplet>();
return std::make_shared<StubApplet>(id);
}
}

View File

@@ -54,6 +54,14 @@ public:
AppletDataBroker();
~AppletDataBroker();
struct RawChannelData {
std::vector<std::vector<u8>> normal;
std::vector<std::vector<u8>> interactive;
};
// Retrieves but does not pop the data sent to applet.
RawChannelData PeekDataToAppletForDebug() const;
std::unique_ptr<IStorage> PopNormalDataToGame();
std::unique_ptr<IStorage> PopNormalDataToApplet();
@@ -76,16 +84,16 @@ private:
// Queues are named from applet's perspective
// PopNormalDataToApplet and PushNormalDataFromGame
std::queue<std::unique_ptr<IStorage>> in_channel;
std::deque<std::unique_ptr<IStorage>> in_channel;
// PopNormalDataToGame and PushNormalDataFromApplet
std::queue<std::unique_ptr<IStorage>> out_channel;
std::deque<std::unique_ptr<IStorage>> out_channel;
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
std::deque<std::unique_ptr<IStorage>> in_interactive_channel;
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
std::deque<std::unique_ptr<IStorage>> out_interactive_channel;
Kernel::EventPair state_changed_event;

View File

@@ -9,8 +9,10 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/error.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/error.h"
#include "core/reporter.h"
namespace Service::AM::Applets {
@@ -143,9 +145,12 @@ void Error::Execute() {
}
const auto callback = [this] { DisplayCompleted(); };
const auto title_id = Core::CurrentProcess()->GetTitleID();
const auto& reporter{Core::System::GetInstance().GetReporter()};
switch (mode) {
case ErrorAppletMode::ShowError:
reporter.SaveErrorReport(title_id, error_code);
frontend.ShowError(error_code, callback);
break;
case ErrorAppletMode::ShowSystemError:
@@ -156,14 +161,18 @@ void Error::Execute() {
const auto& detail_text =
system ? args->system_error.detail_text : args->application_error.detail_text;
frontend.ShowCustomErrorText(
error_code,
Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()),
Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()),
callback);
const auto main_text_string =
Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size());
const auto detail_text_string =
Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size());
reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string);
frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback);
break;
}
case ErrorAppletMode::ShowErrorRecord:
reporter.SaveErrorReport(title_id, error_code,
fmt::format("{:016X}", args->error_record.posix_time));
frontend.ShowErrorWithTimestamp(
error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
break;

View File

@@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include <string_view>
#include "common/assert.h"
#include "common/hex_util.h"
@@ -13,24 +13,25 @@
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/general_backend.h"
#include "core/reporter.h"
namespace Service::AM::Applets {
static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
const auto data = storage->GetData();
LOG_INFO(Service_AM,
"called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
prefix, data.size(), Common::HexVectorToString(data));
"called (STUBBED), during {} received normal data with size={:08X}, data={}",
prefix, data.size(), Common::HexToString(data));
}
storage = broker.PopInteractiveDataToApplet();
for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
const auto data = storage->GetData();
LOG_INFO(Service_AM,
"called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
prefix, data.size(), Common::HexVectorToString(data));
"called (STUBBED), during {} received interactive data with size={:08X}, data={}",
prefix, data.size(), Common::HexToString(data));
}
}
@@ -83,13 +84,20 @@ void PhotoViewer::ViewFinished() {
broker.SignalStateChanged();
}
StubApplet::StubApplet() = default;
StubApplet::StubApplet(AppletId id) : id(id) {}
StubApplet::~StubApplet() = default;
void StubApplet::Initialize() {
LOG_WARNING(Service_AM, "called (STUBBED)");
Applet::Initialize();
const auto data = broker.PeekDataToAppletForDebug();
Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport(
static_cast<u32>(id), common_args.arguments_version, common_args.library_version,
common_args.theme_color, common_args.play_startup_sound, common_args.system_tick,
data.normal, data.interactive);
LogCurrentStorage(broker, "Initialize");
}

View File

@@ -34,7 +34,7 @@ private:
class StubApplet final : public Applet {
public:
StubApplet();
explicit StubApplet(AppletId id);
~StubApplet() override;
void Initialize() override;
@@ -43,6 +43,9 @@ public:
ResultCode GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
private:
AppletId id;
};
} // namespace Service::AM::Applets

View File

@@ -1,75 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/arp/arp.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::ARP {
class ARP_R final : public ServiceFramework<ARP_R> {
public:
explicit ARP_R() : ServiceFramework{"arp:r"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetApplicationLaunchProperty"},
{1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
{2, nullptr, "GetApplicationControlProperty"},
{3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IRegistrar final : public ServiceFramework<IRegistrar> {
public:
explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Issue"},
{1, nullptr, "SetApplicationLaunchProperty"},
{2, nullptr, "SetApplicationControlProperty"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ARP_W final : public ServiceFramework<ARP_W> {
public:
explicit ARP_W() : ServiceFramework{"arp:w"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
{1, nullptr, "DeleteProperties"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ARP, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRegistrar>();
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<ARP_R>()->InstallAsService(sm);
std::make_shared<ARP_W>()->InstallAsService(sm);
}
} // namespace Service::ARP

View File

@@ -1,16 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Service::SM {
class ServiceManager;
}
namespace Service::ARP {
/// Registers all ARP services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::ARP

View File

@@ -58,8 +58,8 @@ public:
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{11, nullptr, "FlushAudioOutBuffers"},
{12, nullptr, "SetAudioOutVolume"},
{13, nullptr, "GetAudioOutVolume"},
{12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
{13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
};
// clang-format on
RegisterHandlers(functions);
@@ -183,6 +183,25 @@ private:
rb.Push(static_cast<u32>(stream->GetQueueSize()));
}
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const float volume = rp.Pop<float>();
LOG_DEBUG(Service_Audio, "called, volume={}", volume);
stream->SetVolume(volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(stream->GetVolume());
}
AudioCore::AudioOut& audio_core;
AudioCore::StreamPtr stream;
std::string device_name;

View File

@@ -16,6 +16,7 @@
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
#include "core/reporter.h"
namespace Service::Fatal {
@@ -100,27 +101,10 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "{}", crash_report);
const std::string crashreport_dir =
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
if (!FileUtil::CreateFullPath(crashreport_dir)) {
LOG_ERROR(
Service_Fatal,
"Unable to create crash report directory. Possible log directory permissions issue.");
return;
}
const std::time_t t = std::time(nullptr);
const std::string crashreport_filename =
fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
auto file = FileUtil::IOFile(crashreport_filename, "wb");
if (file.IsOpen()) {
file.WriteString(crash_report);
LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
} else {
LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
}
Core::System::GetInstance().GetReporter().SaveCrashReport(
title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
info.backtrace_size, info.ArchAsString(), info.unk10);
}
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {

View File

@@ -0,0 +1,297 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "common/logging/log.h"
#include "core/file_sys/control_metadata.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/glue/arp.h"
#include "core/hle/service/glue/errors.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/service.h"
namespace Service::Glue {
namespace {
std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) {
const auto& list = system.Kernel().GetProcessList();
const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
return process->GetProcessID() == process_id;
});
if (iter == list.end()) {
return std::nullopt;
}
return (*iter)->GetTitleID();
}
} // Anonymous namespace
ARP_R::ARP_R(const Core::System& system, const ARPManager& manager)
: ServiceFramework{"arp:r"}, system(system), manager(manager) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
{1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
{2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
{3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
}
ARP_R::~ARP_R() = default;
void ARP_R::GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
const auto title_id = GetTitleIDForProcessID(system, process_id);
if (!title_id.has_value()) {
LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_REGISTERED);
return;
}
const auto res = manager.GetLaunchProperty(*title_id);
if (res.Failed()) {
LOG_ERROR(Service_ARP, "Failed to get launch property!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(*res);
}
void ARP_R::GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
const auto res = manager.GetLaunchProperty(title_id);
if (res.Failed()) {
LOG_ERROR(Service_ARP, "Failed to get launch property!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(*res);
}
void ARP_R::GetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
const auto title_id = GetTitleIDForProcessID(system, process_id);
if (!title_id.has_value()) {
LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_REGISTERED);
return;
}
const auto res = manager.GetControlProperty(*title_id);
if (res.Failed()) {
LOG_ERROR(Service_ARP, "Failed to get control property!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
ctx.WriteBuffer(*res);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ARP_R::GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
const auto res = manager.GetControlProperty(title_id);
if (res.Failed()) {
LOG_ERROR(Service_ARP, "Failed to get control property!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
ctx.WriteBuffer(*res);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
class IRegistrar final : public ServiceFramework<IRegistrar> {
friend class ARP_W;
public:
explicit IRegistrar(
std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issuer)
: ServiceFramework{"IRegistrar"}, issue_process_id(std::move(issuer)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IRegistrar::Issue, "Issue"},
{1, &IRegistrar::SetApplicationLaunchProperty, "SetApplicationLaunchProperty"},
{2, &IRegistrar::SetApplicationControlProperty, "SetApplicationControlProperty"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Issue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
if (process_id == 0) {
LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_PROCESS_ID);
return;
}
if (issued) {
LOG_ERROR(Service_ARP,
"Attempted to issue registrar, but registrar is already issued!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_ACCESS);
return;
}
issue_process_id(process_id, launch, std::move(control));
issued = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ARP, "called");
if (issued) {
LOG_ERROR(
Service_ARP,
"Attempted to set application launch property, but registrar is already issued!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_ACCESS);
return;
}
IPC::RequestParser rp{ctx};
launch = rp.PopRaw<ApplicationLaunchProperty>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ARP, "called");
if (issued) {
LOG_ERROR(
Service_ARP,
"Attempted to set application control property, but registrar is already issued!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_ACCESS);
return;
}
control = ctx.ReadBuffer();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issue_process_id;
bool issued = false;
ApplicationLaunchProperty launch;
std::vector<u8> control;
};
ARP_W::ARP_W(const Core::System& system, ARPManager& manager)
: ServiceFramework{"arp:w"}, system(system), manager(manager) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
{1, &ARP_W::DeleteProperties, "DeleteProperties"},
};
// clang-format on
RegisterHandlers(functions);
}
ARP_W::~ARP_W() = default;
void ARP_W::AcquireRegistrar(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ARP, "called");
registrar = std::make_shared<IRegistrar>(
[this](u64 process_id, ApplicationLaunchProperty launch, std::vector<u8> control) {
const auto res = GetTitleIDForProcessID(system, process_id);
if (!res.has_value()) {
return ERR_NOT_REGISTERED;
}
return manager.Register(*res, launch, std::move(control));
});
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface(registrar);
}
void ARP_W::DeleteProperties(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto process_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
if (process_id == 0) {
LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_PROCESS_ID);
return;
}
const auto title_id = GetTitleIDForProcessID(system, process_id);
if (!title_id.has_value()) {
LOG_ERROR(Service_ARP, "No title ID for process ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_REGISTERED);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(manager.Unregister(*title_id));
}
} // namespace Service::Glue

View File

@@ -0,0 +1,43 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::Glue {
class ARPManager;
class IRegistrar;
class ARP_R final : public ServiceFramework<ARP_R> {
public:
explicit ARP_R(const Core::System& system, const ARPManager& manager);
~ARP_R() override;
private:
void GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx);
void GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
void GetApplicationControlProperty(Kernel::HLERequestContext& ctx);
void GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
const Core::System& system;
const ARPManager& manager;
};
class ARP_W final : public ServiceFramework<ARP_W> {
public:
explicit ARP_W(const Core::System& system, ARPManager& manager);
~ARP_W() override;
private:
void AcquireRegistrar(Kernel::HLERequestContext& ctx);
void DeleteProperties(Kernel::HLERequestContext& ctx);
const Core::System& system;
ARPManager& manager;
std::shared_ptr<IRegistrar> registrar;
};
} // namespace Service::Glue

View File

@@ -0,0 +1,50 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/glue/bgtc.h"
namespace Service::Glue {
BGTC_T::BGTC_T() : ServiceFramework{"bgtc:t"} {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "NotifyTaskStarting"},
{2, nullptr, "NotifyTaskFinished"},
{3, nullptr, "GetTriggerEvent"},
{4, nullptr, "IsInHalfAwake"},
{5, nullptr, "NotifyClientName"},
{6, nullptr, "IsInFullAwake"},
{11, nullptr, "ScheduleTask"},
{12, nullptr, "GetScheduledTaskInterval"},
{13, nullptr, "UnscheduleTask"},
{14, nullptr, "GetScheduleEvent"},
{15, nullptr, "SchedulePeriodicTask"},
{101, nullptr, "GetOperationMode"},
{102, nullptr, "WillDisconnectNetworkWhenEnteringSleep"},
{103, nullptr, "WillStayHalfAwakeInsteadSleep"},
};
// clang-format on
RegisterHandlers(functions);
}
BGTC_T::~BGTC_T() = default;
BGTC_SC::BGTC_SC() : ServiceFramework{"bgtc:sc"} {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "GetState"},
{2, nullptr, "GetStateChangedEvent"},
{3, nullptr, "NotifyEnteringHalfAwake"},
{4, nullptr, "NotifyLeavingHalfAwake"},
{5, nullptr, "SetIsUsingSleepUnsupportedDevices"},
};
// clang-format on
RegisterHandlers(functions);
}
BGTC_SC::~BGTC_SC() = default;
} // namespace Service::Glue

View File

@@ -0,0 +1,23 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service::Glue {
class BGTC_T final : public ServiceFramework<BGTC_T> {
public:
BGTC_T();
~BGTC_T() override;
};
class BGTC_SC final : public ServiceFramework<BGTC_SC> {
public:
BGTC_SC();
~BGTC_SC() override;
};
} // namespace Service::Glue

View File

@@ -0,0 +1,16 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/result.h"
namespace Service::Glue {
constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 0x1E};
constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 0x1F};
constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 0x2A};
constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 0x66};
} // namespace Service::Glue

View File

@@ -0,0 +1,25 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "core/core.h"
#include "core/hle/service/glue/arp.h"
#include "core/hle/service/glue/bgtc.h"
#include "core/hle/service/glue/glue.h"
namespace Service::Glue {
void InstallInterfaces(Core::System& system) {
// ARP
std::make_shared<ARP_R>(system, system.GetARPManager())
->InstallAsService(system.ServiceManager());
std::make_shared<ARP_W>(system, system.GetARPManager())
->InstallAsService(system.ServiceManager());
// BackGround Task Controller
std::make_shared<BGTC_T>()->InstallAsService(system.ServiceManager());
std::make_shared<BGTC_SC>()->InstallAsService(system.ServiceManager());
}
} // namespace Service::Glue

View File

@@ -0,0 +1,16 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Core {
class System;
} // namespace Core
namespace Service::Glue {
/// Registers all Glue services with the specified service manager.
void InstallInterfaces(Core::System& system);
} // namespace Service::Glue

View File

@@ -0,0 +1,78 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/glue/errors.h"
#include "core/hle/service/glue/manager.h"
namespace Service::Glue {
struct ARPManager::MapEntry {
ApplicationLaunchProperty launch;
std::vector<u8> control;
};
ARPManager::ARPManager() = default;
ARPManager::~ARPManager() = default;
ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id) const {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
const auto iter = entries.find(title_id);
if (iter == entries.end()) {
return ERR_NOT_REGISTERED;
}
return MakeResult<ApplicationLaunchProperty>(iter->second.launch);
}
ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
const auto iter = entries.find(title_id);
if (iter == entries.end()) {
return ERR_NOT_REGISTERED;
}
return MakeResult<std::vector<u8>>(iter->second.control);
}
ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
std::vector<u8> control) {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
const auto iter = entries.find(title_id);
if (iter != entries.end()) {
return ERR_INVALID_ACCESS;
}
entries.insert_or_assign(title_id, MapEntry{launch, std::move(control)});
return RESULT_SUCCESS;
}
ResultCode ARPManager::Unregister(u64 title_id) {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
const auto iter = entries.find(title_id);
if (iter == entries.end()) {
return ERR_NOT_REGISTERED;
}
entries.erase(iter);
return RESULT_SUCCESS;
}
void ARPManager::ResetAll() {
entries.clear();
}
} // namespace Service::Glue

View File

@@ -0,0 +1,63 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/result.h"
namespace Service::Glue {
struct ApplicationLaunchProperty {
u64 title_id;
u32 version;
FileSys::StorageId base_game_storage_id;
FileSys::StorageId update_storage_id;
u8 program_index;
u8 reserved;
};
static_assert(sizeof(ApplicationLaunchProperty) == 0x10,
"ApplicationLaunchProperty has incorrect size.");
// A class to manage state related to the arp:w and arp:r services, specifically the registration
// and unregistration of launch and control properties.
class ARPManager {
public:
ARPManager();
~ARPManager();
// Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was
// previously registered, otherwise ERR_NOT_REGISTERED if it was never registered or
// ERR_INVALID_PROCESS_ID if the title ID is 0.
ResultVal<ApplicationLaunchProperty> GetLaunchProperty(u64 title_id) const;
// Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to
// the provided title ID if it was previously registered, otherwise ERR_NOT_REGISTERED if it was
// never registered or ERR_INVALID_PROCESS_ID if the title ID is 0.
ResultVal<std::vector<u8>> GetControlProperty(u64 title_id) const;
// Adds a new entry to the internal database with the provided parameters, returning
// ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister
// step, and ERR_INVALID_PROCESS_ID if the title ID is 0.
ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
// Removes the registration for the provided title ID from the database, returning
// ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the
// title ID is 0.
ResultCode Unregister(u64 title_id);
// Removes all entries from the database, always succeeds. Should only be used when resetting
// system state.
void ResetAll();
private:
struct MapEntry;
std::map<u64, MapEntry> entries;
};
} // namespace Service::Glue

View File

@@ -310,7 +310,7 @@ public:
if (!IsValidNROHash(hash)) {
LOG_ERROR(Service_LDR,
"NRO hash is not present in any currently loaded NRRs (hash={})!",
Common::HexArrayToString(hash));
Common::HexToString(hash));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MISSING_NRR_HASH);
return;

View File

@@ -2,10 +2,18 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <json.hpp>
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/service.h"
#include "core/reporter.h"
#include "core/settings.h"
namespace Service::PlayReport {
@@ -40,8 +48,21 @@ public:
private:
void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
// TODO(ogniK): Do we want to add play report?
LOG_WARNING(Service_PREPO, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto user_id = rp.PopRaw<u128>();
const auto process_id = rp.PopRaw<u64>();
const auto data1 = ctx.ReadBuffer(0);
const auto data2 = ctx.ReadBuffer(1);
LOG_DEBUG(
Service_PREPO,
"called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}",
user_id[1], user_id[0], process_id, data1.size(), data2.size());
const auto& reporter{Core::System::GetInstance().GetReporter()};
reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2},
user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -19,7 +19,6 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/arp/arp.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/bcat/module.h"
#include "core/hle/service/bpc/bpc.h"
@@ -33,6 +32,7 @@
#include "core/hle/service/fgm/fgm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/grc/grc.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/lbl/lbl.h"
@@ -68,6 +68,7 @@
#include "core/hle/service/usb/usb.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/wlan/wlan.h"
#include "core/reporter.h"
namespace Service {
@@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
}
buf.push_back('}');
Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport(
ctx, ctx.GetCommand(), function_name, service_name);
UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
}
@@ -200,11 +203,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
SM::ServiceManager::InstallInterfaces(sm);
Account::InstallInterfaces(*sm);
Account::InstallInterfaces(system);
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
ARP::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);
@@ -218,6 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
FGM::InstallInterfaces(*sm);
FileSystem::InstallInterfaces(*sm, vfs);
Friend::InstallInterfaces(*sm);
Glue::InstallInterfaces(system);
GRC::InstallInterfaces(*sm);
HID::InstallInterfaces(*sm);
LBL::InstallInterfaces(*sm);

View File

@@ -141,6 +141,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
const FileSys::PatchManager pm(metadata.GetTitleID());
// Load NSO modules
modules.clear();
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
VAddr next_load_addr = base_address;
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
@@ -159,6 +160,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
next_load_addr = *tentative_next_load_addr;
modules.insert_or_assign(load_addr, module);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
@@ -212,4 +214,13 @@ bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
return false;
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& modules) {
if (!is_loaded) {
return ResultStatus::ErrorNotInitialized;
}
modules = this->modules;
return ResultStatus::Success;
}
} // namespace Loader

View File

@@ -45,6 +45,8 @@ public:
ResultStatus ReadTitle(std::string& title) override;
bool IsRomFSUpdatable() const override;
ResultStatus ReadNSOModules(Modules& modules) override;
private:
FileSys::ProgramMetadata metadata;
FileSys::VirtualFile romfs;
@@ -54,6 +56,8 @@ private:
std::string name;
u64 title_id{};
bool override_update;
Modules modules;
};
} // namespace Loader

102
src/core/loader/kip.cpp Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/program_metadata.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/loader/kip.h"
namespace Loader {
namespace {
constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
}
} // Anonymous namespace
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
: AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
AppLoader_KIP::~AppLoader_KIP() = default;
FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& file) {
u32_le magic{};
if (file->GetSize() < sizeof(u32) || file->ReadObject(&magic) != sizeof(u32)) {
return FileType::Error;
}
if (magic == Common::MakeMagic('K', 'I', 'P', '1')) {
return FileType::KIP;
}
return FileType::Error;
}
FileType AppLoader_KIP::GetFileType() const {
return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP
: FileType::Error;
}
AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
if (kip == nullptr) {
return {ResultStatus::ErrorNullFile, {}};
}
if (kip->GetStatus() != ResultStatus::Success) {
return {kip->GetStatus(), {}};
}
const auto get_kip_address_space_type = [](const auto& kip) {
return kip.Is64Bit()
? (kip.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
: FileSys::ProgramAddressSpaceType::Is36Bit)
: FileSys::ProgramAddressSpaceType::Is32Bit;
};
const auto address_space = get_kip_address_space_type(*kip);
FileSys::ProgramMetadata metadata;
metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(),
kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),
kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities());
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
Kernel::CodeSet codeset;
std::vector<u8> program_image;
const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
const std::vector<u8>& data, u32 offset) {
segment.addr = offset;
segment.offset = offset;
segment.size = PageAlignSize(static_cast<u32>(data.size()));
program_image.resize(offset);
program_image.insert(program_image.end(), data.begin(), data.end());
};
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset());
load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset());
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
codeset.DataSegment().size += kip->GetBSSSize();
GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size());
codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), base_address);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
is_loaded = true;
return {ResultStatus::Success,
LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}};
}
} // namespace Loader

35
src/core/loader/kip.h Normal file
View File

@@ -0,0 +1,35 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/loader/loader.h"
namespace FileSys {
class KIP;
}
namespace Loader {
class AppLoader_KIP final : public AppLoader {
public:
explicit AppLoader_KIP(FileSys::VirtualFile file);
~AppLoader_KIP() override;
/**
* Returns the type of the file
* @param file std::shared_ptr<VfsFile> open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() const override;
LoadResult Load(Kernel::Process& process) override;
private:
std::unique_ptr<FileSys::KIP> kip;
};
} // namespace Loader

View File

@@ -11,6 +11,7 @@
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h"
#include "core/loader/kip.h"
#include "core/loader/nax.h"
#include "core/loader/nca.h"
#include "core/loader/nro.h"
@@ -36,6 +37,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
CHECK_TYPE(XCI)
CHECK_TYPE(NAX)
CHECK_TYPE(NSP)
CHECK_TYPE(KIP)
#undef CHECK_TYPE
@@ -63,6 +65,8 @@ FileType GuessFromFilename(const std::string& name) {
return FileType::XCI;
if (extension == "nsp")
return FileType::NSP;
if (extension == "kip")
return FileType::KIP;
return FileType::Unknown;
}
@@ -83,6 +87,8 @@ std::string GetFileTypeString(FileType type) {
return "NAX";
case FileType::NSP:
return "NSP";
case FileType::KIP:
return "KIP";
case FileType::DeconstructedRomDirectory:
return "Directory";
case FileType::Error:
@@ -93,7 +99,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown";
}
constexpr std::array<const char*, 62> RESULT_MESSAGES{
constexpr std::array<const char*, 66> RESULT_MESSAGES{
"The operation completed successfully.",
"The loader requested to load is already loaded.",
"The operation is not implemented.",
@@ -156,6 +162,10 @@ constexpr std::array<const char*, 62> RESULT_MESSAGES{
"The BKTR-type NCA has a bad Subsection bucket.",
"The BKTR-type NCA is missing the base RomFS.",
"The NSP or XCI does not contain an update in addition to the base game.",
"The KIP file has a bad header.",
"The KIP BLZ decompression of the section failed unexpectedly.",
"The INI file has a bad header.",
"The INI file contains more than the maximum allowable number of KIP files.",
};
std::ostream& operator<<(std::ostream& os, ResultStatus status) {
@@ -205,6 +215,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
case FileType::NSP:
return std::make_unique<AppLoader_NSP>(std::move(file));
// NX KIP (Kernel Internal Process) file format
case FileType::KIP:
return std::make_unique<AppLoader_KIP>(std::move(file));
// NX deconstructed ROM directory.
case FileType::DeconstructedRomDirectory:
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));

View File

@@ -37,6 +37,7 @@ enum class FileType {
NSP,
XCI,
NAX,
KIP,
DeconstructedRomDirectory,
};
@@ -124,6 +125,10 @@ enum class ResultStatus : u16 {
ErrorBadSubsectionBuckets,
ErrorMissingBKTRBaseRomFS,
ErrorNoPackedUpdate,
ErrorBadKIPHeader,
ErrorBLZDecompressionFailed,
ErrorBadINIHeader,
ErrorINITooManyKIPs,
};
std::ostream& operator<<(std::ostream& os, ResultStatus status);
@@ -267,6 +272,12 @@ public:
return ResultStatus::ErrorNotImplemented;
}
using Modules = std::map<VAddr, std::string>;
virtual ResultStatus ReadNSOModules(Modules& modules) {
return ResultStatus::ErrorNotImplemented;
}
protected:
FileSys::VirtualFile file;
bool is_loaded = false;

View File

@@ -94,4 +94,8 @@ ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
ResultStatus AppLoader_NAX::ReadNSOModules(Modules& modules) {
return nca_loader->ReadNSOModules(modules);
}
} // namespace Loader

View File

@@ -42,6 +42,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
ResultStatus ReadNSOModules(Modules& modules) override;
private:
std::unique_ptr<FileSys::NAX> nax;
std::unique_ptr<AppLoader_NCA> nca_loader;

View File

@@ -105,4 +105,13 @@ ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
return ResultStatus::Success;
}
ResultStatus AppLoader_NCA::ReadNSOModules(Modules& modules) {
if (directory_loader == nullptr) {
return ResultStatus::ErrorNotInitialized;
}
return directory_loader->ReadNSOModules(modules);
}
} // namespace Loader

View File

@@ -42,6 +42,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
ResultStatus ReadNSOModules(Modules& modules) override;
private:
std::unique_ptr<FileSys::NCA> nca;
std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;

View File

@@ -152,8 +152,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
auto& system = Core::System::GetInstance();
const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
if (!cheats.empty()) {
system.RegisterCheatList(cheats, Common::HexArrayToString(nso_header.build_id),
load_base, load_base + program_image.size());
system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base,
load_base + program_image.size());
}
}
@@ -172,11 +172,15 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
modules.clear();
// Load module
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
if (!LoadModule(process, *file, base_address, true)) {
return {ResultStatus::ErrorLoadingNSO, {}};
}
modules.insert_or_assign(base_address, file->GetName());
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
is_loaded = true;
@@ -184,4 +188,9 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
}
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) {
modules = this->modules;
return ResultStatus::Success;
}
} // namespace Loader

View File

@@ -85,6 +85,11 @@ public:
std::optional<FileSys::PatchManager> pm = {});
LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadNSOModules(Modules& modules) override;
private:
Modules modules;
};
} // namespace Loader

View File

@@ -183,4 +183,8 @@ ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
return secondary_loader->ReadLogo(buffer);
}
ResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) {
return secondary_loader->ReadNSOModules(modules);
}
} // namespace Loader

View File

@@ -49,6 +49,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
ResultStatus ReadNSOModules(Modules& modules) override;
private:
std::unique_ptr<FileSys::NSP> nsp;
std::unique_ptr<AppLoader> secondary_loader;

View File

@@ -149,4 +149,8 @@ ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
return nca_loader->ReadLogo(buffer);
}
ResultStatus AppLoader_XCI::ReadNSOModules(Modules& modules) {
return nca_loader->ReadNSOModules(modules);
}
} // namespace Loader

View File

@@ -49,6 +49,8 @@ public:
ResultStatus ReadBanner(std::vector<u8>& buffer) override;
ResultStatus ReadLogo(std::vector<u8>& buffer) override;
ResultStatus ReadNSOModules(Modules& modules) override;
private:
std::unique_ptr<FileSys::XCI> xci;
std::unique_ptr<AppLoader_NCA> nca_loader;

353
src/core/reporter.cpp Normal file
View File

@@ -0,0 +1,353 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fstream>
#include <json.hpp>
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/scm_rev.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
#include "core/reporter.h"
#include "core/settings.h"
#include "fmt/time.h"
namespace {
std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir),
type, title_id, timestamp);
}
std::string GetTimestamp() {
const auto time = std::time(nullptr);
return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time));
}
using namespace nlohmann;
void SaveToFile(const json& json, const std::string& filename) {
if (!FileUtil::CreateFullPath(filename))
LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
std::ofstream file(
FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault));
file << std::setw(4) << json << std::endl;
}
json GetYuzuVersionData() {
return {
{"scm_rev", std::string(Common::g_scm_rev)},
{"scm_branch", std::string(Common::g_scm_branch)},
{"scm_desc", std::string(Common::g_scm_desc)},
{"build_name", std::string(Common::g_build_name)},
{"build_date", std::string(Common::g_build_date)},
{"build_fullname", std::string(Common::g_build_fullname)},
{"build_version", std::string(Common::g_build_version)},
{"shader_cache_version", std::string(Common::g_shader_cache_version)},
};
}
json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp,
std::optional<u128> user_id = {}) {
auto out = json{
{"title_id", fmt::format("{:016X}", title_id)},
{"result_raw", fmt::format("{:08X}", result.raw)},
{"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))},
{"result_description", fmt::format("{:08X}", result.description.Value())},
{"timestamp", timestamp},
};
if (user_id.has_value())
out["user_id"] = fmt::format("{:016X}{:016X}", (*user_id)[1], (*user_id)[0]);
return out;
}
json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc,
u64 pstate, std::array<u64, 31> registers,
std::optional<std::array<u64, 32>> backtrace = {}) {
auto out = json{
{"entry_point", fmt::format("{:016X}", entry_point)},
{"sp", fmt::format("{:016X}", sp)},
{"pc", fmt::format("{:016X}", pc)},
{"pstate", fmt::format("{:016X}", pstate)},
{"architecture", architecture},
};
auto registers_out = json::object();
for (std::size_t i = 0; i < registers.size(); ++i) {
registers_out[fmt::format("X{:02d}", i)] = fmt::format("{:016X}", registers[i]);
}
out["registers"] = std::move(registers_out);
if (backtrace.has_value()) {
auto backtrace_out = json::array();
for (const auto& entry : *backtrace) {
backtrace_out.push_back(fmt::format("{:016X}", entry));
}
out["backtrace"] = std::move(backtrace_out);
}
return out;
}
json GetProcessorStateDataAuto(Core::System& system) {
const auto* process{system.CurrentProcess()};
const auto& vm_manager{process->VMManager()};
auto& arm{system.CurrentArmInterface()};
Core::ARM_Interface::ThreadContext context{};
arm.SaveContext(context);
return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
vm_manager.GetCodeRegionBaseAddress(), context.sp, context.pc,
context.pstate, context.cpu_registers);
}
json GetBacktraceData(Core::System& system) {
auto out = json::array();
const auto& backtrace{system.CurrentArmInterface().GetBacktrace()};
for (const auto& entry : backtrace) {
out.push_back({
{"module", entry.module},
{"address", fmt::format("{:016X}", entry.address)},
{"original_address", fmt::format("{:016X}", entry.original_address)},
{"offset", fmt::format("{:016X}", entry.offset)},
{"symbol_name", entry.name},
});
}
return out;
}
json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& system) {
json out;
out["yuzu_version"] = GetYuzuVersionData();
out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp);
out["processor_state"] = GetProcessorStateDataAuto(system);
out["backtrace"] = GetBacktraceData(system);
return out;
}
template <bool read_value, typename DescriptorType>
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
auto buffer_out = json::array();
for (const auto& desc : buffer) {
auto entry = json{
{"address", fmt::format("{:016X}", desc.Address())},
{"size", fmt::format("{:016X}", desc.Size())},
};
if constexpr (read_value) {
std::vector<u8> data(desc.Size());
Memory::ReadBlock(desc.Address(), data.data(), desc.Size());
entry["data"] = Common::HexToString(data);
}
buffer_out.push_back(std::move(entry));
}
return buffer_out;
}
json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
json out;
auto cmd_buf = json::array();
for (std::size_t i = 0; i < IPC::COMMAND_BUFFER_LENGTH; ++i) {
cmd_buf.push_back(fmt::format("{:08X}", ctx.CommandBuffer()[i]));
}
out["command_buffer"] = std::move(cmd_buf);
out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA());
out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB());
out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC());
out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX());
return std::move(out);
}
} // Anonymous namespace
namespace Core {
Reporter::Reporter(Core::System& system) : system(system) {}
Reporter::~Reporter() = default;
void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point,
u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
const std::array<u64, 31>& registers,
const std::array<u64, 32>& backtrace, u32 backtrace_size,
const std::string& arch, u32 unk10) const {
if (!IsReportingEnabled())
return;
const auto timestamp = GetTimestamp();
json out;
out["yuzu_version"] = GetYuzuVersionData();
out["report_common"] = GetReportCommonData(title_id, result, timestamp);
auto proc_out = GetProcessorStateData(arch, entry_point, sp, pc, pstate, registers, backtrace);
proc_out["set_flags"] = fmt::format("{:016X}", set_flags);
proc_out["afsr0"] = fmt::format("{:016X}", afsr0);
proc_out["afsr1"] = fmt::format("{:016X}", afsr1);
proc_out["esr"] = fmt::format("{:016X}", esr);
proc_out["far"] = fmt::format("{:016X}", far);
proc_out["backtrace_size"] = fmt::format("{:08X}", backtrace_size);
proc_out["unknown_10"] = fmt::format("{:08X}", unk10);
out["processor_state"] = std::move(proc_out);
SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp));
}
void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
std::optional<std::vector<u8>> resolved_buffer) const {
if (!IsReportingEnabled())
return;
const auto timestamp = GetTimestamp();
const auto title_id = system.CurrentProcess()->GetTitleID();
auto out = GetFullDataAuto(timestamp, title_id, system);
auto break_out = json{
{"type", fmt::format("{:08X}", type)},
{"signal_debugger", fmt::format("{}", signal_debugger)},
{"info1", fmt::format("{:016X}", info1)},
{"info2", fmt::format("{:016X}", info2)},
};
if (resolved_buffer.has_value()) {
break_out["debug_buffer"] = Common::HexToString(*resolved_buffer);
}
out["svc_break"] = std::move(break_out);
SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp));
}
void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
const std::string& name,
const std::string& service_name) const {
if (!IsReportingEnabled())
return;
const auto timestamp = GetTimestamp();
const auto title_id = system.CurrentProcess()->GetTitleID();
auto out = GetFullDataAuto(timestamp, title_id, system);
auto function_out = GetHLERequestContextData(ctx);
function_out["command_id"] = command_id;
function_out["function_name"] = name;
function_out["service_name"] = service_name;
out["function"] = std::move(function_out);
SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp));
}
void Reporter::SaveUnimplementedAppletReport(
u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color,
bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel,
std::vector<std::vector<u8>> interactive_channel) const {
if (!IsReportingEnabled())
return;
const auto timestamp = GetTimestamp();
const auto title_id = system.CurrentProcess()->GetTitleID();
auto out = GetFullDataAuto(timestamp, title_id, system);
out["applet_common_args"] = {
{"applet_id", fmt::format("{:02X}", applet_id)},
{"common_args_version", fmt::format("{:08X}", common_args_version)},
{"library_version", fmt::format("{:08X}", library_version)},
{"theme_color", fmt::format("{:08X}", theme_color)},
{"startup_sound", fmt::format("{}", startup_sound)},
{"system_tick", fmt::format("{:016X}", system_tick)},
};
auto normal_out = json::array();
for (const auto& data : normal_channel) {
normal_out.push_back(Common::HexToString(data));
}
auto interactive_out = json::array();
for (const auto& data : interactive_channel) {
interactive_out.push_back(Common::HexToString(data));
}
out["applet_normal_data"] = std::move(normal_out);
out["applet_interactive_data"] = std::move(interactive_out);
SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp));
}
void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
std::optional<u128> user_id) const {
if (!IsReportingEnabled())
return;
const auto timestamp = GetTimestamp();
json out;
out["yuzu_version"] = GetYuzuVersionData();
out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp, user_id);
auto data_out = json::array();
for (const auto& d : data) {
data_out.push_back(Common::HexToString(d));
}
out["play_report_process_id"] = fmt::format("{:016X}", process_id);
out["play_report_data"] = std::move(data_out);
SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
}
void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
std::optional<std::string> custom_text_main,
std::optional<std::string> custom_text_detail) const {
if (!IsReportingEnabled())
return;
const auto timestamp = GetTimestamp();
json out;
out["yuzu_version"] = GetYuzuVersionData();
out["report_common"] = GetReportCommonData(title_id, result, timestamp);
out["processor_state"] = GetProcessorStateDataAuto(system);
out["backtrace"] = GetBacktraceData(system);
out["error_custom_text"] = {
{"main", *custom_text_main},
{"detail", *custom_text_detail},
};
SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
}
void Reporter::SaveUserReport() const {
if (!IsReportingEnabled())
return;
const auto timestamp = GetTimestamp();
const auto title_id = system.CurrentProcess()->GetTitleID();
SaveToFile(GetFullDataAuto(timestamp, title_id, system),
GetPath("user_report", title_id, timestamp));
}
bool Reporter::IsReportingEnabled() const {
return Settings::values.reporting_services;
}
} // namespace Core

56
src/core/reporter.h Normal file
View File

@@ -0,0 +1,56 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <optional>
#include <vector>
#include "common/common_types.h"
union ResultCode;
namespace Kernel {
class HLERequestContext;
} // namespace Kernel
namespace Core {
class Reporter {
public:
explicit Reporter(Core::System& system);
~Reporter();
void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp,
u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace,
u32 backtrace_size, const std::string& arch, u32 unk10) const;
void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
std::optional<std::vector<u8>> resolved_buffer = {}) const;
void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
const std::string& name,
const std::string& service_name) const;
void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version,
u32 theme_color, bool startup_sound, u64 system_tick,
std::vector<std::vector<u8>> normal_channel,
std::vector<std::vector<u8>> interactive_channel) const;
void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
std::optional<u128> user_id = {}) const;
void SaveErrorReport(u64 title_id, ResultCode result,
std::optional<std::string> custom_text_main = {},
std::optional<std::string> custom_text_detail = {}) const;
void SaveUserReport() const;
private:
bool IsReportingEnabled() const;
Core::System& system;
};
} // namespace Core

View File

@@ -415,6 +415,7 @@ struct Values {
std::string program_args;
bool dump_exefs;
bool dump_nso;
bool reporting_services;
// WebService
bool enable_telemetry;

View File

@@ -1,100 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace CiTrace {
// NOTE: Things are stored in little-endian
#pragma pack(1)
struct CTHeader {
static const char* ExpectedMagicWord() {
return "CiTr";
}
static u32 ExpectedVersion() {
return 1;
}
char magic[4];
u32 version;
u32 header_size;
struct {
// NOTE: Register range sizes are technically hardware-constants, but the actual limits
// aren't known. Hence we store the presumed limits along the offsets.
// Sizes are given in u32 units.
u32 gpu_registers;
u32 gpu_registers_size;
u32 lcd_registers;
u32 lcd_registers_size;
u32 pica_registers;
u32 pica_registers_size;
u32 default_attributes;
u32 default_attributes_size;
u32 vs_program_binary;
u32 vs_program_binary_size;
u32 vs_swizzle_data;
u32 vs_swizzle_data_size;
u32 vs_float_uniforms;
u32 vs_float_uniforms_size;
u32 gs_program_binary;
u32 gs_program_binary_size;
u32 gs_swizzle_data;
u32 gs_swizzle_data_size;
u32 gs_float_uniforms;
u32 gs_float_uniforms_size;
// Other things we might want to store here:
// - Initial framebuffer data, maybe even a full copy of FCRAM/VRAM
// - Lookup tables for fragment lighting
// - Lookup tables for procedural textures
} initial_state_offsets;
u32 stream_offset;
u32 stream_size;
};
enum CTStreamElementType : u32 {
FrameMarker = 0xE1,
MemoryLoad = 0xE2,
RegisterWrite = 0xE3,
};
struct CTMemoryLoad {
u32 file_offset;
u32 size;
u32 physical_address;
u32 pad;
};
struct CTRegisterWrite {
u32 physical_address;
enum : u32 {
SIZE_8 = 0xD1,
SIZE_16 = 0xD2,
SIZE_32 = 0xD3,
SIZE_64 = 0xD4,
} size;
// TODO: Make it clearer which bits of this member are used for sizes other than 32 bits
u64 value;
};
struct CTStreamElement {
CTStreamElementType type;
union {
CTMemoryLoad memory_load;
CTRegisterWrite register_write;
};
};
#pragma pack()
} // namespace CiTrace

View File

@@ -1,208 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/tracer/recorder.h"
namespace CiTrace {
Recorder::Recorder(const InitialState& initial_state) : initial_state(initial_state) {}
void Recorder::Finish(const std::string& filename) {
// Setup CiTrace header
CTHeader header;
std::memcpy(header.magic, CTHeader::ExpectedMagicWord(), 4);
header.version = CTHeader::ExpectedVersion();
header.header_size = sizeof(CTHeader);
// Calculate file offsets
auto& initial = header.initial_state_offsets;
initial.gpu_registers_size = static_cast<u32>(initial_state.gpu_registers.size());
initial.lcd_registers_size = static_cast<u32>(initial_state.lcd_registers.size());
initial.pica_registers_size = static_cast<u32>(initial_state.pica_registers.size());
initial.default_attributes_size = static_cast<u32>(initial_state.default_attributes.size());
initial.vs_program_binary_size = static_cast<u32>(initial_state.vs_program_binary.size());
initial.vs_swizzle_data_size = static_cast<u32>(initial_state.vs_swizzle_data.size());
initial.vs_float_uniforms_size = static_cast<u32>(initial_state.vs_float_uniforms.size());
initial.gs_program_binary_size = static_cast<u32>(initial_state.gs_program_binary.size());
initial.gs_swizzle_data_size = static_cast<u32>(initial_state.gs_swizzle_data.size());
initial.gs_float_uniforms_size = static_cast<u32>(initial_state.gs_float_uniforms.size());
header.stream_size = static_cast<u32>(stream.size());
initial.gpu_registers = sizeof(header);
initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32);
initial.pica_registers = initial.lcd_registers + initial.lcd_registers_size * sizeof(u32);
;
initial.default_attributes = initial.pica_registers + initial.pica_registers_size * sizeof(u32);
initial.vs_program_binary =
initial.default_attributes + initial.default_attributes_size * sizeof(u32);
initial.vs_swizzle_data =
initial.vs_program_binary + initial.vs_program_binary_size * sizeof(u32);
initial.vs_float_uniforms =
initial.vs_swizzle_data + initial.vs_swizzle_data_size * sizeof(u32);
initial.gs_program_binary =
initial.vs_float_uniforms + initial.vs_float_uniforms_size * sizeof(u32);
initial.gs_swizzle_data =
initial.gs_program_binary + initial.gs_program_binary_size * sizeof(u32);
initial.gs_float_uniforms =
initial.gs_swizzle_data + initial.gs_swizzle_data_size * sizeof(u32);
header.stream_offset = initial.gs_float_uniforms + initial.gs_float_uniforms_size * sizeof(u32);
// Iterate through stream elements, update relevant stream element data
for (auto& stream_element : stream) {
switch (stream_element.data.type) {
case MemoryLoad: {
auto& file_offset = memory_regions[stream_element.hash];
if (!stream_element.uses_existing_data) {
file_offset = header.stream_offset;
}
stream_element.data.memory_load.file_offset = file_offset;
break;
}
default:
// Other commands don't use any extra data
DEBUG_ASSERT(stream_element.extra_data.size() == 0);
break;
}
header.stream_offset += static_cast<u32>(stream_element.extra_data.size());
}
try {
// Open file and write header
FileUtil::IOFile file(filename, "wb");
std::size_t written = file.WriteObject(header);
if (written != 1 || file.Tell() != initial.gpu_registers)
throw "Failed to write header";
// Write initial state
written =
file.WriteArray(initial_state.gpu_registers.data(), initial_state.gpu_registers.size());
if (written != initial_state.gpu_registers.size() || file.Tell() != initial.lcd_registers)
throw "Failed to write GPU registers";
written =
file.WriteArray(initial_state.lcd_registers.data(), initial_state.lcd_registers.size());
if (written != initial_state.lcd_registers.size() || file.Tell() != initial.pica_registers)
throw "Failed to write LCD registers";
written = file.WriteArray(initial_state.pica_registers.data(),
initial_state.pica_registers.size());
if (written != initial_state.pica_registers.size() ||
file.Tell() != initial.default_attributes)
throw "Failed to write Pica registers";
written = file.WriteArray(initial_state.default_attributes.data(),
initial_state.default_attributes.size());
if (written != initial_state.default_attributes.size() ||
file.Tell() != initial.vs_program_binary)
throw "Failed to write default vertex attributes";
written = file.WriteArray(initial_state.vs_program_binary.data(),
initial_state.vs_program_binary.size());
if (written != initial_state.vs_program_binary.size() ||
file.Tell() != initial.vs_swizzle_data)
throw "Failed to write vertex shader program binary";
written = file.WriteArray(initial_state.vs_swizzle_data.data(),
initial_state.vs_swizzle_data.size());
if (written != initial_state.vs_swizzle_data.size() ||
file.Tell() != initial.vs_float_uniforms)
throw "Failed to write vertex shader swizzle data";
written = file.WriteArray(initial_state.vs_float_uniforms.data(),
initial_state.vs_float_uniforms.size());
if (written != initial_state.vs_float_uniforms.size() ||
file.Tell() != initial.gs_program_binary)
throw "Failed to write vertex shader float uniforms";
written = file.WriteArray(initial_state.gs_program_binary.data(),
initial_state.gs_program_binary.size());
if (written != initial_state.gs_program_binary.size() ||
file.Tell() != initial.gs_swizzle_data)
throw "Failed to write geomtry shader program binary";
written = file.WriteArray(initial_state.gs_swizzle_data.data(),
initial_state.gs_swizzle_data.size());
if (written != initial_state.gs_swizzle_data.size() ||
file.Tell() != initial.gs_float_uniforms)
throw "Failed to write geometry shader swizzle data";
written = file.WriteArray(initial_state.gs_float_uniforms.data(),
initial_state.gs_float_uniforms.size());
if (written != initial_state.gs_float_uniforms.size() ||
file.Tell() != initial.gs_float_uniforms + sizeof(u32) * initial.gs_float_uniforms_size)
throw "Failed to write geometry shader float uniforms";
// Iterate through stream elements, write "extra data"
for (const auto& stream_element : stream) {
if (stream_element.extra_data.size() == 0)
continue;
written =
file.WriteBytes(stream_element.extra_data.data(), stream_element.extra_data.size());
if (written != stream_element.extra_data.size())
throw "Failed to write extra data";
}
if (file.Tell() != header.stream_offset)
throw "Unexpected end of extra data";
// Write actual stream elements
for (const auto& stream_element : stream) {
if (1 != file.WriteObject(stream_element.data))
throw "Failed to write stream element";
}
} catch (const char* str) {
LOG_ERROR(HW_GPU, "Writing CiTrace file failed: {}", str);
}
}
void Recorder::FrameFinished() {
stream.push_back({{FrameMarker}});
}
void Recorder::MemoryAccessed(const u8* data, u32 size, u32 physical_address) {
StreamElement element = {{MemoryLoad}};
element.data.memory_load.size = size;
element.data.memory_load.physical_address = physical_address;
// Compute hash over given memory region to check if the contents are already stored internally
boost::crc_32_type result;
result.process_bytes(data, size);
element.hash = result.checksum();
element.uses_existing_data = (memory_regions.find(element.hash) != memory_regions.end());
if (!element.uses_existing_data) {
element.extra_data.resize(size);
memcpy(element.extra_data.data(), data, size);
memory_regions.insert({element.hash, 0}); // file offset will be initialized in Finish()
}
stream.push_back(element);
}
template <typename T>
void Recorder::RegisterWritten(u32 physical_address, T value) {
StreamElement element = {{RegisterWrite}};
element.data.register_write.size =
(sizeof(T) == 1) ? CTRegisterWrite::SIZE_8
: (sizeof(T) == 2) ? CTRegisterWrite::SIZE_16
: (sizeof(T) == 4) ? CTRegisterWrite::SIZE_32
: CTRegisterWrite::SIZE_64;
element.data.register_write.physical_address = physical_address;
element.data.register_write.value = value;
stream.push_back(element);
}
template void Recorder::RegisterWritten(u32, u8);
template void Recorder::RegisterWritten(u32, u16);
template void Recorder::RegisterWritten(u32, u32);
template void Recorder::RegisterWritten(u32, u64);
} // namespace CiTrace

View File

@@ -1,87 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/crc.hpp>
#include "common/common_types.h"
#include "core/tracer/citrace.h"
namespace CiTrace {
class Recorder {
public:
struct InitialState {
std::vector<u32> gpu_registers;
std::vector<u32> lcd_registers;
std::vector<u32> pica_registers;
std::vector<u32> default_attributes;
std::vector<u32> vs_program_binary;
std::vector<u32> vs_swizzle_data;
std::vector<u32> vs_float_uniforms;
std::vector<u32> gs_program_binary;
std::vector<u32> gs_swizzle_data;
std::vector<u32> gs_float_uniforms;
};
/**
* Recorder constructor
* @param initial_state Initial recorder state
*/
explicit Recorder(const InitialState& initial_state);
/// Finish recording of this Citrace and save it using the given filename.
void Finish(const std::string& filename);
/// Mark end of a frame
void FrameFinished();
/**
* Store a copy of the given memory range in the recording.
* @note Use this whenever the GPU is about to access a particular memory region.
* @note The implementation will make sure to minimize redundant memory updates.
*/
void MemoryAccessed(const u8* data, u32 size, u32 physical_address);
/**
* Record a register write.
* @note Use this whenever a GPU-related MMIO register has been written to.
*/
template <typename T>
void RegisterWritten(u32 physical_address, T value);
private:
// Initial state of recording start
InitialState initial_state;
// Command stream
struct StreamElement {
CTStreamElement data;
/**
* Extra data to store along "core" data.
* This is e.g. used for data used in MemoryUpdates.
*/
std::vector<u8> extra_data;
/// Optional CRC hash (e.g. for hashing memory regions)
boost::crc_32_type::value_type hash;
/// If true, refer to data already written to the output file instead of extra_data
bool uses_existing_data;
};
std::vector<StreamElement> stream;
/**
* Internal cache which maps hashes of memory contents to file offsets at which those memory
* contents are stored.
*/
std::unordered_map<boost::crc_32_type::value_type /*hash*/, u32 /*file_offset*/> memory_regions;
};
} // namespace CiTrace

View File

@@ -16,7 +16,8 @@ namespace ArmTests {
TestEnvironment::TestEnvironment(bool mutable_memory_)
: mutable_memory(mutable_memory_),
test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
auto process = Kernel::Process::Create(Core::System::GetInstance(), "");
auto process = Kernel::Process::Create(Core::System::GetInstance(), "",
Kernel::Process::ProcessType::Userland);
page_table = &process->VMManager().page_table;
std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);

View File

@@ -3,6 +3,7 @@ add_library(video_core STATIC
dma_pusher.h
debug_utils/debug_utils.cpp
debug_utils/debug_utils.h
engines/const_buffer_info.h
engines/engine_upload.cpp
engines/engine_upload.h
engines/fermi_2d.cpp

View File

@@ -0,0 +1,17 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Tegra::Engines {
struct ConstBufferInfo {
GPUVAddr address;
u32 size;
bool enabled;
};
} // namespace Tegra::Engines

View File

@@ -396,12 +396,10 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
auto& shader = state.shader_stages[static_cast<std::size_t>(stage)];
auto& bind_data = regs.cb_bind[static_cast<std::size_t>(stage)];
ASSERT(bind_data.index < Regs::MaxConstBuffers);
auto& buffer = shader.const_buffers[bind_data.index];
ASSERT(bind_data.index < Regs::MaxConstBuffers);
buffer.enabled = bind_data.valid.Value() != 0;
buffer.index = bind_data.index;
buffer.address = regs.const_buffer.BufferAddress();
buffer.size = regs.const_buffer.cb_size;
}

View File

@@ -15,6 +15,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/engines/const_buffer_info.h"
#include "video_core/engines/engine_upload.h"
#include "video_core/gpu.h"
#include "video_core/macro_interpreter.h"
@@ -1112,13 +1113,6 @@ public:
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
struct State {
struct ConstBufferInfo {
GPUVAddr address;
u32 index;
u32 size;
bool enabled;
};
struct ShaderStageInfo {
std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers;
};

View File

@@ -163,8 +163,8 @@ private:
static constexpr u64 page_size{1 << page_bits};
static constexpr u64 page_mask{page_size - 1};
/// Address space in bits, this is fairly arbitrary but sufficiently large.
static constexpr u32 address_space_width{39};
/// Address space in bits, according to Tegra X1 TRM
static constexpr u32 address_space_width{40};
/// Start address for mapping, this is fairly arbitrary but must be non-zero.
static constexpr GPUVAddr address_space_base{0x100000};
/// End of address space, based on address space in bits.

View File

@@ -43,8 +43,9 @@ bool Device::TestVariableAoffi() {
// This is a unit test, please ignore me on apitrace bug reports.
uniform sampler2D tex;
uniform ivec2 variable_offset;
out vec4 output_attribute;
void main() {
gl_Position = textureOffset(tex, vec2(0), variable_offset);
output_attribute = textureOffset(tex, vec2(0), variable_offset);
}
)";
const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &AOFFI_TEST)};

View File

@@ -322,9 +322,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
}
const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
SetupConstBuffers(stage_enum, shader, program_handle, base_bindings);
SetupGlobalRegions(stage_enum, shader, program_handle, base_bindings);
SetupTextures(stage_enum, shader, program_handle, base_bindings);
SetupDrawConstBuffers(stage_enum, shader);
SetupGlobalRegions(stage_enum, shader);
SetupTextures(stage_enum, shader, base_bindings);
// Workaround for Intel drivers.
// When a clip distance is enabled but not set in the shader it crops parts of the screen
@@ -776,57 +776,55 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
return true;
}
void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
const Shader& shader, GLuint program_handle,
BaseBindings base_bindings) {
void RasterizerOpenGL::SetupDrawConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
const Shader& shader) {
MICROPROFILE_SCOPE(OpenGL_UBO);
const auto& gpu = system.GPU();
const auto& maxwell3d = gpu.Maxwell3D();
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
const auto stage_index = static_cast<std::size_t>(stage);
const auto& shader_stage = system.GPU().Maxwell3D().state.shader_stages[stage_index];
const auto& entries = shader->GetShaderEntries().const_buffers;
// Upload only the enabled buffers from the 16 constbuffers of each shader stage
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& used_buffer = entries[bindpoint];
const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()];
if (!buffer.enabled) {
// Set values to zero to unbind buffers
bind_ubo_pushbuffer.Push(0, 0, 0);
continue;
}
std::size_t size = 0;
if (used_buffer.IsIndirect()) {
// Buffer is accessed indirectly, so upload the entire thing
size = buffer.size;
if (size > MaxConstbufferSize) {
LOG_WARNING(Render_OpenGL, "Indirect constbuffer size {} exceeds maximum {}", size,
MaxConstbufferSize);
size = MaxConstbufferSize;
}
} else {
// Buffer is accessed directly, upload just what we use
size = used_buffer.GetSize();
}
// Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
// UBO alignment requirements.
size = Common::AlignUp(size, sizeof(GLvec4));
ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
const GLintptr const_buffer_offset =
buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment());
bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size);
const auto& entry = entries[bindpoint];
SetupConstBuffer(shader_stage.const_buffers[entry.GetIndex()], entry);
}
}
void RasterizerOpenGL::SetupConstBuffer(const Tegra::Engines::ConstBufferInfo& buffer,
const GLShader::ConstBufferEntry& entry) {
if (!buffer.enabled) {
// Set values to zero to unbind buffers
bind_ubo_pushbuffer.Push(0, 0, 0);
return;
}
std::size_t size;
if (entry.IsIndirect()) {
// Buffer is accessed indirectly, so upload the entire thing
size = buffer.size;
if (size > MaxConstbufferSize) {
LOG_WARNING(Render_OpenGL, "Indirect constbuffer size {} exceeds maximum {}", size,
MaxConstbufferSize);
size = MaxConstbufferSize;
}
} else {
// Buffer is accessed directly, upload just what we use
size = entry.GetSize();
}
// Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
// UBO alignment requirements.
size = Common::AlignUp(size, sizeof(GLvec4));
ASSERT_MSG(size <= MaxConstbufferSize, "Constant buffer is too big");
const std::size_t alignment = device.GetUniformBufferAlignment();
const GLintptr offset = buffer_cache.UploadMemory(buffer.address, size, alignment);
bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, size);
}
void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
const Shader& shader, GLenum primitive_mode,
BaseBindings base_bindings) {
const Shader& shader) {
const auto& entries = shader->GetShaderEntries().global_memory_entries;
for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry{entries[bindpoint]};
@@ -840,7 +838,7 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade
}
void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
GLuint program_handle, BaseBindings base_bindings) {
BaseBindings base_bindings) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = system.GPU();
const auto& maxwell3d = gpu.Maxwell3D();

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