Compare commits

..

2 Commits

538 changed files with 12693 additions and 30100 deletions

View File

@@ -1,19 +0,0 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo Hello, world!
displayName: 'Run a one-line script'
- script: |
echo Add other tasks to build, test, and deploy your project.
echo See https://aka.ms/yaml
displayName: 'Run a multi-line script'

View File

@@ -1,19 +0,0 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo Hello, world!
displayName: 'Run a one-line script'
- script: |
echo Add other tasks to build, test, and deploy your project.
echo See https://aka.ms/yaml
displayName: 'Run a multi-line script'

View File

@@ -1,19 +0,0 @@
trigger:
- master
jobs:
- job: copy
displayName: 'Sync Repository'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo 'https://$(GitUsername):$(GitAccessToken)@dev.azure.com' > $HOME/.git-credentials
displayName: 'Load Credentials'
- script: git config --global credential.helper store
displayName: 'Register Credential Helper'
- script: git remote add other $(GitRepoPushChangesURL)
displayName: 'Register Repository'
- script: git push --force other HEAD:$(GitPushBranch)
displayName: 'Update Code'
- script: rm -rf $HOME/.git-credentials
displayName: 'Clear Cached Credentials'

View File

@@ -1,19 +0,0 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo Hello, world!
displayName: 'Run a one-line script'
- script: |
echo Add other tasks to build, test, and deploy your project.
echo See https://aka.ms/yaml
displayName: 'Run a multi-line script'

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
# These are supported funding model platforms
patreon: yuzuteam

6
.gitmodules vendored
View File

@@ -40,9 +40,3 @@
[submodule "Vulkan-Headers"]
path = externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "externals/zstd"]
path = externals/zstd
url = https://github.com/facebook/zstd
[submodule "sirit"]
path = externals/sirit
url = https://github.com/ReinUsesLisp/sirit

View File

@@ -24,7 +24,7 @@ matrix:
- os: osx
env: NAME="macos build"
sudo: false
osx_image: xcode10.2
osx_image: xcode10.1
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"

View File

@@ -1,3 +1,3 @@
#!/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
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh

View File

@@ -1,3 +1,3 @@
#!/bin/sh -ex
docker pull yuzuemu/build-environments:linux-mingw
docker pull ubuntu:18.04

View File

@@ -1,6 +1,16 @@
#!/bin/bash -ex
cd /yuzu
MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
apt-get update
apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
apt-get update
apt-get install -y ${MINGW_PACKAGES}
# fix a problem in current MinGW headers
wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h
# override Travis CI unreasonable ccache size
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
@@ -13,8 +23,8 @@ echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build && cd build
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
ninja
cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
make -j4
# Clean up the dirty hacks
rm /bin/uname && mv /bin/uname1 /bin/uname

View File

@@ -1,4 +1,4 @@
#!/bin/bash -ex
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-fresh /bin/bash /yuzu/.travis/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -1,3 +1,3 @@
#!/bin/sh -ex
docker pull yuzuemu/build-environments:linux-fresh
docker pull ubuntu:18.04

View File

@@ -1,9 +1,12 @@
#!/bin/bash -ex
apt-get update
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev qtwebengine5-dev wget cmake ninja-build ccache
cd /yuzu
mkdir build && cd build
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
ninja
ccache -s

View File

@@ -7,7 +7,6 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
# TODO: Build using ninja instead of make
mkdir build && cd build
cmake --version
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON

View File

@@ -19,6 +19,18 @@ $(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app" -executable="${R
# move libs into folder for deployment
macpack "${REV_NAME}/yuzu-cmd" -d "libs"
# Make the yuzu.app application launch a debugging terminal.
# Store away the actual binary
mv ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu-bin
cat > ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu <<EOL
#!/usr/bin/env bash
cd "\`dirname "\$0"\`"
chmod +x yuzu-bin
open yuzu-bin --args "\$@"
EOL
# Content that will serve as the launching script for yuzu (within the .app folder)
# Make the launching script executable
chmod +x ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu

View File

@@ -132,7 +132,7 @@ find_package(Threads REQUIRED)
if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.8")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
@@ -165,7 +165,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
if (MSVC)
message(STATUS "unicorn not found, falling back to bundled")
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(UNICORN_VER "unicorn-yuzu")
else()
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
@@ -233,7 +233,7 @@ endif()
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.12.0-msvc2017_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
@@ -309,7 +309,7 @@ if (CLANG_FORMAT)
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
if (WIN32)
add_custom_target(clang-format
COMMAND powershell.exe -Command "Get-ChildItem '${SRCS}/*' -Include *.cpp,*.h -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}"
COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}"
COMMENT ${CCOMMENT})
elseif(MINGW)
add_custom_target(clang-format

View File

@@ -19,7 +19,7 @@ set(BUILD_VERSION "0")
if (BUILD_REPOSITORY)
# regex capture the string nightly or canary into CMAKE_MATCH_1
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if ("${CMAKE_MATCH_COUNT}" GREATER 0)
if (${CMAKE_MATCH_COUNT} GREATER 0)
# capitalize the first letter of each word in the repo name.
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
foreach(WORD ${REPO_NAME_LIST})
@@ -70,7 +70,6 @@ set(HASH_FILES
"${VIDEO_CORE}/shader/decode/half_set.cpp"
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
"${VIDEO_CORE}/shader/decode/image.cpp"
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/memory.cpp"
@@ -82,12 +81,7 @@ set(HASH_FILES
"${VIDEO_CORE}/shader/decode/shift.cpp"
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
"${VIDEO_CORE}/shader/shader_ir.cpp"
"${VIDEO_CORE}/shader/shader_ir.h"
"${VIDEO_CORE}/shader/track.cpp"

View File

@@ -7,7 +7,7 @@ yuzu is an experimental open-source emulator for the Nintendo Switch from the cr
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success.
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. At this time, yuzu cannot play any commercial games without major problems. yuzu can boot some games, to varying degrees of success.
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.

View File

@@ -7,10 +7,6 @@ include(DownloadExternals)
add_library(catch-single-include INTERFACE)
target_include_directories(catch-single-include INTERFACE catch/single_include)
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# Dynarmic
if (ARCHITECTURE_x86_64)
set(DYNARMIC_TESTS OFF)
@@ -18,6 +14,10 @@ if (ARCHITECTURE_x86_64)
add_subdirectory(dynarmic)
endif()
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# getopt
if (MSVC)
add_subdirectory(getopt)
@@ -49,10 +49,6 @@ add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# Zstandard
add_subdirectory(zstd/build/cmake EXCLUDE_FROM_ALL)
target_include_directories(libzstd_static INTERFACE ./zstd/lib)
# SoundTouch
add_subdirectory(soundtouch)
@@ -72,11 +68,6 @@ if (USE_DISCORD_PRESENCE)
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
endif()
# Sirit
if (ENABLE_VULKAN)
add_subdirectory(sirit)
endif()
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")

View File

@@ -90,20 +90,12 @@
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
@@ -119,7 +111,7 @@
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC)
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
externals/sirit vendored

Submodule externals/sirit deleted from f7c4b07a7e

1
externals/zstd vendored

Submodule externals/zstd deleted from 470344d33e

View File

@@ -18,32 +18,15 @@ if (MSVC)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(-DWIN32_LEAN_AND_MEAN)
# Ensure that projects build with Unicode support.
add_definitions(-DUNICODE -D_UNICODE)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /volatile:iso - Use strict standards-compliant volatile semantics.
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
# /Zc:inline - Let codegen omit inline functions in object files
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
add_compile_options(
/W3
/MP
/Zi
/Zo
/permissive-
/EHsc
/std:c++latest
/volatile:iso
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - enhanced debug info for optimized builds
# /permissive- - enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /Zc:throwingNew - let codegen assume `operator new` will never return null
# /Zc:inline - let codegen omit inline functions in object files
add_compile_options(/W3 /MP /Zi /Zo /permissive- /EHsc /std:c++latest /Zc:throwingNew,inline)
# /GS- - No stack buffer overflow checks
add_compile_options("$<$<CONFIG:Release>:/GS->")
@@ -51,10 +34,7 @@ if (MSVC)
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
else()
add_compile_options(
-Wall
-Wno-attributes
)
add_compile_options("-Wno-attributes")
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_compile_options("-stdlib=libc++")
@@ -88,7 +68,6 @@ add_subdirectory(tests)
if (ENABLE_SDL2)
add_subdirectory(yuzu_cmd)
add_subdirectory(yuzu_tester)
endif()
if (ENABLE_QT)

View File

@@ -73,15 +73,13 @@ private:
EffectInStatus info{};
};
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event,
std::size_t instance_number)
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
effects(params.effect_count) {
audio_out = std::make_unique<AudioCore::AudioOut>();
stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
fmt::format("AudioRenderer-Instance{}", instance_number),
[=]() { buffer_event->Signal(); });
"AudioRenderer", [=]() { buffer_event->Signal(); });
audio_out->StartStream(stream);
QueueMixedBuffer(0);
@@ -219,15 +217,13 @@ std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_co
if (offset == samples.size()) {
offset = 0;
if (!wave_buffer.is_looping && wave_buffer.buffer_sz) {
if (!wave_buffer.is_looping) {
SetWaveIndex(wave_index + 1);
}
if (wave_buffer.buffer_sz) {
out_status.wave_buffer_consumed++;
}
out_status.wave_buffer_consumed++;
if (wave_buffer.end_of_stream || wave_buffer.buffer_sz == 0) {
if (wave_buffer.end_of_stream) {
info.play_state = PlayState::Paused;
}
}

View File

@@ -215,8 +215,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
class AudioRenderer {
public:
AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event,
std::size_t instance_number);
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event);
~AudioRenderer();
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);

View File

@@ -51,23 +51,17 @@ void Stream::Stop() {
UNIMPLEMENTED();
}
void Stream::SetVolume(float volume) {
game_volume = volume;
}
Stream::State Stream::GetState() const {
return state;
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
const auto us =
std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate);
return Core::Timing::usToCycles(us);
return Core::Timing::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
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)};
static void VolumeAdjustSamples(std::vector<s16>& samples) {
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
if (volume == 1.0f) {
return;
@@ -101,11 +95,11 @@ void Stream::PlayNextBuffer() {
active_buffer = queued_buffers.front();
queued_buffers.pop();
VolumeAdjustSamples(active_buffer->GetSamples(), game_volume);
VolumeAdjustSamples(active_buffer->GetSamples());
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {});
core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {

View File

@@ -61,12 +61,6 @@ 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;
@@ -100,7 +94,6 @@ 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

@@ -44,7 +44,6 @@ add_custom_command(OUTPUT scm_rev.cpp
"${VIDEO_CORE}/shader/decode/half_set.cpp"
"${VIDEO_CORE}/shader/decode/half_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/hfma2.cpp"
"${VIDEO_CORE}/shader/decode/image.cpp"
"${VIDEO_CORE}/shader/decode/integer_set.cpp"
"${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp"
"${VIDEO_CORE}/shader/decode/memory.cpp"
@@ -56,12 +55,7 @@ add_custom_command(OUTPUT scm_rev.cpp
"${VIDEO_CORE}/shader/decode/shift.cpp"
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
"${VIDEO_CORE}/shader/shader_ir.cpp"
"${VIDEO_CORE}/shader/shader_ir.h"
"${VIDEO_CORE}/shader/track.cpp"
@@ -77,7 +71,6 @@ add_library(common STATIC
assert.h
detached_tasks.cpp
detached_tasks.h
binary_find.h
bit_field.h
bit_util.h
cityhash.cpp
@@ -98,8 +91,6 @@ add_library(common STATIC
logging/log.h
logging/text_formatter.cpp
logging/text_formatter.h
lz4_compression.cpp
lz4_compression.h
math_util.h
memory_hook.cpp
memory_hook.h
@@ -130,12 +121,8 @@ add_library(common STATIC
timer.h
uint128.cpp
uint128.h
uuid.cpp
uuid.h
vector_math.h
web_result.h
zstd_compression.cpp
zstd_compression.h
)
if(ARCHITECTURE_x86_64)
@@ -149,4 +136,3 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
target_link_libraries(common PRIVATE lz4_static libzstd_static)

View File

@@ -19,12 +19,6 @@ constexpr T AlignDown(T value, std::size_t size) {
return static_cast<T>(value - value % size);
}
template <typename T>
constexpr T AlignBits(T value, std::size_t align) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
}
template <typename T>
constexpr bool Is4KBAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");

View File

@@ -57,21 +57,3 @@ __declspec(noinline, noreturn)
#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
// If the assert is ignored, execute _b_
#define ASSERT_OR_EXECUTE(_a_, _b_) \
do { \
ASSERT(_a_); \
if (!(_a_)) { \
_b_ \
} \
} while (0)
// If the assert is ignored, execute _b_
#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
do { \
ASSERT_MSG(_a_, __VA_ARGS__); \
if (!(_a_)) { \
_b_ \
} \
} while (0)

View File

@@ -1,21 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
namespace Common {
template <class ForwardIt, class T, class Compare = std::less<>>
ForwardIt BinaryFind(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) {
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
first = std::lower_bound(first, last, value, comp);
return first != last && !comp(value, *first) ? first : last;
}
} // namespace Common

View File

@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return 32;
}
inline u32 CountLeadingZeroes64(u64 value) {
inline u64 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -47,15 +47,15 @@ inline u32 CountLeadingZeroes32(u32 value) {
return 32;
}
return static_cast<u32>(__builtin_clz(value));
return __builtin_clz(value);
}
inline u32 CountLeadingZeroes64(u64 value) {
inline u64 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_clzll(value));
return __builtin_clzll(value);
}
#endif
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return 32;
}
inline u32 CountTrailingZeroes64(u64 value) {
inline u64 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -85,60 +85,16 @@ inline u32 CountTrailingZeroes32(u32 value) {
return 32;
}
return static_cast<u32>(__builtin_ctz(value));
return __builtin_ctz(value);
}
inline u32 CountTrailingZeroes64(u64 value) {
inline u64 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_ctzll(value));
return __builtin_ctzll(value);
}
#endif
#ifdef _MSC_VER
inline u32 MostSignificantBit32(const u32 value) {
unsigned long result;
_BitScanReverse(&result, value);
return static_cast<u32>(result);
}
inline u32 MostSignificantBit64(const u64 value) {
unsigned long result;
_BitScanReverse64(&result, value);
return static_cast<u32>(result);
}
#else
inline u32 MostSignificantBit32(const u32 value) {
return 31U - static_cast<u32>(__builtin_clz(value));
}
inline u32 MostSignificantBit64(const u64 value) {
return 63U - static_cast<u32>(__builtin_clzll(value));
}
#endif
inline u32 Log2Floor32(const u32 value) {
return MostSignificantBit32(value);
}
inline u32 Log2Ceil32(const u32 value) {
const u32 log2_f = Log2Floor32(value);
return log2_f + ((value ^ (1U << log2_f)) != 0U);
}
inline u32 Log2Floor64(const u64 value) {
return MostSignificantBit64(value);
}
inline u32 Log2Ceil64(const u64 value) {
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
}
} // namespace Common

View File

@@ -4,7 +4,6 @@
#pragma once
#include <algorithm>
#include <string>
#if !defined(ARCHITECTURE_x86_64)

View File

@@ -78,17 +78,16 @@ namespace FileUtil {
// Remove any ending forward slashes from directory paths
// Modifies argument.
static void StripTailDirSlashes(std::string& fname) {
if (fname.length() <= 1) {
return;
if (fname.length() > 1) {
std::size_t i = fname.length();
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
--i;
fname.resize(i);
}
std::size_t i = fname.length();
while (i > 0 && fname[i - 1] == DIR_SEP_CHR) {
--i;
}
fname.resize(i);
return;
}
// Returns true if file filename exists
bool Exists(const std::string& filename) {
struct stat file_info;
@@ -108,6 +107,7 @@ bool Exists(const std::string& filename) {
return (result == 0);
}
// Returns true if filename is a directory
bool IsDirectory(const std::string& filename) {
struct stat file_info;
@@ -132,6 +132,8 @@ bool IsDirectory(const std::string& filename) {
return S_ISDIR(file_info.st_mode);
}
// Deletes a given filename, return true on success
// Doesn't supports deleting a directory
bool Delete(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "file {}", filename);
@@ -163,6 +165,7 @@ bool Delete(const std::string& filename) {
return true;
}
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& path) {
LOG_TRACE(Common_Filesystem, "directory {}", path);
#ifdef _WIN32
@@ -191,6 +194,7 @@ bool CreateDir(const std::string& path) {
#endif
}
// Creates the full path of fullPath returns true on success
bool CreateFullPath(const std::string& fullPath) {
int panicCounter = 100;
LOG_TRACE(Common_Filesystem, "path {}", fullPath);
@@ -226,6 +230,7 @@ bool CreateFullPath(const std::string& fullPath) {
}
}
// Deletes a directory filename, returns true on success
bool DeleteDir(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "directory {}", filename);
@@ -247,6 +252,7 @@ bool DeleteDir(const std::string& filename) {
return false;
}
// renames file srcFilename to destFilename, returns true on success
bool Rename(const std::string& srcFilename, const std::string& destFilename) {
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
#ifdef _WIN32
@@ -262,6 +268,7 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
return false;
}
// copies file srcFilename to destFilename, returns true on success
bool Copy(const std::string& srcFilename, const std::string& destFilename) {
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename);
#ifdef _WIN32
@@ -317,6 +324,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
#endif
}
// Returns the size of filename (64bit)
u64 GetSize(const std::string& filename) {
if (!Exists(filename)) {
LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename);
@@ -343,6 +351,7 @@ u64 GetSize(const std::string& filename) {
return 0;
}
// Overloaded GetSize, accepts file descriptor
u64 GetSize(const int fd) {
struct stat buf;
if (fstat(fd, &buf) != 0) {
@@ -352,6 +361,7 @@ u64 GetSize(const int fd) {
return buf.st_size;
}
// Overloaded GetSize, accepts FILE*
u64 GetSize(FILE* f) {
// can't use off_t here because it can be 32-bit
u64 pos = ftello(f);
@@ -367,6 +377,7 @@ u64 GetSize(FILE* f) {
return size;
}
// creates an empty file filename, returns true on success
bool CreateEmptyFile(const std::string& filename) {
LOG_TRACE(Common_Filesystem, "{}", filename);
@@ -491,6 +502,7 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
return true;
}
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path) {
#ifndef _WIN32
if (source_path == dest_path)
@@ -527,7 +539,8 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
#endif
}
std::optional<std::string> GetCurrentDir() {
// Returns the current directory
std::string GetCurrentDir() {
// Get the current working directory (getcwd uses malloc)
#ifdef _WIN32
wchar_t* dir;
@@ -537,7 +550,7 @@ std::optional<std::string> GetCurrentDir() {
if (!(dir = getcwd(nullptr, 0))) {
#endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
return {};
return nullptr;
}
#ifdef _WIN32
std::string strDir = Common::UTF16ToUTF8(dir);
@@ -548,6 +561,7 @@ std::optional<std::string> GetCurrentDir() {
return strDir;
}
// Sets the current directory to the given directory
bool SetCurrentDir(const std::string& directory) {
#ifdef _WIN32
return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0;
@@ -659,6 +673,8 @@ std::string GetSysDirectory() {
return sysDir;
}
// Returns a string with a yuzu data dir or file in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path) {
static std::unordered_map<UserPath, std::string> paths;
auto& user_path = paths[UserPath::UserDir];
@@ -746,11 +762,11 @@ std::string GetNANDRegistrationDir(bool system) {
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
}
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) {
return IOFile(filename, text_file ? "w" : "wb").WriteString(str);
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
}
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str) {
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
IOFile file(filename, text_file ? "r" : "rb");
if (!file.IsOpen())
@@ -760,6 +776,13 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
return file.ReadArray(&str[0], str.size());
}
/**
* Splits the filename into 8.3 format
* Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
* @param filename The normal filename to use
* @param short_name A 9-char array in which the short name will be written
* @param extension A 4-char array in which the extension will be written
*/
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension) {
const std::string forbidden_characters = ".\"/\\[]:;=, ";

View File

@@ -9,7 +9,6 @@
#include <fstream>
#include <functional>
#include <limits>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
@@ -119,7 +118,7 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
// Returns the current directory
std::optional<std::string> GetCurrentDir();
std::string GetCurrentDir();
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string& source_path, const std::string& dest_path);
@@ -147,9 +146,9 @@ const std::string& GetExeDirectory();
std::string AppDataRoamingDirectory();
#endif
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str);
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
/**
* Splits the filename into 8.3 format
@@ -258,8 +257,8 @@ public:
return WriteArray(&object, 1);
}
std::size_t WriteString(std::string_view str) {
return WriteArray(str.data(), str.length());
std::size_t WriteString(const std::string& str) {
return WriteArray(str.c_str(), str.length());
}
bool IsOpen() const {
@@ -287,8 +286,8 @@ private:
template <typename T>
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
#ifdef _MSC_VER
fstream.open(Common::UTF8ToUTF16W(filename), openmode);
fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode);
#else
fstream.open(filename, openmode);
fstream.open(filename.c_str(), openmode);
#endif
}

View File

@@ -30,6 +30,13 @@ 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,7 +7,6 @@
#include <array>
#include <cstddef>
#include <string>
#include <type_traits>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
@@ -31,20 +30,13 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
return out;
}
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;
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
template <std::size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;
out.reserve(std::size(data) * pad_width);
for (const u8 c : data) {
for (u8 c : array)
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
}
return out;
}

View File

@@ -1,76 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <lz4hc.h>
#include "common/assert.h"
#include "common/lz4_compression.h"
namespace Common::Compression {
std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) {
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
const auto source_size_int = static_cast<int>(source_size);
const int max_compressed_size = LZ4_compressBound(source_size_int);
std::vector<u8> compressed(max_compressed_size);
const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
reinterpret_cast<char*>(compressed.data()),
source_size_int, max_compressed_size);
if (compressed_size <= 0) {
// Compression failed
return {};
}
compressed.resize(compressed_size);
return compressed;
}
std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
s32 compression_level) {
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
const auto source_size_int = static_cast<int>(source_size);
const int max_compressed_size = LZ4_compressBound(source_size_int);
std::vector<u8> compressed(max_compressed_size);
const int compressed_size = LZ4_compress_HC(
reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
source_size_int, max_compressed_size, compression_level);
if (compressed_size <= 0) {
// Compression failed
return {};
}
compressed.resize(compressed_size);
return compressed;
}
std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size) {
return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX);
}
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
std::size_t uncompressed_size) {
std::vector<u8> uncompressed(uncompressed_size);
const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()),
reinterpret_cast<char*>(uncompressed.data()),
static_cast<int>(compressed.size()),
static_cast<int>(uncompressed.size()));
if (static_cast<int>(uncompressed_size) != size_check) {
// Decompression failed
return {};
}
return uncompressed;
}
} // namespace Common::Compression

View File

@@ -1,57 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common::Compression {
/**
* Compresses a source memory region with LZ4 and returns the compressed data in a vector.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
* levels result in a smaller compressed size, but require more CPU time for compression. The
* compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
* also be decompressed with the default LZ4 decompression.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
* @param compression_level the used compression level. Should be between 3 and 12.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
/**
* Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
*
* @param compressed the compressed source memory region.
* @param uncompressed_size the size in bytes of the uncompressed data.
*
* @return the decompressed data.
*/
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
} // namespace Common::Compression

View File

@@ -41,7 +41,4 @@ struct Rectangle {
}
};
template <typename T>
Rectangle(T, T, T, T)->Rectangle<T>;
} // namespace Common

View File

@@ -72,7 +72,7 @@ public:
u64 prios = mlq.used_priorities;
prios &= ~((1ULL << (current_priority + 1)) - 1);
if (prios == 0) {
current_priority = static_cast<u32>(mlq.depth());
current_priority = mlq.depth();
} else {
current_priority = CountTrailingZeroes64(prios);
it = GetBeginItForPrio();

View File

@@ -20,7 +20,7 @@ struct ScopeExitHelper {
template <typename Func>
ScopeExitHelper<Func> ScopeExit(Func&& func) {
return ScopeExitHelper<Func>(std::forward<Func>(func));
return ScopeExitHelper<Func>(std::move(func));
}
} // namespace detail

View File

@@ -21,6 +21,11 @@
#if defined(_MSC_VER)
#include <cstdlib>
#elif defined(__linux__)
#include <byteswap.h>
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/endian.h>
#endif
#include <cstring>
#include "common/common_types.h"
@@ -57,49 +62,86 @@
namespace Common {
#ifdef _MSC_VER
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return _byteswap_ushort(data);
inline u16 swap16(u16 _data) {
return _byteswap_ushort(_data);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return _byteswap_ulong(data);
inline u32 swap32(u32 _data) {
return _byteswap_ulong(_data);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return _byteswap_uint64(data);
inline u64 swap64(u64 _data) {
return _byteswap_uint64(_data);
}
#elif defined(__clang__) || defined(__GNUC__)
#if defined(__Bitrig__) || defined(__OpenBSD__)
#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6)
inline u16 swap16(u16 _data) {
u32 data = _data;
__asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
return (u16)data;
}
inline u32 swap32(u32 _data) {
__asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data));
return _data;
}
inline u64 swap64(u64 _data) {
return ((u64)swap32(_data) << 32) | swap32(_data >> 32);
}
#elif __linux__
inline u16 swap16(u16 _data) {
return bswap_16(_data);
}
inline u32 swap32(u32 _data) {
return bswap_32(_data);
}
inline u64 swap64(u64 _data) {
return bswap_64(_data);
}
#elif __APPLE__
inline __attribute__((always_inline)) u16 swap16(u16 _data) {
return (_data >> 8) | (_data << 8);
}
inline __attribute__((always_inline)) u32 swap32(u32 _data) {
return __builtin_bswap32(_data);
}
inline __attribute__((always_inline)) u64 swap64(u64 _data) {
return __builtin_bswap64(_data);
}
#elif defined(__Bitrig__) || defined(__OpenBSD__)
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
#endif
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return __builtin_bswap16(data);
inline u16 swap16(u16 _data) {
return __swap16(_data);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return __builtin_bswap32(data);
inline u32 swap32(u32 _data) {
return __swap32(_data);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return __builtin_bswap64(data);
inline u64 swap64(u64 _data) {
return __swap64(_data);
}
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
inline u16 swap16(u16 _data) {
return bswap16(_data);
}
inline u32 swap32(u32 _data) {
return bswap32(_data);
}
inline u64 swap64(u64 _data) {
return bswap64(_data);
}
#else
// Generic implementation.
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
// Slow generic implementation.
inline u16 swap16(u16 data) {
return (data >> 8) | (data << 8);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) |
((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
inline u32 swap32(u32 data) {
return (swap16(data) << 16) | swap16(data >> 16);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) |
((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) |
((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
inline u64 swap64(u64 data) {
return ((u64)swap32(data) << 32) | swap32(data >> 32);
}
#endif
[[nodiscard]] inline float swapf(float f) noexcept {
inline float swapf(float f) {
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
u32 value;
@@ -111,7 +153,7 @@ namespace Common {
return f;
}
[[nodiscard]] inline double swapd(double f) noexcept {
inline double swapd(double f) {
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
u64 value;

View File

@@ -1,33 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <random>
#include <fmt/format.h>
#include "common/uuid.h"
namespace Common {
UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return UUID{distribution(gen), distribution(gen)};
}
std::string UUID::Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
std::string UUID::FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
}
} // namespace Common

View File

@@ -1,48 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
namespace Common {
constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid = INVALID_UUID;
constexpr UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
constexpr explicit operator bool() const {
return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1];
}
constexpr bool operator==(const UUID& rhs) const {
// TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20
return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1];
}
constexpr bool operator!=(const UUID& rhs) const {
return !operator==(rhs);
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
constexpr void Invalidate() {
uuid = INVALID_UUID;
}
std::string Format() const;
std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
} // namespace Common

View File

@@ -1,51 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <zstd.h>
#include "common/assert.h"
#include "common/zstd_compression.h"
namespace Common::Compression {
std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level) {
compression_level = std::clamp(compression_level, 1, ZSTD_maxCLevel());
const std::size_t max_compressed_size = ZSTD_compressBound(source_size);
std::vector<u8> compressed(max_compressed_size);
const std::size_t compressed_size =
ZSTD_compress(compressed.data(), compressed.size(), source, source_size, compression_level);
if (ZSTD_isError(compressed_size)) {
// Compression failed
return {};
}
compressed.resize(compressed_size);
return compressed;
}
std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size) {
return CompressDataZSTD(source, source_size, ZSTD_CLEVEL_DEFAULT);
}
std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed) {
const std::size_t decompressed_size =
ZSTD_getDecompressedSize(compressed.data(), compressed.size());
std::vector<u8> decompressed(decompressed_size);
const std::size_t uncompressed_result_size = ZSTD_decompress(
decompressed.data(), decompressed.size(), compressed.data(), compressed.size());
if (decompressed_size != uncompressed_result_size || ZSTD_isError(uncompressed_result_size)) {
// Decompression failed
return {};
}
return decompressed;
}
} // namespace Common::Compression

View File

@@ -1,44 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common::Compression {
/**
* Compresses a source memory region with Zstandard and returns the compressed data in a vector.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
* @param compression_level the used compression level. Should be between 1 and 22.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level);
/**
* Compresses a source memory region with Zstandard with the default compression level and returns
* the compressed data in a vector.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
/**
* Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector.
*
* @param compressed the compressed source memory region.
*
* @return the decompressed data.
*/
std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
} // namespace Common::Compression

View File

@@ -5,8 +5,6 @@ add_library(core STATIC
arm/exclusive_monitor.h
arm/unicorn/arm_unicorn.cpp
arm/unicorn/arm_unicorn.h
constants.cpp
constants.h
core.cpp
core.h
core_cpu.cpp
@@ -45,8 +43,6 @@ 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
@@ -92,10 +88,6 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/error.cpp
frontend/applets/error.h
frontend/applets/general_frontend.cpp
frontend/applets/general_frontend.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
@@ -175,7 +167,6 @@ add_library(core STATIC
hle/service/acc/acc_u0.h
hle/service/acc/acc_u1.cpp
hle/service/acc/acc_u1.h
hle/service/acc/errors.h
hle/service/acc/profile_manager.cpp
hle/service/acc/profile_manager.h
hle/service/am/am.cpp
@@ -186,14 +177,12 @@ add_library(core STATIC
hle/service/am/applet_oe.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
hle/service/am/applets/error.cpp
hle/service/am/applets/error.h
hle/service/am/applets/general_backend.cpp
hle/service/am/applets/general_backend.h
hle/service/am/applets/profile_select.cpp
hle/service/am/applets/profile_select.h
hle/service/am/applets/software_keyboard.cpp
hle/service/am/applets/software_keyboard.h
hle/service/am/applets/stub_applet.cpp
hle/service/am/applets/stub_applet.h
hle/service/am/applets/web_browser.cpp
hle/service/am/applets/web_browser.h
hle/service/am/idle.cpp
@@ -208,10 +197,10 @@ add_library(core STATIC
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
hle/service/apm/apm.h
hle/service/apm/controller.cpp
hle/service/apm/controller.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
@@ -273,20 +262,10 @@ add_library(core STATIC
hle/service/filesystem/fsp_srv.h
hle/service/fgm/fgm.cpp
hle/service/fgm/fgm.h
hle/service/friend/errors.h
hle/service/friend/friend.cpp
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
@@ -295,7 +274,6 @@ add_library(core STATIC
hle/service/hid/irs.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/errors.h
hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h
hle/service/hid/controllers/debug_pad.cpp
@@ -326,8 +304,6 @@ add_library(core STATIC
hle/service/mig/mig.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mii/mii_manager.cpp
hle/service/mii/mii_manager.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/ncm/ncm.cpp
@@ -344,9 +320,6 @@ add_library(core STATIC
hle/service/nim/nim.h
hle/service/npns/npns.cpp
hle/service/npns/npns.h
hle/service/ns/errors.h
hle/service/ns/language.cpp
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pl_u.cpp
@@ -434,8 +407,6 @@ add_library(core STATIC
hle/service/time/interface.h
hle/service/time/time.cpp
hle/service/time/time.h
hle/service/time/time_sharedmemory.cpp
hle/service/time/time_sharedmemory.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/display/vi_display.cpp
@@ -456,8 +427,6 @@ 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
@@ -477,20 +446,19 @@ 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
tools/freezer.cpp
tools/freezer.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 json-headers mbedtls opus unicorn open_source_archives)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static 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,213 +2,26 @@
// 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();
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;
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
while (true) {
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});
LOG_ERROR(Core_ARM, "{:016X}", lr);
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,13 +5,8 @@
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
namespace Common {
struct PageTable;
}
namespace Kernel {
enum class VMAPermission : u8;
}
@@ -44,17 +39,18 @@ public:
/// Step CPU by one instruction
virtual void Step() = 0;
/// Maps a backing memory region for the CPU
virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) = 0;
/// Unmaps a region of memory that was previously mapped using MapBackingMemory
virtual void UnmapMemory(VAddr address, std::size_t size) = 0;
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
/// Notifies CPU emulation that the current page table has changed.
///
/// @param new_page_table The new page table.
/// @param new_address_space_size_in_bits The new usable size of the address space in bits.
/// This can be either 32, 36, or 39 on official software.
///
virtual void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) = 0;
/// Notify CPU emulation that page tables have changed
virtual void PageTableChanged() = 0;
/**
* Set the Program Counter to an address
@@ -146,16 +142,6 @@ 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

@@ -26,6 +26,7 @@ using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
public:
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
~ARM_Dynarmic_Callbacks() = default;
u8 MemoryRead8(u64 vaddr) override {
return Memory::Read8(vaddr);
@@ -99,7 +100,7 @@ public:
}
void CallSVC(u32 swi) override {
Kernel::CallSVC(parent.system, swi);
Kernel::CallSVC(swi);
}
void AddTicks(u64 ticks) override {
@@ -112,14 +113,14 @@ public:
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
parent.system.CoreTiming().AddTicks(amortized_ticks);
parent.core_timing.AddTicks(amortized_ticks);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
return std::max(parent.system.CoreTiming().GetDowncount(), 0);
return std::max(parent.core_timing.GetDowncount(), 0);
}
u64 GetCNTPCT() override {
return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
}
ARM_Dynarmic& parent;
@@ -128,16 +129,18 @@ public:
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const {
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
auto* current_process = Core::CurrentProcess();
auto** const page_table = current_process->VMManager().page_table.pointers.data();
Dynarmic::A64::UserConfig config;
// Callbacks
config.callbacks = cb.get();
// Memory
config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
config.page_table_address_space_bits = address_space_bits;
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth();
config.silently_mirror_page_table = false;
// Multi-process state
@@ -161,6 +164,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)
void ARM_Dynarmic::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
jit->Run();
}
@@ -169,14 +173,28 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
core_index{core_index}, system{system},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
core_index{core_index}, core_timing{core_timing},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
ThreadContext ctx{};
inner_unicorn.SaveContext(ctx);
PageTableChanged();
LoadContext(ctx);
}
ARM_Dynarmic::~ARM_Dynarmic() = default;
void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) {
inner_unicorn.MapBackingMemory(address, size, memory, perms);
}
void ARM_Dynarmic::UnmapMemory(u64 address, std::size_t size) {
inner_unicorn.UnmapMemory(address, size);
}
void ARM_Dynarmic::SetPC(u64 pc) {
jit->SetPC(pc);
}
@@ -259,9 +277,9 @@ void ARM_Dynarmic::ClearExclusiveState() {
jit->ClearExclusiveState();
}
void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
std::size_t new_address_space_size_in_bits) {
jit = MakeJit(page_table, new_address_space_size_in_bits);
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit();
current_page_table = Memory::GetCurrentPageTable();
}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}

View File

@@ -12,17 +12,28 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Common {
struct PageTable;
}
namespace Core::Timing {
class CoreTiming;
}
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic() override;
ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index);
~ARM_Dynarmic();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) override;
void UnmapMemory(u64 address, std::size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
@@ -45,12 +56,10 @@ public:
void ClearExclusiveState() override;
void ClearInstructionCache() override;
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
void PageTableChanged() override;
private:
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const;
std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const;
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
@@ -58,14 +67,16 @@ private:
ARM_Unicorn inner_unicorn;
std::size_t core_index;
System& system;
Timing::CoreTiming& core_timing;
DynarmicExclusiveMonitor& exclusive_monitor;
Common::PageTable* current_page_table = nullptr;
};
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public:
explicit DynarmicExclusiveMonitor(std::size_t core_count);
~DynarmicExclusiveMonitor() override;
~DynarmicExclusiveMonitor();
void SetExclusive(std::size_t core_index, VAddr addr) override;
void ClearExclusive() override;

View File

@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {
@@ -48,19 +49,30 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_
}
}
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
void* user_data) {
auto* const system = static_cast<System*>(user_data);
static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
ARM_Interface::ThreadContext ctx{};
system->CurrentArmInterface().SaveContext(ctx);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
ctx.pc, ctx.cpu_registers[30]);
auto ec = esr >> 26;
auto iss = esr & 0xFFFFFF;
return false;
switch (ec) {
case 0x15: // SVC
Kernel::CallSVC(iss);
break;
}
}
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
void* user_data) {
ARM_Interface::ThreadContext ctx{};
Core::CurrentArmInterface().SaveContext(ctx);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
ctx.pc, ctx.cpu_registers[30]);
return {};
}
ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
auto fpv = 3 << 20;
@@ -68,7 +80,7 @@ ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
uc_hook hook{};
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0, -1));
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, 0, -1));
if (GDBStub::IsServerEnabled()) {
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
last_bkpt_hit = false;
@@ -79,6 +91,15 @@ ARM_Unicorn::~ARM_Unicorn() {
CHECKED(uc_close(uc));
}
void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) {
CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
}
void ARM_Unicorn::UnmapMemory(VAddr address, std::size_t size) {
CHECKED(uc_mem_unmap(uc, address, size));
}
void ARM_Unicorn::SetPC(u64 pc) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
}
@@ -156,7 +177,7 @@ void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
} else {
ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0));
ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
}
}
@@ -169,15 +190,14 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
system.CoreTiming().AddTicks(num_instructions);
core_timing.AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
if (last_bkpt_hit) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->GetContext());
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
@@ -252,20 +272,4 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt_hit = true;
}
void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
const auto ec = esr >> 26;
const auto iss = esr & 0xFFFFFF;
auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
switch (ec) {
case 0x15: // SVC
Kernel::CallSVC(arm_instance->system, iss);
break;
}
}
} // namespace Core

View File

@@ -9,15 +9,20 @@
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
namespace Core {
namespace Core::Timing {
class CoreTiming;
}
class System;
namespace Core {
class ARM_Unicorn final : public ARM_Interface {
public:
explicit ARM_Unicorn(System& system);
~ARM_Unicorn() override;
explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
~ARM_Unicorn();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) override;
void UnmapMemory(VAddr address, std::size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
@@ -38,16 +43,14 @@ public:
void Run() override;
void Step() override;
void ClearInstructionCache() override;
void PageTableChanged(Common::PageTable&, std::size_t) override {}
void PageTableChanged() override{};
void RecordBreak(GDBStub::BreakpointAddress bkpt);
private:
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
uc_engine* uc{};
System& system;
Timing::CoreTiming& core_timing;
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit = false;
bool last_bkpt_hit;
};
} // namespace Core

View File

@@ -1,17 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/constants.h"
namespace Core::Constants {
const std::array<u8, 107> ACCOUNT_BACKUP_JPEG{{
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
}};
}

View File

@@ -1,17 +0,0 @@
// 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"
// This is to consolidate system-wide constants that are used by multiple components of yuzu.
// This is especially to prevent the case of something in frontend duplicating a constexpr array or
// directly including some service header for the sole purpose of data.
namespace Core::Constants {
// ACC Service - Blank JPEG used as user icon in absentia of real one.
extern const std::array<u8, 107> ACCOUNT_BACKUP_JPEG;
} // namespace Core::Constants

View File

@@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include <array>
#include <map>
#include <memory>
#include <thread>
#include <utility>
#include "common/file_util.h"
@@ -15,7 +17,6 @@
#include "core/core_timing.h"
#include "core/cpu_core_manager.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
@@ -24,49 +25,25 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/am/applets/software_keyboard.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 "core/tools/freezer.h"
#include "file_sys/cheat_engine.h"
#include "file_sys/patch_manager.h"
#include "frontend/applets/profile_select.h"
#include "frontend/applets/software_keyboard.h"
#include "frontend/applets/web_browser.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu_asynch.h"
#include "video_core/gpu_synch.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,
@@ -103,7 +80,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}, reporter{system} {}
explicit Impl(System& system) : kernel{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -121,7 +98,6 @@ struct System::Impl {
LOG_DEBUG(HW_Memory, "initialized OK");
core_timing.Initialize();
cpu_core_manager.Initialize();
kernel.Initialize();
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
@@ -132,19 +108,22 @@ struct System::Impl {
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
if (content_provider == nullptr)
content_provider = std::make_unique<FileSys::ContentProviderUnion>();
/// Create default implementations of applets if one is not provided.
applet_manager.SetDefaultAppletsIfMissing();
if (profile_selector == nullptr)
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
if (software_keyboard == nullptr)
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
if (web_browser == nullptr)
web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
/// Reset all glue registrations
arp_manager.ResetAll();
auto main_process = Kernel::Process::Create(system, "main");
kernel.MakeCurrentProcess(main_process.get());
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, system);
Service::Init(service_manager, system, *virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window, system);
@@ -152,10 +131,16 @@ struct System::Impl {
return ResultStatus::ErrorVideoCore;
}
gpu_core = VideoCore::CreateGPU(system);
is_powered_on = true;
if (Settings::values.use_asynchronous_gpu_emulation) {
gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
} else {
gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
}
cpu_core_manager.Initialize(system);
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
@@ -168,10 +153,20 @@ struct System::Impl {
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
}
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
if (system_mode.second != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second));
return ResultStatus::ErrorSystemMode;
}
ResultStatus init_result{Init(system, emu_window)};
if (init_result != ResultStatus::Success) {
@@ -181,10 +176,7 @@ struct System::Impl {
return init_result;
}
telemetry_session->AddInitialInfo(*app_loader);
auto main_process =
Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
@@ -192,17 +184,6 @@ 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.
// Begin GPU and CPU execution.
gpu_core->Start();
cpu_core_manager.StartThreads();
// All threads are started, begin main process execution, now that we're in the clear.
main_process->Run(load_parameters->main_thread_priority,
load_parameters->main_thread_stack_size);
status = ResultStatus::Success;
return status;
@@ -240,7 +221,9 @@ struct System::Impl {
app_loader.reset();
// Clear all applets
applet_manager.ClearAll();
profile_selector.reset();
software_keyboard.reset();
web_browser.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
@@ -251,31 +234,6 @@ 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) {
@@ -291,8 +249,6 @@ struct System::Impl {
Kernel::KernelCore kernel;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// ContentProviderUnion instance
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -302,16 +258,11 @@ struct System::Impl {
bool is_powered_on = false;
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
std::unique_ptr<Tools::Freezer> memory_freezer;
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
/// APM (Performance) services
Service::APM::Controller apm_controller{core_timing};
/// Glue services
Service::Glue::ARPManager arp_manager;
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -319,8 +270,6 @@ struct System::Impl {
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
Reporter reporter;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
@@ -523,61 +472,32 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
impl->applet_manager.SetAppletFrontendSet(std::move(set));
void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
impl->profile_selector = std::move(applet);
}
void System::SetDefaultAppletFrontendSet() {
impl->applet_manager.SetDefaultAppletFrontendSet();
const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
return *impl->profile_selector;
}
Service::AM::Applets::AppletManager& System::GetAppletManager() {
return impl->applet_manager;
void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
impl->software_keyboard = std::move(applet);
}
const Service::AM::Applets::AppletManager& System::GetAppletManager() const {
return impl->applet_manager;
const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
return *impl->software_keyboard;
}
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
impl->content_provider = std::move(provider);
void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
impl->web_browser = std::move(applet);
}
FileSys::ContentProvider& System::GetContentProvider() {
return *impl->content_provider;
Frontend::WebBrowserApplet& System::GetWebBrowser() {
return *impl->web_browser;
}
const FileSys::ContentProvider& System::GetContentProvider() const {
return *impl->content_provider;
}
void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider) {
impl->content_provider->SetSlot(slot, provider);
}
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;
}
Service::APM::Controller& System::GetAPMController() {
return impl->apm_controller;
}
const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
return *impl->web_browser;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {

View File

@@ -8,20 +8,19 @@
#include <memory>
#include <string>
#include <map>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/kernel/object.h"
namespace Core::Frontend {
class EmuWindow;
class ProfileSelectApplet;
class SoftwareKeyboardApplet;
class WebBrowserApplet;
} // namespace Core::Frontend
namespace FileSys {
class CheatList;
class ContentProvider;
class ContentProviderUnion;
enum class ContentProviderUnionSlot;
class VfsFilesystem;
} // namespace FileSys
@@ -36,26 +35,9 @@ class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
namespace Service {
namespace AM::Applets {
struct AppletFrontendSet;
class AppletManager;
} // namespace AM::Applets
namespace APM {
class Controller;
}
namespace Glue {
class ARPManager;
}
namespace SM {
namespace Service::SM {
class ServiceManager;
} // namespace SM
} // namespace Service
} // namespace Service::SM
namespace Tegra {
class DebugContext;
@@ -77,7 +59,6 @@ class Cpu;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
class Reporter;
class TelemetrySession;
struct PerfStatsResults;
@@ -108,6 +89,7 @@ public:
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorSystemFiles, ///< Error in finding system files
ErrorSharedFont, ///< Error in finding shared font
ErrorVideoCore, ///< Error in the video core
@@ -275,34 +257,18 @@ public:
void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
void SetDefaultAppletFrontendSet();
const Frontend::ProfileSelectApplet& GetProfileSelector() const;
Service::AM::Applets::AppletManager& GetAppletManager();
void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
const Service::AM::Applets::AppletManager& GetAppletManager() const;
const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
FileSys::ContentProvider& GetContentProvider();
const FileSys::ContentProvider& GetContentProvider() const;
void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider);
void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
const Reporter& GetReporter() const;
Service::Glue::ARPManager& GetARPManager();
const Service::Glue::ARPManager& GetARPManager() const;
Service::APM::Controller& GetAPMController();
const Service::APM::Controller& GetAPMController() const;
Frontend::WebBrowserApplet& GetWebBrowser();
const Frontend::WebBrowserApplet& GetWebBrowser() const;
private:
System();
@@ -327,6 +293,10 @@ private:
static System s_instance;
};
inline ARM_Interface& CurrentArmInterface() {
return System::GetInstance().CurrentArmInterface();
}
inline Kernel::Process* CurrentProcess() {
return System::GetInstance().CurrentProcess();
}

View File

@@ -53,12 +53,16 @@ bool CpuBarrier::Rendezvous() {
Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
std::size_t core_index)
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
#else
arm_interface = std::make_unique<ARM_Unicorn>(system);
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
arm_interface = std::make_unique<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
}
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
}
@@ -66,12 +70,15 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
Cpu::~Cpu() = default;
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
#else
// TODO(merry): Passthrough exclusive monitor
return nullptr;
return nullptr; // TODO(merry): Passthrough exclusive monitor
#endif
} else {
return nullptr; // TODO(merry): Passthrough exclusive monitor
}
}
void Cpu::RunLoop(bool tight_loop) {

View File

@@ -56,12 +56,12 @@ void CoreTiming::Initialize() {
}
void CoreTiming::Shutdown() {
MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
}
EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
std::lock_guard guard{inner_mutex};
// check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization.
ASSERT_MSG(event_types.find(name) == event_types.end(),
@@ -82,7 +82,6 @@ void CoreTiming::UnregisterAllEvents() {
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
ASSERT(event_type != nullptr);
std::lock_guard guard{inner_mutex};
const s64 timeout = GetTicks() + cycles_into_future;
// If this event needs to be scheduled before the next advance(), force one early
@@ -94,8 +93,12 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
u64 userdata) {
ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
}
void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
std::lock_guard guard{inner_mutex};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type == event_type && e.userdata == userdata;
});
@@ -107,6 +110,10 @@ void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
}
}
void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
unschedule_queue.Push(std::make_pair(event_type, userdata));
}
u64 CoreTiming::GetTicks() const {
u64 ticks = static_cast<u64>(global_timer);
if (!is_global_timer_sane) {
@@ -128,7 +135,6 @@ void CoreTiming::ClearPendingEvents() {
}
void CoreTiming::RemoveEvent(const EventType* event_type) {
std::lock_guard guard{inner_mutex};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
@@ -139,6 +145,11 @@ void CoreTiming::RemoveEvent(const EventType* event_type) {
}
}
void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
MoveEvents();
RemoveEvent(event_type);
}
void CoreTiming::ForceExceptionCheck(s64 cycles) {
cycles = std::max<s64>(0, cycles);
if (downcount <= cycles) {
@@ -151,8 +162,19 @@ void CoreTiming::ForceExceptionCheck(s64 cycles) {
downcount = static_cast<int>(cycles);
}
void CoreTiming::MoveEvents() {
for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev));
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
void CoreTiming::Advance() {
std::unique_lock<std::mutex> guard(inner_mutex);
MoveEvents();
for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
UnscheduleEvent(ev.first, ev.second);
}
const int cycles_executed = slice_length - downcount;
global_timer += cycles_executed;
@@ -164,9 +186,7 @@ void CoreTiming::Advance() {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
inner_mutex.unlock();
evt.type->callback(evt.userdata, global_timer - evt.time);
inner_mutex.lock();
}
is_global_timer_sane = false;

View File

@@ -6,7 +6,6 @@
#include <chrono>
#include <functional>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
@@ -68,7 +67,7 @@ public:
///
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
/// Unregisters all registered events thus far. Note: not thread unsafe
/// Unregisters all registered events thus far.
void UnregisterAllEvents();
/// After the first Advance, the slice lengths and the downcount will be reduced whenever an
@@ -77,10 +76,20 @@ public:
/// Scheduling from a callback will not update the downcount until the Advance() completes.
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
/// This is to be called when outside of hle threads, such as the graphics thread, wants to
/// schedule things to be executed on the main thread.
///
/// @note This doesn't change slice_length and thus events scheduled by this might be
/// called with a delay of up to MAX_SLICE_LENGTH
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
u64 userdata = 0);
void UnscheduleEvent(const EventType* event_type, u64 userdata);
void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
/// We only permit one event of each type in the queue at a time.
void RemoveEvent(const EventType* event_type);
void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
void ForceExceptionCheck(s64 cycles);
@@ -111,6 +120,7 @@ private:
/// Clear all pending events. This should ONLY be done on exit.
void ClearPendingEvents();
void MoveEvents();
s64 global_timer = 0;
s64 idled_cycles = 0;
@@ -133,9 +143,14 @@ private:
// remain stable regardless of rehashes/resizing.
std::unordered_map<std::string, EventType> event_types;
EventType* ev_lost = nullptr;
// The queue for storing the events from other threads threadsafe until they will be added
// to the event_queue by the emu thread
Common::MPSCQueue<Event> ts_queue;
std::mutex inner_mutex;
// The queue for unscheduling the events from other threads threadsafe
Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
EventType* ev_lost = nullptr;
};
} // namespace Core::Timing

View File

@@ -13,40 +13,52 @@ namespace Core::Timing {
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE;
s64 msToCycles(std::chrono::milliseconds ms) {
if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
s64 usToCycles(s64 us) {
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
if (us > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (ms.count() / 1000);
return BASE_CLOCK_RATE * (us / 1000000);
}
return (BASE_CLOCK_RATE * ms.count()) / 1000;
return (BASE_CLOCK_RATE * us) / 1000000;
}
s64 usToCycles(std::chrono::microseconds us) {
if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
s64 usToCycles(u64 us) {
if (us / 1000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
if (us > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (us.count() / 1000000);
return BASE_CLOCK_RATE * static_cast<s64>(us / 1000000);
}
return (BASE_CLOCK_RATE * us.count()) / 1000000;
return (BASE_CLOCK_RATE * static_cast<s64>(us)) / 1000000;
}
s64 nsToCycles(std::chrono::nanoseconds ns) {
if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) {
s64 nsToCycles(s64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) {
if (ns > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (ns.count() / 1000000000);
return BASE_CLOCK_RATE * (ns / 1000000000);
}
return (BASE_CLOCK_RATE * ns.count()) / 1000000000;
return (BASE_CLOCK_RATE * ns) / 1000000000;
}
s64 nsToCycles(u64 ns) {
if (ns / 1000000000 > MAX_VALUE_TO_MULTIPLY) {
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
return std::numeric_limits<s64>::max();
}
if (ns > MAX_VALUE_TO_MULTIPLY) {
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
return BASE_CLOCK_RATE * (static_cast<s64>(ns) / 1000000000);
}
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
}
u64 CpuCyclesToClockCycles(u64 ticks) {

View File

@@ -4,7 +4,6 @@
#pragma once
#include <chrono>
#include "common/common_types.h"
namespace Core::Timing {
@@ -14,20 +13,53 @@ namespace Core::Timing {
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
constexpr u64 CNTFREQ = 19200000; // Value from fusee.
s64 msToCycles(std::chrono::milliseconds ms);
s64 usToCycles(std::chrono::microseconds us);
s64 nsToCycles(std::chrono::nanoseconds ns);
inline std::chrono::milliseconds CyclesToMs(s64 cycles) {
return std::chrono::milliseconds(cycles * 1000 / BASE_CLOCK_RATE);
inline s64 msToCycles(int ms) {
// since ms is int there is no way to overflow
return BASE_CLOCK_RATE * static_cast<s64>(ms) / 1000;
}
inline std::chrono::nanoseconds CyclesToNs(s64 cycles) {
return std::chrono::nanoseconds(cycles * 1000000000 / BASE_CLOCK_RATE);
inline s64 msToCycles(float ms) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.001f) * ms);
}
inline std::chrono::microseconds CyclesToUs(s64 cycles) {
return std::chrono::microseconds(cycles * 1000000 / BASE_CLOCK_RATE);
inline s64 msToCycles(double ms) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.001) * ms);
}
inline s64 usToCycles(float us) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.000001f) * us);
}
inline s64 usToCycles(int us) {
return (BASE_CLOCK_RATE * static_cast<s64>(us) / 1000000);
}
s64 usToCycles(s64 us);
s64 usToCycles(u64 us);
inline s64 nsToCycles(float ns) {
return static_cast<s64>(BASE_CLOCK_RATE * (0.000000001f) * ns);
}
inline s64 nsToCycles(int ns) {
return BASE_CLOCK_RATE * static_cast<s64>(ns) / 1000000000;
}
s64 nsToCycles(s64 ns);
s64 nsToCycles(u64 ns);
inline u64 cyclesToNs(s64 cycles) {
return cycles * 1000000000 / BASE_CLOCK_RATE;
}
inline s64 cyclesToUs(s64 cycles) {
return cycles * 1000000 / BASE_CLOCK_RATE;
}
inline u64 cyclesToMs(s64 cycles) {
return cycles * 1000 / BASE_CLOCK_RATE;
}
u64 CpuCyclesToClockCycles(u64 ticks);

View File

@@ -19,19 +19,17 @@ void RunCpuCore(const System& system, Cpu& cpu_state) {
}
} // Anonymous namespace
CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
CpuCoreManager::CpuCoreManager() = default;
CpuCoreManager::~CpuCoreManager() = default;
void CpuCoreManager::Initialize() {
void CpuCoreManager::Initialize(System& system) {
barrier = std::make_unique<CpuBarrier>();
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
for (std::size_t index = 0; index < cores.size(); ++index) {
cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
}
}
void CpuCoreManager::StartThreads() {
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();

View File

@@ -18,7 +18,7 @@ class System;
class CpuCoreManager {
public:
explicit CpuCoreManager(System& system);
CpuCoreManager();
CpuCoreManager(const CpuCoreManager&) = delete;
CpuCoreManager(CpuCoreManager&&) = delete;
@@ -27,8 +27,7 @@ public:
CpuCoreManager& operator=(const CpuCoreManager&) = delete;
CpuCoreManager& operator=(CpuCoreManager&&) = delete;
void Initialize();
void StartThreads();
void Initialize(System& system);
void Shutdown();
Cpu& GetCore(std::size_t index);
@@ -55,8 +54,6 @@ private:
/// Map of guest threads to CPU cores
std::map<std::thread::id, Cpu*> thread_to_cpu;
System& system;
};
} // namespace Core

View File

@@ -22,7 +22,6 @@
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
@@ -572,7 +571,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::HexToString(key));
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
}
@@ -583,7 +582,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::HexToString(rights_id), key);
WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
}
auto category = KeyCategory::Standard;
@@ -795,7 +794,7 @@ void KeyManager::DeriveBase() {
void KeyManager::DeriveETicket(PartitionDataManager& data) {
// ETicket keys
const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
const auto es = Service::FileSystem::GetUnionContents().GetEntry(
0x0100000000000033, FileSys::ContentRecordType::Program);
if (es == nullptr)

View File

@@ -22,10 +22,8 @@
#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;
@@ -47,6 +45,36 @@ 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
@@ -142,6 +170,65 @@ 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{})
@@ -391,22 +478,37 @@ 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);
const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
const FileSys::INI ini{ini_file};
if (ini.GetStatus() != Loader::ResultStatus::Success)
INIHeader ini;
std::memcpy(&ini, c.data(), sizeof(INIHeader));
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
return;
for (const auto& kip : ini.GetKIPs()) {
if (kip.GetStatus() != Loader::ResultStatus::Success)
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'))
return;
if (kip.GetName() != "FS" && kip.GetName() != "spl") {
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;
continue;
}
const auto& text = kip.GetTextSection();
const auto& rodata = kip.GetRODataSection();
const auto& data = kip.GetDataSection();
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});
std::vector<u8> out;
out.reserve(text.size() + rodata.size() + data.size());
@@ -414,9 +516,12 @@ 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());
if (kip.GetName() == "FS")
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
if (name == "FS")
package2_fs[static_cast<size_t>(type)] = std::move(out);
else if (kip.GetName() == "spl")
else if (name == "spl")
package2_spl[static_cast<size_t>(type)] = std::move(out);
}
}

View File

@@ -18,16 +18,11 @@
namespace FileSys {
constexpr std::array partition_names{
"update",
"normal",
"secure",
"logo",
};
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
XCI::XCI(VirtualFile file_)
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
partitions(partition_names.size()) {
partitions(0x4) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorBadXCIHeader;
return;
@@ -48,24 +43,23 @@ XCI::XCI(VirtualFile file_)
for (XCIPartition partition :
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
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));
}
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);
}
secure_partition = std::make_shared<NSP>(
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
ncas = secure_partition->GetNCAsCollapsed();
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
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) {
@@ -153,9 +147,8 @@ 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;
}
@@ -176,22 +169,17 @@ VirtualDir XCI::GetParentDirectory() const {
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
const auto partition_index = static_cast<std::size_t>(part);
const auto& partition = partitions[partition_index];
if (partition == nullptr) {
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorXCIMissingPartition;
}
for (const VirtualFile& file : partition->GetFiles()) {
if (file->GetExtension() != "nca") {
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
continue;
}
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
if (nca->IsUpdate()) {
// TODO(DarkLordZach): Add proper Rev1+ Support
if (nca->IsUpdate())
continue;
}
if (nca->GetType() == NCAContentType::Program) {
program_nca_status = nca->GetStatus();
}
@@ -200,7 +188,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[partition_index], nca->GetName(), error_id,
partition_names[static_cast<std::size_t>(part)], 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_TRACE(Crypto, "called with mode=NONE");
LOG_DEBUG(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_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
std::optional<Core::Crypto::Key128> key = {};
if (has_rights_id) {

View File

@@ -67,7 +67,7 @@ std::string NACP::GetDeveloperName(Language language) const {
}
u64 NACP::GetTitleId() const {
return raw.save_data_owner_id;
return raw.title_id;
}
u64 NACP::GetDLCBaseTitleId() const {
@@ -80,19 +80,11 @@ std::string NACP::GetVersionString() const {
}
u64 NACP::GetDefaultNormalSaveSize() const {
return raw.user_account_save_data_size;
return raw.normal_save_data_size;
}
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;
return raw.journal_sava_data_size;
}
std::vector<u8> NACP::GetRawBytes() const {

View File

@@ -30,8 +30,7 @@ struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
std::array<u8, 0x25> isbn;
u8 startup_user_account;
u8 user_account_switch_lock;
u8 addon_content_registration_type;
INSERT_PADDING_BYTES(2);
u32_le application_attribute;
u32_le supported_languages;
u32_le parental_control;
@@ -39,35 +38,23 @@ struct RawNACP {
u8 video_capture_mode;
bool data_loss_confirmation;
INSERT_PADDING_BYTES(1);
u64_le presence_group_id;
u64_le title_id;
std::array<u8, 0x20> rating_age;
std::array<char, 0x10> version_string;
u64_le dlc_base_title_id;
u64_le save_data_owner_id;
u64_le user_account_save_data_size;
u64_le user_account_save_data_journal_size;
u64_le device_save_data_size;
u64_le device_save_data_journal_size;
u64_le bcat_delivery_cache_storage_size;
char application_error_code_category[8];
u64_le title_id_2;
u64_le normal_save_data_size;
u64_le journal_sava_data_size;
INSERT_PADDING_BYTES(0x18);
u64_le product_code;
std::array<u64_le, 0x8> local_communication;
u8 logo_type;
u8 logo_handling;
bool runtime_add_on_content_install;
INSERT_PADDING_BYTES(5);
u64_le seed_for_pseudo_device_id;
std::array<u8, 0x41> bcat_passphrase;
INSERT_PADDING_BYTES(7);
u64_le user_account_save_data_max_size;
u64_le user_account_save_data_max_journal_size;
u64_le device_save_data_max_size;
u64_le device_save_data_max_journal_size;
u64_le temporary_storage_size;
u64_le cache_storage_size;
u64_le cache_storage_journal_size;
u64_le cache_storage_data_and_journal_max_size;
u64_le cache_storage_max_index;
INSERT_PADDING_BYTES(0xE70);
u64_le title_id_update;
std::array<u8, 0x40> bcat_passphrase;
INSERT_PADDING_BYTES(0xEC0);
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
@@ -110,9 +97,7 @@ public:
std::string GetVersionString() const;
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
u32 GetSupportedLanguages() const;
std::vector<u8> GetRawBytes() const;
bool GetUserAccountSwitchLock() const;
private:
RawNACP raw{};

View File

@@ -23,7 +23,6 @@
*/
#include <cstring>
#include <string_view>
#include "common/alignment.h"
#include "common/assert.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
@@ -98,8 +97,7 @@ struct RomFSBuildFileContext {
VirtualFile source;
};
static u32 romfs_calc_path_hash(u32 parent, std::string_view path, u32 start,
std::size_t path_len) {
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
u32 hash = parent ^ 123456789;
for (u32 i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);

View File

@@ -287,6 +287,7 @@ 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);
}
@@ -294,7 +295,7 @@ void IPSwitchCompiler::Parse() {
LOG_INFO(Loader,
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
"with byte string '{}'",
patch_text->GetName(), offset, Common::HexToString(replace));
patch_text->GetName(), offset, Common::HexVectorToString(replace));
}
patch.records.insert_or_assign(offset, std::move(replace));

View File

@@ -1,228 +0,0 @@
// 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

@@ -1,99 +0,0 @@
// 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

@@ -10,6 +10,14 @@
namespace FileSys {
bool operator>=(TitleType lhs, TitleType rhs) {
return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
}
bool operator<=(TitleType lhs, TitleType rhs) {
return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
}
CNMT::CNMT(VirtualFile file) {
if (file->ReadObject(&header) != sizeof(CNMTHeader))
return;

View File

@@ -4,7 +4,6 @@
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "common/common_funcs.h"
@@ -30,14 +29,17 @@ enum class TitleType : u8 {
DeltaTitle = 0x83,
};
bool operator>=(TitleType lhs, TitleType rhs);
bool operator<=(TitleType lhs, TitleType rhs);
enum class ContentRecordType : u8 {
Meta = 0,
Program = 1,
Data = 2,
Control = 3,
HtmlDocument = 4,
LegalInformation = 5,
DeltaFragment = 6,
Manual = 4,
Legal = 5,
Patch = 6,
};
struct ContentRecord {
@@ -70,15 +72,11 @@ struct CNMTHeader {
u64_le title_id;
u32_le title_version;
TitleType type;
u8 reserved;
INSERT_PADDING_BYTES(1);
u16_le table_offset;
u16_le number_content_entries;
u16_le number_meta_entries;
u8 attributes;
std::array<u8, 2> reserved2;
u8 is_committed;
u32_le required_download_system_version;
std::array<u8, 4> reserved3;
INSERT_PADDING_BYTES(12);
};
static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");

View File

@@ -10,7 +10,6 @@
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/ips_layer.h"
@@ -70,7 +69,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
}
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto installed = Service::FileSystem::GetUnionContents();
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
@@ -142,7 +141,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
if (!compiler.IsValid())
continue;
auto this_build_id = Common::HexToString(compiler.GetBuildID());
auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
this_build_id =
this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
@@ -156,7 +155,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
return out;
}
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::string& name) const {
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
if (nso.size() < sizeof(Loader::NSOHeader)) {
return nso;
}
@@ -168,23 +167,22 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
return nso;
}
const auto build_id_raw = Common::HexToString(header.build_id);
const auto build_id_raw = Common::HexArrayToString(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) {
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
title_id);
LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
file->Resize(nso.size());
file->WriteBytes(nso);
}
}
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
@@ -219,7 +217,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::HexToString(build_id_);
const auto build_id_raw = Common::HexArrayToString(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 +233,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::HexToString(build_id_, upper);
const auto build_id_raw = Common::HexArrayToString(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));
@@ -347,7 +345,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
if (romfs == nullptr)
return romfs;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto installed = Service::FileSystem::GetUnionContents();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
@@ -394,7 +392,7 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
std::map<std::string, std::string, std::less<>> out;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto installed = Service::FileSystem::GetUnionContents();
const auto& disabled = Settings::values.disabled_addons[title_id];
// Game Updates
@@ -468,10 +466,10 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
// DLC
const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<ContentProviderEntry> dlc_match;
std::vector<RegisteredCacheEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
[this, &installed](const ContentProviderEntry& entry) {
[this, &installed](const RegisteredCacheEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
});
@@ -493,18 +491,8 @@ 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();
const auto installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)

View File

@@ -44,7 +44,7 @@ public:
// Currently tracked NSO patches:
// - IPS
// - IPSwitch
std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const;
std::vector<u8> PatchNSO(const std::vector<u8>& nso) const;
// Checks to see if PatchNSO() will have any effect given the NSO's build ID.
// Used to prevent expensive copies in NSO loader.
@@ -66,13 +66,8 @@ public:
std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
VirtualFile update_raw = nullptr) const;
// 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.
// 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

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cstddef>
#include <cstring>
#include <vector>
#include "common/logging/log.h"
@@ -16,30 +17,28 @@ ProgramMetadata::ProgramMetadata() = default;
ProgramMetadata::~ProgramMetadata() = default;
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
const std::size_t total_size = file->GetSize();
if (total_size < sizeof(Header)) {
std::size_t total_size = static_cast<std::size_t>(file->GetSize());
if (total_size < sizeof(Header))
return Loader::ResultStatus::ErrorBadNPDMHeader;
}
if (sizeof(Header) != file->ReadObject(&npdm_header)) {
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
if (sizeof(Header) != npdm_header_data.size())
return Loader::ResultStatus::ErrorBadNPDMHeader;
}
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) {
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
if (sizeof(AcidHeader) != acid_header_data.size())
return Loader::ResultStatus::ErrorBadACIDHeader;
}
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) {
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
return Loader::ResultStatus::ErrorBadACIHeader;
}
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) {
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
return Loader::ResultStatus::ErrorBadFileAccessControl;
}
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) {
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
return Loader::ResultStatus::ErrorBadFileAccessHeader;
}
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
const u64 read_size = aci_header.kac_size;
@@ -51,21 +50,6 @@ 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,11 +46,6 @@ 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;
@@ -63,6 +58,7 @@ public:
void Print() const;
private:
// TODO(DarkLordZach): BitField is not trivially copyable.
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
@@ -89,6 +85,7 @@ private:
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
// TODO(DarkLordZach): BitField is not trivially copyable.
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;

View File

@@ -23,19 +23,19 @@ namespace FileSys {
// The size of blocks to use when vfs raw copying into nand.
constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
std::string ContentProviderEntry::DebugInfo() const {
std::string RegisteredCacheEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
}
bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
}
bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return !operator==(lhs, rhs);
}
@@ -53,14 +53,13 @@ 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::HexToString(nca_id, second_hex_upper));
}
if (!within_two_digit)
return fmt::format("/{}.nca", Common::HexArrayToString(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::HexToString(nca_id, second_hex_upper));
Common::HexArrayToString(nca_id, second_hex_upper));
}
static std::string GetCNMTName(TitleType type, u64 title_id) {
@@ -85,7 +84,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
}
ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
switch (type) {
case NCAContentType::Program:
// TODO(DarkLordZach): Differentiate between Program and Patch
@@ -99,34 +98,12 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
return ContentRecordType::Data;
case NCAContentType::Manual:
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
return ContentRecordType::HtmlDocument;
return ContentRecordType::Manual;
default:
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
}
}
ContentProvider::~ContentProvider() = default;
bool ContentProvider::HasEntry(ContentProviderEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
VirtualFile ContentProvider::GetEntryUnparsed(ContentProviderEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
VirtualFile ContentProvider::GetEntryRaw(ContentProviderEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::unique_ptr<NCA> ContentProvider::GetEntry(ContentProviderEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
@@ -184,8 +161,8 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
return file;
}
static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
ContentRecordType type) {
static std::optional<NcaID> CheckMapForContentRecord(
const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
if (map.find(title_id) == map.end())
return {};
@@ -291,7 +268,7 @@ void RegisteredCache::Refresh() {
AccumulateYuzuMeta();
}
RegisteredCache::RegisteredCache(VirtualDir dir_, ContentProviderParsingFunction parsing_function)
RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function)
: dir(std::move(dir_)), parser(std::move(parsing_function)) {
Refresh();
}
@@ -302,11 +279,19 @@ bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type) != nullptr;
}
bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry) != nullptr;
}
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
return id ? GetFileAtID(*id) : nullptr;
}
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
if (meta_iter != meta.end())
@@ -324,6 +309,10 @@ VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) c
return id ? parser(GetFileAtID(*id), *id) : nullptr;
}
VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
@@ -331,6 +320,10 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
return std::make_unique<NCA>(raw, nullptr, 0, keys);
}
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
template <typename T>
void RegisteredCache::IterateAllMetadata(
std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc,
@@ -355,14 +348,25 @@ void RegisteredCache::IterateAllMetadata(
}
}
std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<ContentProviderEntry> out;
IterateAllMetadata<ContentProviderEntry>(
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return ContentProviderEntry{c.GetTitleID(), r.type};
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[](const CNMT& c, const ContentRecord& r) { return true; });
return out;
}
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type && *title_type != c.GetType())
@@ -377,11 +381,10 @@ std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
}
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false)));
if (file == nullptr) {
const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
if (file == nullptr)
return nullptr;
}
return std::make_shared<NCA>(std::move(file));
return std::make_shared<NCA>(file);
}
InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
@@ -397,8 +400,8 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
});
if (meta_iter == ncas.end()) {
LOG_ERROR(Loader, "The file you are attempting to install does not have a metadata NCA and "
"is therefore malformed. Check your encryption keys.");
LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
"is therefore malformed. Double check your encryption keys.");
return InstallResult::ErrorMetaFailed;
}
@@ -415,9 +418,6 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
for (const auto& record : cnmt.GetContentRecords()) {
// Ignore DeltaFragments, they are not useful to us
if (record.type == ContentRecordType::DeltaFragment)
continue;
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
@@ -521,56 +521,37 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
}) != yuzu_meta.end();
}
ContentProviderUnion::~ContentProviderUnion() = default;
RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
: caches(std::move(caches)) {}
void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) {
providers[slot] = provider;
void RegisteredCacheUnion::Refresh() {
for (const auto& c : caches)
c->Refresh();
}
void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) {
providers[slot] = nullptr;
bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const {
return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) {
return cache->HasEntry(title_id, type);
});
}
void ContentProviderUnion::Refresh() {
for (auto& provider : providers) {
if (provider.second == nullptr)
continue;
provider.second->Refresh();
}
bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
if (provider.second->HasEntry(title_id, type))
return true;
}
return false;
}
std::optional<u32> ContentProviderUnion::GetEntryVersion(u64 title_id) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
const auto res = provider.second->GetEntryVersion(title_id);
if (res != std::nullopt)
std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
for (const auto& c : caches) {
const auto res = c->GetEntryVersion(title_id);
if (res)
return res;
}
return std::nullopt;
return {};
}
VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
const auto res = provider.second->GetEntryUnparsed(title_id, type);
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryUnparsed(title_id, type);
if (res != nullptr)
return res;
}
@@ -578,12 +559,13 @@ VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordTy
return nullptr;
}
VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
const auto res = provider.second->GetEntryRaw(title_id, type);
VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryRaw(title_id, type);
if (res != nullptr)
return res;
}
@@ -591,30 +573,30 @@ VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType ty
return nullptr;
}
std::unique_ptr<NCA> ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
auto res = provider.second->GetEntry(title_id, type);
if (res != nullptr)
return res;
}
return nullptr;
VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<ContentProviderEntry> out;
std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_unique<NCA>(raw);
}
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
std::copy(vec.begin(), vec.end(), std::back_inserter(out));
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[](const CNMT& c, const ContentRecord& r) { return true; });
}
std::sort(out.begin(), out.end());
@@ -622,101 +604,25 @@ std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
return out;
}
std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>>
ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnionSlot> origin,
std::optional<TitleType> title_type,
std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> out;
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
if (origin.has_value() && *origin != provider.first)
continue;
const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
std::transform(vec.begin(), vec.end(), std::back_inserter(out),
[&provider](const ContentProviderEntry& entry) {
return std::make_pair(provider.first, entry);
});
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
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,
u64 title_id, VirtualFile file) {
entries.insert_or_assign({title_type, content_type, title_id}, file);
}
void ManualContentProvider::ClearAllEntries() {
entries.clear();
}
void ManualContentProvider::Refresh() {}
bool ManualContentProvider::HasEntry(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type) != nullptr;
}
std::optional<u32> ManualContentProvider::GetEntryVersion(u64 title_id) const {
return std::nullopt;
}
VirtualFile ManualContentProvider::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type);
}
VirtualFile ManualContentProvider::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto iter =
std::find_if(entries.begin(), entries.end(), [title_id, type](const auto& entry) {
const auto [title_type, content_type, e_title_id] = entry.first;
return content_type == type && e_title_id == title_id;
});
if (iter == entries.end())
return nullptr;
return iter->second;
}
std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecordType type) const {
const auto res = GetEntryRaw(title_id, type);
if (res == nullptr)
return nullptr;
return std::make_unique<NCA>(res, nullptr, 0, keys);
}
std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<ContentProviderEntry> out;
for (const auto& entry : entries) {
const auto [e_title_type, e_content_type, e_title_id] = entry.first;
if ((title_type == std::nullopt || e_title_type == *title_type) &&
(record_type == std::nullopt || e_content_type == *record_type) &&
(title_id == std::nullopt || e_title_id == *title_id)) {
out.emplace_back(ContentProviderEntry{e_title_id, e_content_type});
}
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type && *title_type != c.GetType())
return false;
if (record_type && *record_type != r.type)
return false;
if (title_id && *title_id != c.GetTitleID())
return false;
return true;
});
}
std::sort(out.begin(), out.end());

View File

@@ -21,13 +21,12 @@ class NSP;
class XCI;
enum class ContentRecordType : u8;
enum class NCAContentType : u8;
enum class TitleType : u8;
struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
enum class InstallResult {
@@ -37,7 +36,7 @@ enum class InstallResult {
ErrorMetaFailed,
};
struct ContentProviderEntry {
struct RegisteredCacheEntry {
u64 title_id;
ContentRecordType type;
@@ -48,46 +47,12 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
return base_title_id | 0x800;
}
ContentRecordType GetCRTypeFromNCAType(NCAContentType type);
// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
// std unique requires operator== to identify duplicates.
bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
class ContentProvider {
public:
virtual ~ContentProvider();
virtual void Refresh() = 0;
virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
virtual bool HasEntry(ContentProviderEntry entry) const;
virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
virtual std::vector<ContentProviderEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
virtual std::vector<ContentProviderEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const = 0;
protected:
// A single instance of KeyManager to be used by GetEntry()
Core::Crypto::KeyManager keys;
};
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
/*
* A class that catalogues NCAs in the registered directory structure.
@@ -102,32 +67,39 @@ protected:
* (This impl also supports substituting the nca dir for an nca file, as that's more convenient
* when 4GB splitting can be ignored.)
*/
class RegisteredCache : public ContentProvider {
class RegisteredCache {
friend class RegisteredCacheUnion;
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
// parsing function.
explicit RegisteredCache(VirtualDir dir,
ContentProviderParsingFunction parsing_function =
RegisteredCacheParsingFunction parsing_function =
[](const VirtualFile& file, const NcaID& id) { return file; });
~RegisteredCache() override;
~RegisteredCache();
void Refresh() override;
void Refresh();
bool HasEntry(u64 title_id, ContentRecordType type) const override;
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
std::optional<u32> GetEntryVersion(u64 title_id) const override;
std::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
std::vector<ContentProviderEntry> ListEntriesFilter(
std::vector<RegisteredCacheEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const override;
std::optional<u64> title_id = {}) const;
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
@@ -159,73 +131,46 @@ private:
bool RawInstallYuzuMeta(const CNMT& cnmt);
VirtualDir dir;
ContentProviderParsingFunction parser;
RegisteredCacheParsingFunction parser;
Core::Crypto::KeyManager keys;
// maps tid -> NcaID of meta
std::map<u64, NcaID> meta_id;
boost::container::flat_map<u64, NcaID> meta_id;
// maps tid -> meta
std::map<u64, CNMT> meta;
boost::container::flat_map<u64, CNMT> meta;
// maps tid -> meta for CNMT in yuzu_meta
std::map<u64, CNMT> yuzu_meta;
boost::container::flat_map<u64, CNMT> yuzu_meta;
};
enum class ContentProviderUnionSlot {
SysNAND, ///< System NAND
UserNAND, ///< User NAND
SDMC, ///< SD Card
FrontendManual, ///< Frontend-defined game list or similar
};
// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
class ContentProviderUnion : public ContentProvider {
// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
class RegisteredCacheUnion {
public:
~ContentProviderUnion() override;
explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider);
void ClearSlot(ContentProviderUnionSlot slot);
void Refresh();
void Refresh() override;
bool HasEntry(u64 title_id, ContentRecordType type) const override;
std::optional<u32> GetEntryVersion(u64 title_id) const override;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
std::vector<ContentProviderEntry> ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const override;
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin(
std::optional<ContentProviderUnionSlot> origin = {},
std::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
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;
};
class ManualContentProvider : public ContentProvider {
public:
~ManualContentProvider() override;
void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id,
VirtualFile file);
void ClearAllEntries();
void Refresh() override;
bool HasEntry(u64 title_id, ContentRecordType type) const override;
std::optional<u32> GetEntryVersion(u64 title_id) const override;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
std::vector<ContentProviderEntry> ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const override;
private:
std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries;
std::vector<RegisteredCache*> caches;
};
} // namespace FileSys

View File

@@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
switch (storage) {
case StorageId::None:
res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type);
break;
case StorageId::NandSystem:
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);

View File

@@ -16,10 +16,8 @@ namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
"rank={}, index={}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
static_cast<u8>(rank), index);
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
@@ -30,7 +28,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,

View File

@@ -32,19 +32,12 @@ enum class SaveDataType : u8 {
CacheStorage = 5,
};
enum class SaveDataRank : u8 {
Primary,
Secondary,
};
struct SaveDataDescriptor {
u64_le title_id;
u128 user_id;
u64_le save_id;
SaveDataType type;
SaveDataRank rank;
u16_le index;
INSERT_PADDING_BYTES(4);
INSERT_PADDING_BYTES(7);
u64_le zero_1;
u64_le zero_2;
u64_le zero_3;
@@ -64,7 +57,7 @@ public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;

View File

@@ -143,12 +143,11 @@ std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
return out;
}
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>>
NSP::GetNCAs() const {
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
return ncas;
}
std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const {
std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
@@ -156,14 +155,14 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
if (title_id_iter == ncas.end())
return nullptr;
const auto type_iter = title_id_iter->second.find({title_type, type});
const auto type_iter = title_id_iter->second.find(type);
if (type_iter == title_id_iter->second.end())
return nullptr;
return type_iter->second;
}
VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
const auto nca = GetNCA(title_id, type);
@@ -235,37 +234,31 @@ 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;
ncas_title[ContentRecordType::Meta] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexToString(rec.nca_id, false);
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
if (rec.type != ContentRecordType::DeltaFragment) {
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
"be found in PFS. NSP appears to be corrupted.",
id_string);
}
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
"be found in PFS. NSP appears to be corrupted.",
id_string);
continue;
}
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys);
if (next_nca->GetType() == NCAContentType::Program) {
auto next_nca = std::make_shared<NCA>(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)) {
ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca);
ncas_title[rec.type] = std::move(next_nca);
}
}

View File

@@ -42,12 +42,9 @@ public:
// Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> GetNCAs()
const;
std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type,
TitleType title_type = TitleType::Application) const;
VirtualFile GetNCAFile(u64 title_id, ContentRecordType type,
TitleType title_type = TitleType::Application) const;
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
std::vector<Core::Crypto::Key128> GetTitlekey() const;
std::vector<VirtualFile> GetFiles() const override;
@@ -70,7 +67,7 @@ private:
std::shared_ptr<PartitionFilesystem> pfs;
// Map title id -> {map type -> NCA}
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
Core::Crypto::KeyManager keys;

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::HexToString(nca_id, false)));
Common::HexArrayToString(nca_id, false)));
}
NAX::~NAX() = default;

View File

@@ -1,34 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/applets/error.h"
namespace Core::Frontend {
ErrorApplet::~ErrorApplet() = default;
void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
}
void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
std::function<void()> finished) const {
LOG_CRITICAL(
Service_Fatal,
"Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count());
}
void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
std::string detail_text,
std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal,
"Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text);
LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text);
}
} // namespace Core::Frontend

View File

@@ -1,37 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <chrono>
#include <functional>
#include "core/hle/result.h"
namespace Core::Frontend {
class ErrorApplet {
public:
virtual ~ErrorApplet();
virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
std::function<void()> finished) const = 0;
virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
std::string fullscreen_text,
std::function<void()> finished) const = 0;
};
class DefaultErrorApplet final : public ErrorApplet {
public:
void ShowError(ResultCode error, std::function<void()> finished) const override;
void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
std::function<void()> finished) const override;
void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
std::function<void()> finished) const override;
};
} // namespace Core::Frontend

View File

@@ -1,124 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/frontend/applets/general_frontend.h"
namespace Core::Frontend {
ParentalControlsApplet::~ParentalControlsApplet() = default;
DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default;
void DefaultParentalControlsApplet::VerifyPIN(std::function<void(bool)> finished,
bool suspend_future_verification_temporarily) {
LOG_INFO(Service_AM,
"Application requested frontend to verify PIN (normal), "
"suspend_future_verification_temporarily={}, verifying as correct.",
suspend_future_verification_temporarily);
finished(true);
}
void DefaultParentalControlsApplet::VerifyPINForSettings(std::function<void(bool)> finished) {
LOG_INFO(Service_AM,
"Application requested frontend to verify PIN (settings), verifying as correct.");
finished(true);
}
void DefaultParentalControlsApplet::RegisterPIN(std::function<void()> finished) {
LOG_INFO(Service_AM, "Application requested frontend to register new PIN");
finished();
}
void DefaultParentalControlsApplet::ChangePIN(std::function<void()> finished) {
LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value");
finished();
}
PhotoViewerApplet::~PhotoViewerApplet() = default;
DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default;
void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
std::function<void()> finished) const {
LOG_INFO(Service_AM,
"Application requested frontend to display stored photos for title_id={:016X}",
title_id);
finished();
}
void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const {
LOG_INFO(Service_AM, "Application requested frontend to display all stored photos.");
finished();
}
ECommerceApplet::~ECommerceApplet() = default;
DefaultECommerceApplet::~DefaultECommerceApplet() = default;
void DefaultECommerceApplet::ShowApplicationInformation(
std::function<void()> finished, u64 title_id, std::optional<u128> user_id,
std::optional<bool> full_display, std::optional<std::string> extra_parameter) {
const auto value = user_id.value_or(u128{});
LOG_INFO(Service_AM,
"Application requested frontend show application information for EShop, "
"title_id={:016X}, user_id={:016X}{:016X}, full_display={}, extra_parameter={}",
title_id, value[1], value[0],
full_display.has_value() ? fmt::format("{}", *full_display) : "null",
extra_parameter.value_or("null"));
finished();
}
void DefaultECommerceApplet::ShowAddOnContentList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id,
std::optional<bool> full_display) {
const auto value = user_id.value_or(u128{});
LOG_INFO(Service_AM,
"Application requested frontend show add on content list for EShop, "
"title_id={:016X}, user_id={:016X}{:016X}, full_display={}",
title_id, value[1], value[0],
full_display.has_value() ? fmt::format("{}", *full_display) : "null");
finished();
}
void DefaultECommerceApplet::ShowSubscriptionList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id) {
const auto value = user_id.value_or(u128{});
LOG_INFO(Service_AM,
"Application requested frontend show subscription list for EShop, title_id={:016X}, "
"user_id={:016X}{:016X}",
title_id, value[1], value[0]);
finished();
}
void DefaultECommerceApplet::ShowConsumableItemList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id) {
const auto value = user_id.value_or(u128{});
LOG_INFO(
Service_AM,
"Application requested frontend show consumable item list for EShop, title_id={:016X}, "
"user_id={:016X}{:016X}",
title_id, value[1], value[0]);
finished();
}
void DefaultECommerceApplet::ShowShopHome(std::function<void()> finished, u128 user_id,
bool full_display) {
LOG_INFO(Service_AM,
"Application requested frontend show home menu for EShop, user_id={:016X}{:016X}, "
"full_display={}",
user_id[1], user_id[0], full_display);
finished();
}
void DefaultECommerceApplet::ShowSettings(std::function<void()> finished, u128 user_id,
bool full_display) {
LOG_INFO(Service_AM,
"Application requested frontend show settings menu for EShop, user_id={:016X}{:016X}, "
"full_display={}",
user_id[1], user_id[0], full_display);
finished();
}
} // namespace Core::Frontend

View File

@@ -1,112 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <optional>
#include "common/common_types.h"
namespace Core::Frontend {
class ParentalControlsApplet {
public:
virtual ~ParentalControlsApplet();
// Prompts the user to enter a PIN and calls the callback with whether or not it matches the
// correct PIN. If the bool is passed, and the PIN was recently entered correctly, the frontend
// should not prompt and simply return true.
virtual void VerifyPIN(std::function<void(bool)> finished,
bool suspend_future_verification_temporarily) = 0;
// Prompts the user to enter a PIN and calls the callback for correctness. Frontends can
// optionally alert the user that this is to change parental controls settings.
virtual void VerifyPINForSettings(std::function<void(bool)> finished) = 0;
// Prompts the user to create a new PIN for pctl and stores it with the service.
virtual void RegisterPIN(std::function<void()> finished) = 0;
// Prompts the user to verify the current PIN and then store a new one into pctl.
virtual void ChangePIN(std::function<void()> finished) = 0;
};
class DefaultParentalControlsApplet final : public ParentalControlsApplet {
public:
~DefaultParentalControlsApplet() override;
void VerifyPIN(std::function<void(bool)> finished,
bool suspend_future_verification_temporarily) override;
void VerifyPINForSettings(std::function<void(bool)> finished) override;
void RegisterPIN(std::function<void()> finished) override;
void ChangePIN(std::function<void()> finished) override;
};
class PhotoViewerApplet {
public:
virtual ~PhotoViewerApplet();
virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0;
virtual void ShowAllPhotos(std::function<void()> finished) const = 0;
};
class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
public:
~DefaultPhotoViewerApplet() override;
void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
void ShowAllPhotos(std::function<void()> finished) const override;
};
class ECommerceApplet {
public:
virtual ~ECommerceApplet();
// Shows a page with application icons, description, name, and price.
virtual void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id = {},
std::optional<bool> full_display = {},
std::optional<std::string> extra_parameter = {}) = 0;
// Shows a page with all of the add on content available for a game, with name, description, and
// price.
virtual void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id = {},
std::optional<bool> full_display = {}) = 0;
// Shows a page with all of the subscriptions (recurring payments) for a game, with name,
// description, price, and renewal period.
virtual void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id = {}) = 0;
// Shows a page with a list of any additional game related purchasable items (DLC,
// subscriptions, etc) for a particular game, with name, description, type, and price.
virtual void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id = {}) = 0;
// Shows the home page of the shop.
virtual void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) = 0;
// Shows the user settings page of the shop.
virtual void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) = 0;
};
class DefaultECommerceApplet : public ECommerceApplet {
public:
~DefaultECommerceApplet() override;
void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id, std::optional<bool> full_display,
std::optional<std::string> extra_parameter) override;
void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id,
std::optional<bool> full_display) override;
void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id) override;
void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
std::optional<u128> user_id) override;
void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) override;
void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) override;
};
} // namespace Core::Frontend

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
namespace Core::Frontend {
@@ -11,9 +10,9 @@ namespace Core::Frontend {
ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Common::UUID>)> callback) const {
std::function<void(std::optional<Service::Account::UUID>)> callback) const {
Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user).value_or(Common::UUID{}));
callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
}

View File

@@ -6,7 +6,7 @@
#include <functional>
#include <optional>
#include "common/uuid.h"
#include "core/hle/service/acc/profile_manager.h"
namespace Core::Frontend {
@@ -14,12 +14,14 @@ class ProfileSelectApplet {
public:
virtual ~ProfileSelectApplet();
virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0;
virtual void SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0;
};
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
public:
void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override;
void SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
};
} // namespace Core::Frontend

View File

@@ -11,9 +11,9 @@ WebBrowserApplet::~WebBrowserApplet() = default;
DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
void DefaultWebBrowserApplet::OpenPageLocal(std::string_view filename,
std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) {
void DefaultWebBrowserApplet::OpenPage(std::string_view filename,
std::function<void()> unpack_romfs_callback,
std::function<void()> finished_callback) {
LOG_INFO(Service_AM,
"(STUBBED) called - No suitable web browser implementation found to open website page "
"at '{}'!",

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