Compare commits

..

1 Commits

Author SHA1 Message Date
Lioncash
9cd9af61cf core/file_sys: Replace includes with forward declarations where applicable
Avoids proliferating includes through other headers, lessening the total
amount of files that need to be rebuilt if those headers are changed.
This also resolves a few indirect inclusions.
2018-08-16 04:19:32 -04:00
470 changed files with 4572 additions and 124065 deletions

3
.gitmodules vendored
View File

@@ -31,6 +31,3 @@
[submodule "opus"]
path = externals/opus
url = https://github.com/ogniK5377/opus.git
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git

View File

@@ -20,7 +20,6 @@ matrix:
install: "./.travis/linux/deps.sh"
script: "./.travis/linux/build.sh"
after_success: "./.travis/linux/upload.sh"
cache: ccache
- os: osx
env: NAME="macos build"
sudo: false
@@ -28,7 +27,6 @@ matrix:
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"
cache: ccache
deploy:
provider: releases
@@ -44,3 +42,7 @@ notifications:
webhooks:
urls:
- https://api.yuzu-emu.org/code/travis/notify
cache:
directories:
- $HOME/.ccache

View File

@@ -1,12 +0,0 @@
# List of environment variables to be shared with Docker containers
CI
TRAVIS
CONTINUOUS_INTEGRATION
TRAVIS_BRANCH
TRAVIS_BUILD_ID
TRAVIS_BUILD_NUMBER
TRAVIS_COMMIT
TRAVIS_JOB_ID
TRAVIS_JOB_NUMBER
TRAVIS_REPO_SLUG
TRAVIS_TAG

View File

@@ -1,4 +1,3 @@
#!/bin/bash -ex
mkdir -p "$HOME/.ccache"
docker run --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
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -5,8 +5,14 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
export PATH=/usr/lib/ccache:$PATH
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
mkdir build && cd build
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
ccache --show-stats > ccache_before
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release

View File

@@ -5,11 +5,14 @@ set -o pipefail
export MACOSX_DEPLOYMENT_TARGET=10.12
export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
export PATH=/usr/local/opt/ccache/libexec:$PATH
ccache --show-stats > ccache_before
cmake --version
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
make -j4
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release

View File

@@ -41,19 +41,6 @@ function(check_submodules_present)
endfunction()
check_submodules_present()
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY)
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif()
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif()
# Detect current compilation architecture and create standard definitions
# =======================================================================
@@ -431,9 +418,6 @@ enable_testing()
add_subdirectory(externals)
add_subdirectory(src)
# Set yuzu project as default StartUp Project in Visual Studio
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
# Installation instructions
# =========================

View File

@@ -41,9 +41,9 @@ before_build:
- ps: |
if ($env:BUILD_TYPE -eq 'msvc') {
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
} else {
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
}
- cd ..
@@ -162,6 +162,10 @@ artifacts:
- path: $(BUILD_ZIP)
name: build
type: zip
- path: $(BUILD_SYMBOLS)
name: debugsymbols
- path: $(BUILD_UPDATE)
name: update
deploy:
provider: GitHub

View File

@@ -1,5 +0,0 @@
<RCC>
<qresource prefix="compatibility_list">
<file>compatibility_list.json</file>
</qresource>
</RCC>

View File

@@ -43,16 +43,10 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
add_library(microprofile INTERFACE)
target_include_directories(microprofile INTERFACE ./microprofile)
# Open Source Archives
add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
# Unicorn
add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# SoundTouch
add_subdirectory(soundtouch)
# Xbyak
if (ARCHITECTURE_x86_64)
# Defined before "dynarmic" above

2
externals/fmt vendored

View File

@@ -1,16 +0,0 @@
add_library(open_source_archives
src/FontChineseSimplified.cpp
src/FontChineseTraditional.cpp
src/FontExtendedChineseSimplified.cpp
src/FontKorean.cpp
src/FontNintendoExtended.cpp
src/FontStandard.cpp
include/FontChineseSimplified.h
include/FontChineseTraditional.h
include/FontExtendedChineseSimplified.h
include/FontKorean.h
include/FontNintendoExtended.h
include/FontStandard.h
)
target_include_directories(open_source_archives PUBLIC include)

View File

@@ -1,4 +0,0 @@
These files were generated by https://github.com/FearlessTobi/yuzu_system_archives at git commit 0a24b0c9f38d71fb2c4bba5645a39029e539a5ec. To generate the files use the run.sh inside that repository.
The follwing system archives are currently included:
- JPN/EUR/USA System Font

View File

@@ -1,6 +0,0 @@
#pragma once
#include <array>
extern const std::array<unsigned char, 217276> FontChineseSimplified;

View File

@@ -1,6 +0,0 @@
#pragma once
#include <array>
extern const std::array<unsigned char, 222236> FontChineseTraditional;

View File

@@ -1,6 +0,0 @@
#pragma once
#include <array>
extern const std::array<unsigned char, 293516> FontExtendedChineseSimplified;

View File

@@ -1,6 +0,0 @@
#pragma once
#include <array>
extern const std::array<unsigned char, 217276> FontKorean;

View File

@@ -1,6 +0,0 @@
#pragma once
#include <array>
extern const std::array<unsigned char, 172064> FontNintendoExtended;

View File

@@ -1,6 +0,0 @@
#pragma once
#include <array>
extern const std::array<unsigned char, 217276> FontStandard;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Submodule externals/soundtouch deleted from 060181eaf2

View File

@@ -17,8 +17,6 @@ add_library(audio_core STATIC
sink_stream.h
stream.cpp
stream.h
time_stretch.cpp
time_stretch.h
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
)
@@ -26,7 +24,6 @@ add_library(audio_core STATIC
create_target_directory_groups(audio_core)
target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb)

View File

@@ -35,18 +35,18 @@ Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2)
: a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {}
void Filter::Process(std::vector<s16>& signal) {
const std::size_t num_frames = signal.size() / 2;
for (std::size_t i = 0; i < num_frames; i++) {
const size_t num_frames = signal.size() / 2;
for (size_t i = 0; i < num_frames; i++) {
std::rotate(in.begin(), in.end() - 1, in.end());
std::rotate(out.begin(), out.end() - 1, out.end());
for (std::size_t ch = 0; ch < channel_count; ch++) {
for (size_t ch = 0; ch < channel_count; ch++) {
in[0][ch] = signal[i * channel_count + ch];
out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] -
a2 * out[2][ch];
signal[i * 2 + ch] = static_cast<s16>(std::clamp(out[0][ch], -32768.0, 32767.0));
signal[i * 2 + ch] = std::clamp(out[0][ch], -32768.0, 32767.0);
}
}
}
@@ -54,14 +54,14 @@ void Filter::Process(std::vector<s16>& signal) {
/// Calculates the appropriate Q for each biquad in a cascading filter.
/// @param total_count The total number of biquads to be cascaded.
/// @param index 0-index of the biquad to calculate the Q value for.
static double CascadingBiquadQ(std::size_t total_count, std::size_t index) {
static double CascadingBiquadQ(size_t total_count, size_t index) {
const double pole = M_PI * (2 * index + 1) / (4.0 * total_count);
return 1.0 / (2.0 * std::cos(pole));
}
CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size) {
CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) {
std::vector<Filter> cascade(cascade_size);
for (std::size_t i = 0; i < cascade_size; i++) {
for (size_t i = 0; i < cascade_size; i++) {
cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i));
}
return CascadingFilter{std::move(cascade)};

View File

@@ -30,7 +30,7 @@ public:
void Process(std::vector<s16>& signal);
private:
static constexpr std::size_t channel_count = 2;
static constexpr size_t channel_count = 2;
/// Coefficients are in normalized form (a0 = 1.0).
double a1, a2, b0, b1, b2;
@@ -46,7 +46,7 @@ public:
/// Creates a cascading low-pass filter.
/// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
/// @param cascade_size Number of biquads in cascade.
static CascadingFilter LowPass(double cutoff, std::size_t cascade_size);
static CascadingFilter LowPass(double cutoff, size_t cascade_size);
/// Passthrough.
CascadingFilter();

View File

@@ -14,7 +14,7 @@
namespace AudioCore {
/// The Lanczos kernel
static double Lanczos(std::size_t a, double x) {
static double Lanczos(size_t a, double x) {
if (x == 0.0)
return 1.0;
const double px = M_PI * x;
@@ -37,15 +37,15 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
}
state.nyquist.Process(input);
constexpr std::size_t taps = InterpolationState::lanczos_taps;
const std::size_t num_frames = input.size() / 2;
constexpr size_t taps = InterpolationState::lanczos_taps;
const size_t num_frames = input.size() / 2;
std::vector<s16> output;
output.reserve(static_cast<std::size_t>(input.size() / ratio + 4));
output.reserve(static_cast<size_t>(input.size() / ratio + 4));
double& pos = state.position;
auto& h = state.history;
for (std::size_t i = 0; i < num_frames; ++i) {
for (size_t i = 0; i < num_frames; ++i) {
std::rotate(h.begin(), h.end() - 1, h.end());
h[0][0] = input[i * 2 + 0];
h[0][1] = input[i * 2 + 1];
@@ -53,7 +53,7 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
while (pos <= 1.0) {
double l = 0.0;
double r = 0.0;
for (std::size_t j = 0; j < h.size(); j++) {
for (size_t j = 0; j < h.size(); j++) {
l += Lanczos(taps, pos + j - taps + 1) * h[j][0];
r += Lanczos(taps, pos + j - taps + 1) * h[j][1];
}

View File

@@ -12,8 +12,8 @@
namespace AudioCore {
struct InterpolationState {
static constexpr std::size_t lanczos_taps = 4;
static constexpr std::size_t history_size = lanczos_taps * 2 - 1;
static constexpr size_t lanczos_taps = 4;
static constexpr size_t history_size = lanczos_taps * 2 - 1;
double current_ratio = 0.0;
CascadingFilter nyquist;

View File

@@ -39,8 +39,7 @@ StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&&
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
}
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
std::size_t max_count) {
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
return stream->GetTagsAndReleaseBuffers(max_count);
}

View File

@@ -25,7 +25,7 @@ public:
Stream::ReleaseCallback&& release_callback);
/// Returns a vector of recently released buffers specified by tag for the specified stream
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
/// Starts an audio stream for playback
void StartStream(StreamPtr stream);

View File

@@ -3,12 +3,9 @@
// Refer to the license.txt file included.
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_out.h"
#include "audio_core/audio_renderer.h"
#include "audio_core/codec.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/event.h"
#include "core/memory.h"
namespace AudioCore {
@@ -16,57 +13,20 @@ namespace AudioCore {
constexpr u32 STREAM_SAMPLE_RATE{48000};
constexpr u32 STREAM_NUM_CHANNELS{2};
class AudioRenderer::VoiceState {
public:
bool IsPlaying() const {
return is_in_use && info.play_state == PlayState::Started;
}
const VoiceOutStatus& GetOutStatus() const {
return out_status;
}
const VoiceInfo& GetInfo() const {
return info;
}
VoiceInfo& Info() {
return info;
}
void SetWaveIndex(std::size_t index);
std::vector<s16> DequeueSamples(std::size_t sample_count);
void UpdateState();
void RefreshBuffer();
private:
bool is_in_use{};
bool is_refresh_pending{};
std::size_t wave_index{};
std::size_t offset{};
Codec::ADPCMState adpcm_state{};
InterpolationState interp_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
audio_out = std::make_unique<AudioCore::AudioOut>();
stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
[=]() { buffer_event->Signal(); });
audio_out->StartStream(stream);
audio_core = std::make_unique<AudioCore::AudioOut>();
stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
[=]() { buffer_event->Signal(); });
audio_core->StartStream(stream);
QueueMixedBuffer(0);
QueueMixedBuffer(1);
QueueMixedBuffer(2);
}
AudioRenderer::~AudioRenderer() = default;
u32 AudioRenderer::GetSampleRate() const {
return worker_params.sample_rate;
}
@@ -92,8 +52,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
memory_pool_count * sizeof(MemoryPoolInfo));
// Copy VoiceInfo structs
std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
config.voice_resource_size};
size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
config.voice_resource_size};
for (auto& voice : voices) {
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
offset += sizeof(VoiceInfo);
@@ -112,7 +72,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
// Update memory pool state
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (std::size_t index = 0; index < memory_pool.size(); ++index) {
for (size_t index = 0; index < memory_pool.size(); ++index) {
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
memory_pool[index].state = MemoryPoolStates::Attached;
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
@@ -133,7 +93,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
response_data.memory_pools_size);
// Copy output voice status
std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
for (const auto& voice : voices) {
std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
sizeof(VoiceOutStatus));
@@ -143,12 +103,12 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
return output_params;
}
void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
wave_index = index & 3;
is_refresh_pending = true;
}
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
if (!IsPlaying()) {
return {};
}
@@ -157,9 +117,9 @@ std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_co
RefreshBuffer();
}
const std::size_t max_size{samples.size() - offset};
const std::size_t dequeue_offset{offset};
std::size_t size{sample_count * STREAM_NUM_CHANNELS};
const size_t max_size{samples.size() - offset};
const size_t dequeue_offset{offset};
size_t size{sample_count * STREAM_NUM_CHANNELS};
if (size > max_size) {
size = max_size;
}
@@ -224,7 +184,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
case 1:
// 1 channel is upsampled to 2 channel
samples.resize(new_samples.size() * 2);
for (std::size_t index = 0; index < new_samples.size(); ++index) {
for (size_t index = 0; index < new_samples.size(); ++index) {
samples[index * 2] = new_samples[index];
samples[index * 2 + 1] = new_samples[index];
}
@@ -250,7 +210,7 @@ static constexpr s16 ClampToS16(s32 value) {
}
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
constexpr std::size_t BUFFER_SIZE{512};
constexpr size_t BUFFER_SIZE{512};
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
for (auto& voice : voices) {
@@ -258,7 +218,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
continue;
}
std::size_t offset{};
size_t offset{};
s64 samples_remaining{BUFFER_SIZE};
while (samples_remaining > 0) {
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
@@ -276,11 +236,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
}
}
}
audio_out->QueueBuffer(stream, tag, std::move(buffer));
audio_core->QueueBuffer(stream, tag, std::move(buffer));
}
void AudioRenderer::ReleaseAndQueueBuffers() {
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
for (const auto& tag : released_buffers) {
QueueMixedBuffer(tag);
}

View File

@@ -8,20 +8,16 @@
#include <memory>
#include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_out.h"
#include "audio_core/codec.h"
#include "audio_core/stream.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
class Event;
}
#include "core/hle/kernel/event.h"
namespace AudioCore {
class AudioOut;
enum class PlayState : u8 {
Started = 0,
Stopped = 1,
@@ -162,8 +158,6 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
class AudioRenderer {
public:
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
~AudioRenderer();
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
@@ -172,12 +166,45 @@ public:
u32 GetMixBufferCount() const;
private:
class VoiceState;
class VoiceState {
public:
bool IsPlaying() const {
return is_in_use && info.play_state == PlayState::Started;
}
const VoiceOutStatus& GetOutStatus() const {
return out_status;
}
const VoiceInfo& GetInfo() const {
return info;
}
VoiceInfo& Info() {
return info;
}
void SetWaveIndex(size_t index);
std::vector<s16> DequeueSamples(size_t sample_count);
void UpdateState();
void RefreshBuffer();
private:
bool is_in_use{};
bool is_refresh_pending{};
size_t wave_index{};
size_t offset{};
Codec::ADPCMState adpcm_state{};
InterpolationState interp_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::unique_ptr<AudioOut> audio_out;
std::unique_ptr<AudioCore::AudioOut> audio_core;
AudioCore::StreamPtr stream;
};

View File

@@ -8,27 +8,27 @@
namespace AudioCore::Codec {
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long.
constexpr std::size_t FRAME_LEN = 8;
constexpr std::size_t SAMPLES_PER_FRAME = 14;
constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const std::size_t ret_size =
const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
std::vector<s16> ret(ret_size);
int yn1 = state.yn1, yn2 = state.yn2;
const std::size_t NUM_FRAMES =
const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (std::size_t framei = 0; framei < NUM_FRAMES; framei++) {
for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF);
const int idx = (frame_header >> 4) & 0x7;
@@ -53,9 +53,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM
return static_cast<s16>(val);
};
std::size_t outputi = framei * SAMPLES_PER_FRAME;
std::size_t datai = framei * FRAME_LEN + 1;
for (std::size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
size_t outputi = framei * SAMPLES_PER_FRAME;
size_t datai = framei * FRAME_LEN + 1;
for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
ret[outputi] = sample1;
outputi++;

View File

@@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>;
* @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length
*/
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state);
}; // namespace AudioCore::Codec

View File

@@ -3,23 +3,27 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <atomic>
#include <cstring>
#include <mutex>
#include "audio_core/cubeb_sink.h"
#include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/logging/log.h"
#include "common/ring_buffer.h"
#include "core/settings.h"
namespace AudioCore {
class CubebSinkStream final : public SinkStream {
class SinkStreamImpl final : public SinkStream {
public:
CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
: ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate,
num_channels} {
SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
: ctx{ctx}, num_channels{num_channels_} {
if (num_channels == 6) {
// 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
// channel for now
is_6_channel = true;
num_channels = 2;
}
cubeb_stream_params params{};
params.rate = sample_rate;
@@ -34,7 +38,7 @@ public:
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
&params, std::max(512u, minimum_latency),
&CubebSinkStream::DataCallback, &CubebSinkStream::StateCallback,
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
this) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
return;
@@ -46,7 +50,7 @@ public:
}
}
~CubebSinkStream() {
~SinkStreamImpl() {
if (!ctx) {
return;
}
@@ -58,32 +62,27 @@ public:
cubeb_stream_destroy(stream_backend);
}
void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
if (source_num_channels > num_channels) {
// Downsample 6 channels to 2
std::vector<s16> buf;
buf.reserve(samples.size() * num_channels / source_num_channels);
for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
for (std::size_t ch = 0; ch < num_channels; ch++) {
buf.push_back(samples[i + ch]);
}
}
queue.Push(buf);
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
if (!ctx) {
return;
}
queue.Push(samples);
}
std::lock_guard lock{queue_mutex};
std::size_t SamplesInQueue(u32 num_channels) const override {
if (!ctx)
return 0;
queue.reserve(queue.size() + samples.size() * GetNumChannels());
return queue.Size() / num_channels;
}
void Flush() override {
should_flush = true;
if (is_6_channel) {
// Downsample 6 channels to 2
const size_t sample_count_copy_size = samples.size() * 2;
queue.reserve(sample_count_copy_size);
for (size_t i = 0; i < samples.size(); i += num_channels) {
queue.push_back(samples[i]);
queue.push_back(samples[i + 1]);
}
} else {
// Copy as-is
std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
}
}
u32 GetNumChannels() const {
@@ -96,11 +95,10 @@ private:
cubeb* ctx{};
cubeb_stream* stream_backend{};
u32 num_channels{};
bool is_6_channel{};
Common::RingBuffer<s16, 0x10000> queue;
std::array<s16, 2> last_frame;
std::atomic<bool> should_flush{};
TimeStretcher time_stretch;
std::mutex queue_mutex;
std::vector<s16> queue;
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames);
@@ -119,10 +117,10 @@ CubebSink::CubebSink(std::string target_device_name) {
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
} else {
const auto collection_end{collection.device + collection.count};
const auto device{
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
return target_device_name == info.friendly_name;
})};
const auto device{std::find_if(collection.device, collection_end,
[&](const cubeb_device_info& device) {
return target_device_name == device.friendly_name;
})};
if (device != collection_end) {
output_device = device->devid;
}
@@ -146,59 +144,44 @@ CubebSink::~CubebSink() {
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) {
sink_streams.push_back(
std::make_unique<CubebSinkStream>(ctx, sample_rate, num_channels, output_device, name));
std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
return *sink_streams.back();
}
long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames) {
CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames) {
SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data);
u8* buffer = reinterpret_cast<u8*>(output_buffer);
if (!impl) {
return {};
}
const std::size_t num_channels = impl->GetNumChannels();
const std::size_t samples_to_write = num_channels * num_frames;
std::size_t samples_written;
std::lock_guard lock{impl->queue_mutex};
if (Settings::values.enable_audio_stretching) {
const std::vector<s16> in{impl->queue.Pop()};
const std::size_t num_in{in.size() / num_channels};
s16* const out{reinterpret_cast<s16*>(buffer)};
const std::size_t out_frames =
impl->time_stretch.Process(in.data(), num_in, out, num_frames);
samples_written = out_frames * num_channels;
const size_t frames_to_write{
std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))};
if (impl->should_flush) {
impl->time_stretch.Flush();
impl->should_flush = false;
}
} else {
samples_written = impl->queue.Pop(buffer, samples_to_write);
}
memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels());
impl->queue.erase(impl->queue.begin(),
impl->queue.begin() + frames_to_write * impl->GetNumChannels());
if (samples_written >= num_channels) {
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
num_channels * sizeof(s16));
}
// Fill the rest of the frames with last_frame
for (std::size_t i = samples_written; i < samples_to_write; i += num_channels) {
std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16));
if (frames_to_write < num_frames) {
// Fill the rest of the frames with silence
memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0,
(num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels());
}
return num_frames;
}
void CubebSinkStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
std::vector<std::string> ListCubebSinkDevices() {
std::vector<std::string> device_list;
cubeb* ctx;
if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
return {};
}
@@ -207,7 +190,7 @@ std::vector<std::string> ListCubebSinkDevices() {
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
} else {
for (std::size_t i = 0; i < collection.count; i++) {
for (size_t i = 0; i < collection.count; i++) {
const cubeb_device_info& device = collection.device[i];
if (device.friendly_name) {
device_list.emplace_back(device.friendly_name);

View File

@@ -21,12 +21,6 @@ public:
private:
struct NullSinkStreamImpl final : SinkStream {
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
std::size_t SamplesInQueue(u32 /*num_channels*/) const override {
return 0;
}
void Flush() override {}
} null_sink_stream;
};

View File

@@ -24,7 +24,7 @@ const std::vector<SinkDetails> g_sink_details = {
[] { return std::vector<std::string>{"null"}; }},
};
const SinkDetails& GetSinkDetails(std::string_view sink_id) {
const SinkDetails& GetSinkDetails(std::string sink_id) {
auto iter =
std::find_if(g_sink_details.begin(), g_sink_details.end(),
[sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });

View File

@@ -6,8 +6,6 @@
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
@@ -32,6 +30,6 @@ struct SinkDetails {
extern const std::vector<SinkDetails> g_sink_details;
const SinkDetails& GetSinkDetails(std::string_view sink_id);
const SinkDetails& GetSinkDetails(std::string sink_id);
} // namespace AudioCore

View File

@@ -25,10 +25,6 @@ public:
* @param samples Samples in interleaved stereo PCM16 format.
*/
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
virtual std::size_t SamplesInQueue(u32 num_channels) const = 0;
virtual void Flush() = 0;
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;

View File

@@ -7,18 +7,16 @@
#include "audio_core/sink.h"
#include "audio_core/sink_details.h"
#include "audio_core/sink_stream.h"
#include "audio_core/stream.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/settings.h"
namespace AudioCore {
constexpr std::size_t MaxAudioBufferCount{32};
constexpr size_t MaxAudioBufferCount{32};
u32 Stream::GetNumChannels() const {
switch (format) {
@@ -53,7 +51,7 @@ void Stream::Stop() {
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
@@ -74,7 +72,6 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
void Stream::PlayNextBuffer() {
if (!IsPlaying()) {
// Ensure we are in playing state before playing the next buffer
sink_stream.Flush();
return;
}
@@ -85,7 +82,6 @@ void Stream::PlayNextBuffer() {
if (queued_buffers.empty()) {
// No queued buffers - we are effectively paused
sink_stream.Flush();
return;
}
@@ -93,16 +89,12 @@ void Stream::PlayNextBuffer() {
queued_buffers.pop();
VolumeAdjustSamples(active_buffer->Samples());
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
void Stream::ReleaseActiveBuffer() {
MICROPROFILE_SCOPE(AudioOutput);
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
@@ -123,9 +115,9 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const {
return {};
}
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
std::vector<Buffer::Tag> tags;
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
tags.push_back(released_buffers.front()->GetTag());
released_buffers.pop();
}

View File

@@ -11,16 +11,13 @@
#include <queue>
#include "audio_core/buffer.h"
#include "audio_core/sink_stream.h"
#include "common/assert.h"
#include "common/common_types.h"
namespace CoreTiming {
struct EventType;
}
#include "core/core_timing.h"
namespace AudioCore {
class SinkStream;
/**
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
*/
@@ -52,7 +49,7 @@ public:
bool ContainsBuffer(Buffer::Tag tag) const;
/// Returns a vector of recently released buffers specified by tag
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
/// Returns true if the stream is currently playing
bool IsPlaying() const {
@@ -60,7 +57,7 @@ public:
}
/// Returns the number of queued buffers
std::size_t GetQueueSize() const {
size_t GetQueueSize() const {
return queued_buffers.size();
}

View File

@@ -1,69 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cmath>
#include <cstddef>
#include "audio_core/time_stretch.h"
#include "common/logging/log.h"
namespace AudioCore {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count)
: m_sample_rate(sample_rate), m_channel_count(channel_count) {
m_sound_touch.setChannels(channel_count);
m_sound_touch.setSampleRate(sample_rate);
m_sound_touch.setPitch(1.0);
m_sound_touch.setTempo(1.0);
}
void TimeStretcher::Clear() {
m_sound_touch.clear();
}
void TimeStretcher::Flush() {
m_sound_touch.flush();
}
std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
std::size_t num_out) {
const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
// We were given actual_samples number of samples, and num_samples were requested from us.
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
const double max_latency = 1.0; // seconds
const double max_backlog = m_sample_rate * max_latency;
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
if (backlog_fullness > 5.0) {
// Too many samples in backlog: Don't push anymore on
num_in = 0;
}
// We ideally want the backlog to be about 50% full.
// This gives some headroom both ways to prevent underflow and overflow.
// We tweak current_ratio to encourage this.
constexpr double tweak_time_scale = 0.05; // seconds
const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
// This low-pass filter smoothes out variance in the calculated stretch ratio.
// The time-scale determines how responsive this filter is.
constexpr double lpf_time_scale = 2.0; // seconds
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
// Place a lower limit of 5% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
backlog_fullness);
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
}
} // namespace AudioCore

View File

@@ -1,35 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <SoundTouch.h>
#include "common/common_types.h"
namespace AudioCore {
class TimeStretcher {
public:
TimeStretcher(u32 sample_rate, u32 channel_count);
/// @param in Input sample buffer
/// @param num_in Number of input frames in `in`
/// @param out Output sample buffer
/// @param num_out Desired number of output frames in `out`
/// @returns Actual number of frames written to `out`
std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
void Clear();
void Flush();
private:
u32 m_sample_rate;
u32 m_channel_count;
soundtouch::SoundTouch m_sound_touch;
double m_stretch_ratio = 1.0;
};
} // namespace AudioCore

View File

@@ -1,16 +1,13 @@
# Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
# Also if this is a CI build, add the build name (ie: Nightly, Bleeding Edge) to the scm_rev file as well
set(REPO_NAME "")
set(BUILD_VERSION "0")
if ($ENV{CI})
if ($ENV{TRAVIS})
set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
set(BUILD_TAG $ENV{TRAVIS_TAG})
elseif($ENV{APPVEYOR})
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
endif()
# regex capture the string nightly or canary into CMAKE_MATCH_1
# regex capture the string nightly or bleeding-edge into CMAKE_MATCH_1
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if (${CMAKE_MATCH_COUNT} GREATER 0)
# capitalize the first letter of each word in the repo name.
@@ -19,21 +16,10 @@ if ($ENV{CI})
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
# this leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ")
endforeach()
if (BUILD_TAG)
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
if (${CMAKE_MATCH_COUNT} GREATER 0)
set(BUILD_VERSION ${CMAKE_MATCH_1})
endif()
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
endif()
endif()
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
@@ -71,7 +57,6 @@ add_library(common STATIC
param_package.cpp
param_package.h
quaternion.h
ring_buffer.h
scm_rev.cpp
scm_rev.h
scope_exit.h

View File

@@ -8,13 +8,13 @@
namespace Common {
template <typename T>
constexpr T AlignUp(T value, std::size_t size) {
constexpr T AlignUp(T value, size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value + (size - value % size) % size);
}
template <typename T>
constexpr T AlignDown(T value, std::size_t size) {
constexpr T AlignDown(T value, size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value - value % size);
}

View File

@@ -129,8 +129,8 @@ private:
public:
/// Constants to allow limited introspection of fields if needed
static constexpr std::size_t position = Position;
static constexpr std::size_t bits = Bits;
static constexpr size_t position = Position;
static constexpr size_t bits = Bits;
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
/**
@@ -178,7 +178,8 @@ public:
return ExtractValue(storage);
}
constexpr explicit operator bool() const {
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
constexpr FORCE_INLINE bool ToBool() const {
return Value() != 0;
}

View File

@@ -170,14 +170,14 @@ public:
m_val |= (IntTy)1 << bit;
}
static BitSet AllTrue(std::size_t count) {
static BitSet AllTrue(size_t count) {
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
}
Ref operator[](std::size_t bit) {
Ref operator[](size_t bit) {
return Ref(this, (IntTy)1 << bit);
}
const Ref operator[](std::size_t bit) const {
const Ref operator[](size_t bit) const {
return (*const_cast<BitSet*>(this))[bit];
}
bool operator==(BitSet other) const {

View File

@@ -114,7 +114,7 @@ static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
return b;
}
static uint64 HashLen0to16(const char* s, std::size_t len) {
static uint64 HashLen0to16(const char* s, size_t len) {
if (len >= 8) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) + k2;
@@ -141,7 +141,7 @@ static uint64 HashLen0to16(const char* s, std::size_t len) {
// This probably works well for 16-byte strings as well, but it may be overkill
// in that case.
static uint64 HashLen17to32(const char* s, std::size_t len) {
static uint64 HashLen17to32(const char* s, size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k1;
uint64 b = Fetch64(s + 8);
@@ -170,7 +170,7 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint
}
// Return an 8-byte hash for 33 to 64 bytes.
static uint64 HashLen33to64(const char* s, std::size_t len) {
static uint64 HashLen33to64(const char* s, size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k2;
uint64 b = Fetch64(s + 8);
@@ -191,7 +191,7 @@ static uint64 HashLen33to64(const char* s, std::size_t len) {
return b + x;
}
uint64 CityHash64(const char* s, std::size_t len) {
uint64 CityHash64(const char* s, size_t len) {
if (len <= 32) {
if (len <= 16) {
return HashLen0to16(s, len);
@@ -212,7 +212,7 @@ uint64 CityHash64(const char* s, std::size_t len) {
x = x * k1 + Fetch64(s);
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
len = (len - 1) & ~static_cast<std::size_t>(63);
len = (len - 1) & ~static_cast<size_t>(63);
do {
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
@@ -229,17 +229,17 @@ uint64 CityHash64(const char* s, std::size_t len) {
HashLen16(v.second, w.second) + x);
}
uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) {
return CityHash64WithSeeds(s, len, k2, seed);
}
uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) {
uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) {
return HashLen16(CityHash64(s, len) - seed0, seed1);
}
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
// of any length representable in signed long. Based on City and Murmur.
static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
static uint128 CityMurmur(const char* s, size_t len, uint128 seed) {
uint64 a = Uint128Low64(seed);
uint64 b = Uint128High64(seed);
uint64 c = 0;
@@ -269,7 +269,7 @@ static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
return uint128(a ^ b, HashLen16(b, a));
}
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
if (len < 128) {
return CityMurmur(s, len, seed);
}
@@ -313,7 +313,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
w.first *= 9;
v.first *= k0;
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
for (std::size_t tail_done = 0; tail_done < len;) {
for (size_t tail_done = 0; tail_done < len;) {
tail_done += 32;
y = Rotate(x + y, 42) * k0 + v.second;
w.first += Fetch64(s + len - tail_done + 16);
@@ -331,7 +331,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
}
uint128 CityHash128(const char* s, std::size_t len) {
uint128 CityHash128(const char* s, size_t len) {
return len >= 16
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
: CityHash128WithSeed(s, len, uint128(k0, k1));

View File

@@ -63,7 +63,7 @@
#include <utility>
#include <stdint.h>
#include <stdlib.h> // for std::size_t.
#include <stdlib.h> // for size_t.
namespace Common {
@@ -77,22 +77,22 @@ inline uint64_t Uint128High64(const uint128& x) {
}
// Hash function for a byte array.
uint64_t CityHash64(const char* buf, std::size_t len);
uint64_t CityHash64(const char* buf, size_t len);
// Hash function for a byte array. For convenience, a 64-bit seed is also
// hashed into the result.
uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed);
// Hash function for a byte array. For convenience, two seeds are also
// hashed into the result.
uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1);
uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1);
// Hash function for a byte array.
uint128 CityHash128(const char* s, std::size_t len);
uint128 CityHash128(const char* s, size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result.
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed);
// Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function.

View File

@@ -76,7 +76,7 @@ namespace FileUtil {
// Modifies argument.
static void StripTailDirSlashes(std::string& fname) {
if (fname.length() > 1) {
std::size_t i = fname.length();
size_t i = fname.length();
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
--i;
fname.resize(i);
@@ -201,7 +201,7 @@ bool CreateFullPath(const std::string& fullPath) {
return true;
}
std::size_t position = 0;
size_t position = 0;
while (true) {
// Find next sub path
position = fullPath.find(DIR_SEP_CHR, position);
@@ -299,7 +299,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
std::array<char, 1024> buffer;
while (!feof(input.get())) {
// read input
std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
if (rnum != buffer.size()) {
if (ferror(input.get()) != 0) {
LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
@@ -309,7 +309,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
}
// write output
std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
if (wnum != rnum) {
LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
destFilename, GetLastErrorMsg());
@@ -756,15 +756,15 @@ std::string GetNANDRegistrationDir(bool system) {
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
}
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
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 char* filename, std::string& str) {
size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
IOFile file(filename, text_file ? "r" : "rb");
if (!file.IsOpen())
return 0;
return false;
str.resize(static_cast<u32>(file.GetSize()));
return file.ReadArray(&str[0], str.size());
@@ -829,7 +829,7 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string_view GetParentPath(std::string_view path) {
const auto name_bck_index = path.rfind('\\');
const auto name_fwd_index = path.rfind('/');
std::size_t name_index;
size_t name_index;
if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
name_index = std::min(name_bck_index, name_fwd_index);
@@ -868,7 +868,7 @@ std::string_view GetFilename(std::string_view path) {
}
std::string_view GetExtensionFromFilename(std::string_view name) {
const std::size_t index = name.rfind('.');
const size_t index = name.rfind('.');
if (index == std::string_view::npos) {
return {};

View File

@@ -143,9 +143,8 @@ const std::string& GetExeDirectory();
std::string AppDataRoamingDirectory();
#endif
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
/**
* Splits the filename into 8.3 format
@@ -178,10 +177,10 @@ std::string_view RemoveTrailingSlash(std::string_view path);
// Creates a new vector containing indices [first, last) from the original.
template <typename T>
std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) {
if (first >= last)
return {};
last = std::min<std::size_t>(last, vector.size());
last = std::min<size_t>(last, vector.size());
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
@@ -214,47 +213,47 @@ public:
bool Close();
template <typename T>
std::size_t ReadArray(T* data, std::size_t length) const {
size_t ReadArray(T* data, size_t length) const {
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
return std::numeric_limits<std::size_t>::max();
return std::numeric_limits<size_t>::max();
}
return std::fread(data, sizeof(T), length, m_file);
}
template <typename T>
std::size_t WriteArray(const T* data, std::size_t length) {
size_t WriteArray(const T* data, size_t length) {
static_assert(std::is_trivially_copyable_v<T>,
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
return std::numeric_limits<std::size_t>::max();
return std::numeric_limits<size_t>::max();
}
return std::fwrite(data, sizeof(T), length, m_file);
}
template <typename T>
std::size_t ReadBytes(T* data, std::size_t length) const {
size_t ReadBytes(T* data, size_t length) const {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return ReadArray(reinterpret_cast<char*>(data), length);
}
template <typename T>
std::size_t WriteBytes(const T* data, std::size_t length) {
size_t WriteBytes(const T* data, size_t length) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
return WriteArray(reinterpret_cast<const char*>(data), length);
}
template <typename T>
std::size_t WriteObject(const T& object) {
size_t WriteObject(const T& object) {
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
return WriteArray(&object, 1);
}
std::size_t WriteString(const std::string& str) {
size_t WriteString(const std::string& str) {
return WriteArray(str.c_str(), str.length());
}

View File

@@ -17,7 +17,7 @@ namespace Common {
* @param len Length of data (in bytes) to compute hash over
* @returns 64-bit hash value that was computed over the data block
*/
static inline u64 ComputeHash64(const void* data, std::size_t len) {
static inline u64 ComputeHash64(const void* data, size_t len) {
return CityHash64(static_cast<const char*>(data), len);
}
@@ -63,7 +63,7 @@ struct HashableStruct {
return !(*this == o);
};
std::size_t Hash() const {
size_t Hash() const {
return Common::ComputeStructHash64(state);
}
};

View File

@@ -3,9 +3,6 @@
// Refer to the license.txt file included.
#include "common/hex_util.h"
#include "common/logging/log.h"
namespace Common {
u8 ToHexNibble(char c1) {
if (c1 >= 65 && c1 <= 70)
@@ -14,30 +11,17 @@ u8 ToHexNibble(char c1) {
return c1 - 87;
if (c1 >= 48 && c1 <= 57)
return c1 - 48;
LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
return 0;
throw std::logic_error("Invalid hex digit");
}
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
if (len != 32) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=32, "
"actual={}).",
len);
return {};
}
std::array<u8, 16> operator""_array16(const char* str, size_t len) {
if (len != 32)
throw std::logic_error("Not of correct size.");
return HexStringToArray<16>(str);
}
std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
if (len != 64) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=64, "
"actual={}).",
len);
return {};
}
std::array<u8, 32> operator""_array32(const char* str, size_t len) {
if (len != 64)
throw std::logic_error("Not of correct size.");
return HexStringToArray<32>(str);
}
} // namespace Common

View File

@@ -10,24 +10,22 @@
#include <fmt/format.h>
#include "common/common_types.h"
namespace Common {
u8 ToHexNibble(char c1);
template <std::size_t Size, bool le = false>
template <size_t Size, bool le = false>
std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
for (size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
} else {
for (std::size_t i = 0; i < 2 * Size; i += 2)
for (size_t i = 0; i < 2 * Size; i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
return out;
}
template <std::size_t Size>
template <size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;
for (u8 c : array)
@@ -35,7 +33,5 @@ std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
return out;
}
std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
} // namespace Common
std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);

View File

@@ -135,7 +135,7 @@ FileBackend::FileBackend(const std::string& filename)
void FileBackend::Write(const Entry& entry) {
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
// know)
constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
return;
}

View File

@@ -100,7 +100,7 @@ public:
private:
FileUtil::IOFile file;
std::size_t bytes_written;
size_t bytes_written;
};
void AddBackend(std::unique_ptr<Backend> backend);

View File

@@ -71,7 +71,7 @@ void Filter::ResetAll(Level level) {
}
void Filter::SetClassLevel(Class log_class, Level level) {
class_levels[static_cast<std::size_t>(log_class)] = level;
class_levels[static_cast<size_t>(log_class)] = level;
}
void Filter::ParseFilterString(std::string_view filter_view) {
@@ -93,8 +93,7 @@ void Filter::ParseFilterString(std::string_view filter_view) {
}
bool Filter::CheckMessage(Class log_class, Level level) const {
return static_cast<u8>(level) >=
static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
}
bool Filter::IsDebug() const {

View File

@@ -19,7 +19,7 @@ namespace Log {
class Filter {
public:
/// Initializes the filter with all classes having `default_level` as the minimum level.
explicit Filter(Level default_level = Level::Info);
Filter(Level default_level = Level::Info);
/// Resets the filter so that all classes have `level` as the minimum displayed level.
void ResetAll(Level level);
@@ -49,6 +49,6 @@ public:
bool IsDebug() const;
private:
std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels;
std::array<Level, (size_t)Class::Count> class_levels;
};
} // namespace Log

View File

@@ -12,14 +12,14 @@ namespace Log {
/// Specifies the severity or level of detail of the log message.
enum class Level : u8 {
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
///< pollute logs.
/// pollute logs.
Debug, ///< Less detailed debugging information.
Info, ///< Status information from important points during execution.
Warning, ///< Minor or potential problems found during execution of a task.
Error, ///< Major problems found during execution of a task that prevent it from being
///< completed.
Critical, ///< Major problems during execution that threaten the stability of the entire
///< application.
/// completed.
Critical, ///< Major problems during execution that threathen the stability of the entire
/// application.
Count ///< Total number of logging levels
};
@@ -49,7 +49,7 @@ enum class Class : ClassType {
Kernel, ///< The HLE implementation of the CTR kernel
Kernel_SVC, ///< Kernel system calls
Service, ///< HLE implementation of system services. Each major service
///< should have its own subclass.
/// should have its own subclass.
Service_ACC, ///< The ACC (Accounts) service
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service

View File

@@ -42,7 +42,7 @@ void PrintColoredMessage(const Entry& entry) {
return;
}
CONSOLE_SCREEN_BUFFER_INFO original_info = {};
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
GetConsoleScreenBufferInfo(console_handle, &original_info);
WORD color = 0;

View File

@@ -15,6 +15,6 @@ struct Entry;
std::string FormatLogMessage(const Entry& entry);
/// Formats and prints a log entry to stderr.
void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
void PrintColoredMessage(const Entry& entry);
} // namespace Log

View File

@@ -25,7 +25,7 @@
// This is purposely not a full wrapper for virtualalloc/mmap, but it
// provides exactly the primitive operations that Dolphin needs.
void* AllocateExecutableMemory(std::size_t size, bool low) {
void* AllocateExecutableMemory(size_t size, bool low) {
#if defined(_WIN32)
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
@@ -74,7 +74,7 @@ void* AllocateExecutableMemory(std::size_t size, bool low) {
return ptr;
}
void* AllocateMemoryPages(std::size_t size) {
void* AllocateMemoryPages(size_t size) {
#ifdef _WIN32
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
#else
@@ -90,7 +90,7 @@ void* AllocateMemoryPages(std::size_t size) {
return ptr;
}
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
void* AllocateAlignedMemory(size_t size, size_t alignment) {
#ifdef _WIN32
void* ptr = _aligned_malloc(size, alignment);
#else
@@ -109,7 +109,7 @@ void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
return ptr;
}
void FreeMemoryPages(void* ptr, std::size_t size) {
void FreeMemoryPages(void* ptr, size_t size) {
if (ptr) {
#ifdef _WIN32
if (!VirtualFree(ptr, 0, MEM_RELEASE))
@@ -130,7 +130,7 @@ void FreeAlignedMemory(void* ptr) {
}
}
void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
@@ -140,7 +140,7 @@ void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
#endif
}
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,

View File

@@ -7,13 +7,13 @@
#include <cstddef>
#include <string>
void* AllocateExecutableMemory(std::size_t size, bool low = true);
void* AllocateMemoryPages(std::size_t size);
void FreeMemoryPages(void* ptr, std::size_t size);
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
void* AllocateExecutableMemory(size_t size, bool low = true);
void* AllocateMemoryPages(size_t size);
void FreeMemoryPages(void* ptr, size_t size);
void* AllocateAlignedMemory(size_t size, size_t alignment);
void FreeAlignedMemory(void* ptr);
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
std::string MemUsage();
inline int GetPageSize() {

View File

@@ -16,7 +16,7 @@
// Call directly after the command or use the error num.
// This function might change the error code.
std::string GetLastErrorMsg() {
static const std::size_t buff_size = 255;
static const size_t buff_size = 255;
char err_str[buff_size];
#ifdef _WIN32

View File

@@ -1,111 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <array>
#include <atomic>
#include <cstddef>
#include <cstring>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
namespace Common {
/// SPSC ring buffer
/// @tparam T Element type
/// @tparam capacity Number of slots in ring buffer
/// @tparam granularity Slot size in terms of number of elements
template <typename T, std::size_t capacity, std::size_t granularity = 1>
class RingBuffer {
/// A "slot" is made of `granularity` elements of `T`.
static constexpr std::size_t slot_size = granularity * sizeof(T);
// T must be safely memcpy-able and have a trivial default constructor.
static_assert(std::is_trivial_v<T>);
// Ensure capacity is sensible.
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
// Ensure lock-free.
static_assert(std::atomic<std::size_t>::is_always_lock_free);
public:
/// Pushes slots into the ring buffer
/// @param new_slots Pointer to the slots to push
/// @param slot_count Number of slots to push
/// @returns The number of slots actually pushed
std::size_t Push(const void* new_slots, std::size_t slot_count) {
const std::size_t write_index = m_write_index.load();
const std::size_t slots_free = capacity + m_read_index.load() - write_index;
const std::size_t push_count = std::min(slot_count, slots_free);
const std::size_t pos = write_index % capacity;
const std::size_t first_copy = std::min(capacity - pos, push_count);
const std::size_t second_copy = push_count - first_copy;
const char* in = static_cast<const char*>(new_slots);
std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size);
in += first_copy * slot_size;
std::memcpy(m_data.data(), in, second_copy * slot_size);
m_write_index.store(write_index + push_count);
return push_count;
}
std::size_t Push(const std::vector<T>& input) {
return Push(input.data(), input.size());
}
/// Pops slots from the ring buffer
/// @param output Where to store the popped slots
/// @param max_slots Maximum number of slots to pop
/// @returns The number of slots actually popped
std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
const std::size_t read_index = m_read_index.load();
const std::size_t slots_filled = m_write_index.load() - read_index;
const std::size_t pop_count = std::min(slots_filled, max_slots);
const std::size_t pos = read_index % capacity;
const std::size_t first_copy = std::min(capacity - pos, pop_count);
const std::size_t second_copy = pop_count - first_copy;
char* out = static_cast<char*>(output);
std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size);
out += first_copy * slot_size;
std::memcpy(out, m_data.data(), second_copy * slot_size);
m_read_index.store(read_index + pop_count);
return pop_count;
}
std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
std::vector<T> out(std::min(max_slots, capacity) * granularity);
const std::size_t count = Pop(out.data(), out.size() / granularity);
out.resize(count * granularity);
return out;
}
/// @returns Number of slots used
std::size_t Size() const {
return m_write_index.load() - m_read_index.load();
}
/// @returns Maximum size of ring buffer
constexpr std::size_t Capacity() const {
return capacity;
}
private:
// It is important to align the below variables for performance reasons:
// Having them on the same cache-line would result in false-sharing between them.
alignas(128) std::atomic<std::size_t> m_read_index{0};
alignas(128) std::atomic<std::size_t> m_write_index{0};
std::array<T, granularity * capacity> m_data;
};
} // namespace Common

View File

@@ -9,8 +9,6 @@
#define GIT_DESC "@GIT_DESC@"
#define BUILD_NAME "@REPO_NAME@"
#define BUILD_DATE "@BUILD_DATE@"
#define BUILD_FULLNAME "@BUILD_FULLNAME@"
#define BUILD_VERSION "@BUILD_VERSION@"
namespace Common {
@@ -19,8 +17,6 @@ const char g_scm_branch[] = GIT_BRANCH;
const char g_scm_desc[] = GIT_DESC;
const char g_build_name[] = BUILD_NAME;
const char g_build_date[] = BUILD_DATE;
const char g_build_fullname[] = BUILD_FULLNAME;
const char g_build_version[] = BUILD_VERSION;
} // namespace

View File

@@ -11,7 +11,5 @@ extern const char g_scm_branch[];
extern const char g_scm_desc[];
extern const char g_build_name[];
extern const char g_build_date[];
extern const char g_build_fullname[];
extern const char g_build_version[];
} // namespace Common

View File

@@ -37,7 +37,7 @@ std::string ToUpper(std::string str) {
}
// For Debugging. Read out an u8 array.
std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) {
std::ostringstream oss;
oss << std::setfill('0') << std::hex;
@@ -60,7 +60,7 @@ std::string StringFromBuffer(const std::vector<u8>& data) {
// Turns " hej " into "hej". Also handles tabs.
std::string StripSpaces(const std::string& str) {
const std::size_t s = str.find_first_not_of(" \t\r\n");
const size_t s = str.find_first_not_of(" \t\r\n");
if (str.npos != s)
return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
@@ -121,10 +121,10 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
if (full_path.empty())
return false;
std::size_t dir_end = full_path.find_last_of("/"
size_t dir_end = full_path.find_last_of("/"
// windows needs the : included for something like just "C:" to be considered a directory
#ifdef _WIN32
"\\:"
"\\:"
#endif
);
if (std::string::npos == dir_end)
@@ -132,7 +132,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
else
dir_end += 1;
std::size_t fname_end = full_path.rfind('.');
size_t fname_end = full_path.rfind('.');
if (fname_end < dir_end || std::string::npos == fname_end)
fname_end = full_path.size();
@@ -172,7 +172,7 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
}
std::string TabsToSpaces(int tab_size, std::string in) {
std::size_t i = 0;
size_t i = 0;
while ((i = in.find('\t')) != std::string::npos) {
in.replace(i, 1, tab_size, ' ');
@@ -182,7 +182,7 @@ std::string TabsToSpaces(int tab_size, std::string in) {
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
std::size_t pos = 0;
size_t pos = 0;
if (src == dest)
return result;
@@ -280,22 +280,22 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>&
return {};
}
const std::size_t in_bytes = sizeof(T) * input.size();
const size_t in_bytes = sizeof(T) * input.size();
// Multiply by 4, which is the max number of bytes to encode a codepoint
const std::size_t out_buffer_size = 4 * in_bytes;
const size_t out_buffer_size = 4 * in_bytes;
std::string out_buffer(out_buffer_size, '\0');
auto src_buffer = &input[0];
std::size_t src_bytes = in_bytes;
size_t src_bytes = in_bytes;
auto dst_buffer = &out_buffer[0];
std::size_t dst_bytes = out_buffer.size();
size_t dst_bytes = out_buffer.size();
while (0 != src_bytes) {
std::size_t const iconv_result =
size_t const iconv_result =
iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes);
if (static_cast<std::size_t>(-1) == iconv_result) {
if (static_cast<size_t>(-1) == iconv_result) {
if (EILSEQ == errno || EINVAL == errno) {
// Try to skip the bad character
if (0 != src_bytes) {
@@ -326,22 +326,22 @@ std::u16string UTF8ToUTF16(const std::string& input) {
return {};
}
const std::size_t in_bytes = sizeof(char) * input.size();
const size_t in_bytes = sizeof(char) * input.size();
// Multiply by 4, which is the max number of bytes to encode a codepoint
const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
std::u16string out_buffer(out_buffer_size, char16_t{});
char* src_buffer = const_cast<char*>(&input[0]);
std::size_t src_bytes = in_bytes;
size_t src_bytes = in_bytes;
char* dst_buffer = (char*)(&out_buffer[0]);
std::size_t dst_bytes = out_buffer.size();
size_t dst_bytes = out_buffer.size();
while (0 != src_bytes) {
std::size_t const iconv_result =
size_t const iconv_result =
iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes);
if (static_cast<std::size_t>(-1) == iconv_result) {
if (static_cast<size_t>(-1) == iconv_result) {
if (EILSEQ == errno || EINVAL == errno) {
// Try to skip the bad character
if (0 != src_bytes) {
@@ -381,8 +381,8 @@ std::string SHIFTJISToUTF8(const std::string& input) {
#endif
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) {
std::size_t len = 0;
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len) {
size_t len = 0;
while (len < max_len && buffer[len] != '\0')
++len;

View File

@@ -19,7 +19,7 @@ std::string ToLower(std::string str);
/// Make a string uppercase
std::string ToUpper(std::string str);
std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true);
std::string StringFromBuffer(const std::vector<u8>& data);
@@ -118,7 +118,7 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
* Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* NUL-terminated then the string ends at max_len characters.
*/
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's

View File

@@ -3,15 +3,8 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "common/scm_rev.h"
#include "common/telemetry.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
namespace Telemetry {
void FieldCollection::Accept(VisitorInterface& visitor) const {
@@ -44,62 +37,4 @@ template class Field<std::string>;
template class Field<const char*>;
template class Field<std::chrono::microseconds>;
#ifdef ARCHITECTURE_x86_64
static const char* CpuVendorToStr(Common::CPUVendor vendor) {
switch (vendor) {
case Common::CPUVendor::INTEL:
return "Intel";
case Common::CPUVendor::AMD:
return "Amd";
case Common::CPUVendor::OTHER:
return "Other";
}
UNREACHABLE();
}
#endif
void AppendBuildInfo(FieldCollection& fc) {
const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr};
fc.AddField(FieldType::App, "Git_IsDirty", is_git_dirty);
fc.AddField(FieldType::App, "Git_Branch", Common::g_scm_branch);
fc.AddField(FieldType::App, "Git_Revision", Common::g_scm_rev);
fc.AddField(FieldType::App, "BuildDate", Common::g_build_date);
fc.AddField(FieldType::App, "BuildName", Common::g_build_name);
}
void AppendCPUInfo(FieldCollection& fc) {
#ifdef ARCHITECTURE_x86_64
fc.AddField(FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
fc.AddField(FieldType::UserSystem, "CPU_BrandString", Common::GetCPUCaps().brand_string);
fc.AddField(FieldType::UserSystem, "CPU_Vendor", CpuVendorToStr(Common::GetCPUCaps().vendor));
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA4", Common::GetCPUCaps().fma4);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE", Common::GetCPUCaps().sse);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE2", Common::GetCPUCaps().sse2);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE3", Common::GetCPUCaps().sse3);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSSE3", Common::GetCPUCaps().ssse3);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE41", Common::GetCPUCaps().sse4_1);
fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2);
#else
fc.AddField(FieldType::UserSystem, "CPU_Model", "Other");
#endif
}
void AppendOSInfo(FieldCollection& fc) {
#ifdef __APPLE__
fc.AddField(FieldType::UserSystem, "OsPlatform", "Apple");
#elif defined(_WIN32)
fc.AddField(FieldType::UserSystem, "OsPlatform", "Windows");
#elif defined(__linux__) || defined(linux) || defined(__linux)
fc.AddField(FieldType::UserSystem, "OsPlatform", "Linux");
#else
fc.AddField(FieldType::UserSystem, "OsPlatform", "Unknown");
#endif
}
} // namespace Telemetry

View File

@@ -180,16 +180,4 @@ struct NullVisitor : public VisitorInterface {
void Complete() override {}
};
/// Appends build-specific information to the given FieldCollection,
/// such as branch name, revision hash, etc.
void AppendBuildInfo(FieldCollection& fc);
/// Appends CPU-specific information to the given FieldCollection,
/// such as instruction set extensions, etc.
void AppendCPUInfo(FieldCollection& fc);
/// Appends OS-specific information to the given FieldCollection,
/// such as platform name, etc.
void AppendOSInfo(FieldCollection& fc);
} // namespace Telemetry

View File

@@ -60,12 +60,12 @@ private:
class Barrier {
public:
explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {}
explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {}
/// Blocks until all "count" threads have called Sync()
void Sync() {
std::unique_lock<std::mutex> lk(mutex);
const std::size_t current_generation = generation;
const size_t current_generation = generation;
if (++waiting == count) {
generation++;
@@ -80,9 +80,9 @@ public:
private:
std::condition_variable condvar;
std::mutex mutex;
const std::size_t count;
std::size_t waiting;
std::size_t generation; // Incremented once each time the barrier is used
const size_t count;
size_t waiting;
size_t generation; // Incremented once each time the barrier is used
};
void SleepCurrentThread(int ms);

View File

@@ -97,7 +97,7 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
Xbyak::util::xmm15,
});
constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
constexpr size_t ABI_SHADOW_SPACE = 0x20;
#else
@@ -147,23 +147,22 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
Xbyak::util::r15,
});
constexpr std::size_t ABI_SHADOW_SPACE = 0;
constexpr size_t ABI_SHADOW_SPACE = 0;
#endif
inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
std::size_t needed_frame_size, s32* out_subtraction,
s32* out_xmm_offset) {
inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size,
s32* out_subtraction, s32* out_xmm_offset) {
int count = (regs & ABI_ALL_GPRS).Count();
rsp_alignment -= count * 8;
std::size_t subtraction = 0;
size_t subtraction = 0;
int xmm_count = (regs & ABI_ALL_XMMS).Count();
if (xmm_count) {
// If we have any XMMs to save, we must align the stack here.
subtraction = rsp_alignment & 0xF;
}
subtraction += 0x10 * xmm_count;
std::size_t xmm_base_subtraction = subtraction;
size_t xmm_base_subtraction = subtraction;
subtraction += needed_frame_size;
subtraction += ABI_SHADOW_SPACE;
// Final alignment.
@@ -174,9 +173,8 @@ inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
*out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
}
inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
std::size_t rsp_alignment,
std::size_t needed_frame_size = 0) {
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
@@ -197,8 +195,7 @@ inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, B
}
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
std::size_t rsp_alignment,
std::size_t needed_frame_size = 0) {
size_t rsp_alignment, size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);

View File

@@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
std::size_t addr = reinterpret_cast<std::size_t>(f);
size_t addr = reinterpret_cast<size_t>(f);
if (IsWithin2G(code, addr)) {
code.call(f);
} else {

View File

@@ -20,8 +20,6 @@ add_library(core STATIC
crypto/key_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
crypto/xts_encryption_layer.h
file_sys/bis_factory.cpp
file_sys/bis_factory.h
file_sys/card_image.cpp
@@ -35,12 +33,8 @@ add_library(core STATIC
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
file_sys/nca_patch.cpp
file_sys/nca_patch.h
file_sys/partition_filesystem.cpp
file_sys/partition_filesystem.h
file_sys/patch_manager.cpp
file_sys/patch_manager.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/registered_cache.cpp
@@ -53,8 +47,6 @@ add_library(core STATIC
file_sys/savedata_factory.h
file_sys/sdmc_factory.cpp
file_sys/sdmc_factory.h
file_sys/submission_package.cpp
file_sys/submission_package.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
@@ -65,8 +57,6 @@ add_library(core STATIC
file_sys/vfs_real.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
@@ -132,8 +122,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/profile_manager.cpp
hle/service/acc/profile_manager.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/applet_ae.cpp
@@ -357,16 +345,12 @@ add_library(core STATIC
loader/linker.h
loader/loader.cpp
loader/loader.h
loader/nax.cpp
loader/nax.h
loader/nca.cpp
loader/nca.h
loader/nro.cpp
loader/nro.h
loader/nso.cpp
loader/nso.h
loader/nsp.cpp
loader/nsp.h
loader/xci.cpp
loader/xci.h
memory.cpp
@@ -388,7 +372,7 @@ add_library(core STATIC
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 lz4_static mbedtls opus unicorn open_source_archives)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn)
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE

View File

@@ -8,9 +8,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/vm_manager.h"
namespace Core {
/// Generic ARMv8 CPU interface
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
public:
virtual ~ARM_Interface() {}
@@ -19,9 +17,9 @@ public:
std::array<u64, 31> cpu_registers;
u64 sp;
u64 pc;
u64 pstate;
std::array<u128, 32> vector_registers;
u64 fpcr;
u64 cpsr;
std::array<u128, 32> fpu_registers;
u64 fpscr;
};
/// Runs the CPU until an event happens
@@ -31,11 +29,11 @@ public:
virtual void Step() = 0;
/// Maps a backing memory region for the CPU
virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
virtual void MapBackingMemory(VAddr address, 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;
virtual void UnmapMemory(VAddr address, size_t size) = 0;
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
@@ -69,50 +67,42 @@ public:
*/
virtual void SetReg(int index, u64 value) = 0;
/**
* Gets the value of a specified vector register.
*
* @param index The index of the vector register.
* @return the value within the vector register.
*/
virtual u128 GetVectorReg(int index) const = 0;
virtual u128 GetExtReg(int index) const = 0;
virtual void SetExtReg(int index, u128 value) = 0;
/**
* Sets a given value into a vector register.
*
* @param index The index of the vector register.
* @param value The new value to place in the register.
* Gets the value of a VFP register
* @param index Register index (0-31)
* @return Returns the value in the register
*/
virtual void SetVectorReg(int index, u128 value) = 0;
virtual u32 GetVFPReg(int index) const = 0;
/**
* Get the current PSTATE register
* @return Returns the value of the PSTATE register
* Sets a VFP register to the given value
* @param index Register index (0-31)
* @param value Value to set register to
*/
virtual u32 GetPSTATE() const = 0;
virtual void SetVFPReg(int index, u32 value) = 0;
/**
* Set the current PSTATE register
* @param pstate Value to set PSTATE to
* Get the current CPSR register
* @return Returns the value of the CPSR register
*/
virtual void SetPSTATE(u32 pstate) = 0;
virtual u32 GetCPSR() const = 0;
/**
* Set the current CPSR register
* @param cpsr Value to set CPSR to
*/
virtual void SetCPSR(u32 cpsr) = 0;
virtual VAddr GetTlsAddress() const = 0;
virtual void SetTlsAddress(VAddr address) = 0;
/**
* Gets the value within the TPIDR_EL0 (read/write software thread ID) register.
*
* @return the value within the register.
*/
virtual u64 GetTPIDR_EL0() const = 0;
/**
* Sets a new value within the TPIDR_EL0 (read/write software thread ID) register.
*
* @param value The new value to place in the register.
*/
virtual void SetTPIDR_EL0(u64 value) = 0;
/**
@@ -127,11 +117,8 @@ public:
*/
virtual void LoadContext(const ThreadContext& ctx) = 0;
/// Clears the exclusive monitor's state.
virtual void ClearExclusiveState() = 0;
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
};
} // namespace Core

View File

@@ -7,17 +7,13 @@
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {
using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
@@ -58,7 +54,7 @@ public:
Memory::Write64(vaddr + 8, value[1]);
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
void InterpreterFallback(u64 pc, size_t num_instructions) override {
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
num_instructions, MemoryReadCode(pc));
@@ -81,7 +77,7 @@ public:
return;
default:
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})",
static_cast<std::size_t>(exception), pc);
static_cast<size_t>(exception), pc);
}
}
@@ -110,7 +106,7 @@ public:
}
ARM_Dynarmic& parent;
std::size_t num_interpreted_instructions = 0;
size_t num_interpreted_instructions = 0;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
};
@@ -138,16 +134,10 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
// Unpredictable instructions
config.define_unpredictable_behaviour = true;
return std::make_unique<Dynarmic::A64::Jit>(config);
}
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();
@@ -157,8 +147,7 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::size_t core_index)
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
ThreadContext ctx;
@@ -169,12 +158,12 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
ARM_Dynarmic::~ARM_Dynarmic() = default;
void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory,
void ARM_Dynarmic::MapBackingMemory(u64 address, 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) {
void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) {
inner_unicorn.UnmapMemory(address, size);
}
@@ -194,20 +183,29 @@ void ARM_Dynarmic::SetReg(int index, u64 value) {
jit->SetRegister(index, value);
}
u128 ARM_Dynarmic::GetVectorReg(int index) const {
u128 ARM_Dynarmic::GetExtReg(int index) const {
return jit->GetVector(index);
}
void ARM_Dynarmic::SetVectorReg(int index, u128 value) {
void ARM_Dynarmic::SetExtReg(int index, u128 value) {
jit->SetVector(index, value);
}
u32 ARM_Dynarmic::GetPSTATE() const {
u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const {
UNIMPLEMENTED();
return {};
}
void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) {
UNIMPLEMENTED();
}
u32 ARM_Dynarmic::GetCPSR() const {
return jit->GetPstate();
}
void ARM_Dynarmic::SetPSTATE(u32 pstate) {
jit->SetPstate(pstate);
void ARM_Dynarmic::SetCPSR(u32 cpsr) {
jit->SetPstate(cpsr);
}
u64 ARM_Dynarmic::GetTlsAddress() const {
@@ -230,18 +228,18 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
ctx.pstate = jit->GetPstate();
ctx.vector_registers = jit->GetVectors();
ctx.fpcr = jit->GetFpcr();
ctx.cpsr = jit->GetPstate();
ctx.fpu_registers = jit->GetVectors();
ctx.fpscr = jit->GetFpcr();
}
void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);
jit->SetPstate(static_cast<u32>(ctx.pstate));
jit->SetVectors(ctx.vector_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpcr));
jit->SetPstate(static_cast<u32>(ctx.cpsr));
jit->SetVectors(ctx.fpu_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
}
void ARM_Dynarmic::PrepareReschedule() {
@@ -261,10 +259,10 @@ void ARM_Dynarmic::PageTableChanged() {
current_page_table = Memory::GetCurrentPageTable();
}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) {
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) {
// Size doesn't actually matter.
monitor.Mark(core_index, addr, 16);
}
@@ -273,31 +271,29 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
monitor.Clear();
}
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
[&] { Memory::Write8(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
[&] { Memory::Write16(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
[&] { Memory::Write32(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
[&] { Memory::Write64(vaddr, value); });
}
bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
Memory::Write64(vaddr + 0, value[0]);
Memory::Write64(vaddr + 8, value[1]);
Memory::Write64(vaddr, value[0]);
Memory::Write64(vaddr, value[1]);
});
}
} // namespace Core

View File

@@ -12,29 +12,29 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index);
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index);
~ARM_Dynarmic();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
void MapBackingMemory(VAddr address, size_t size, u8* memory,
Kernel::VMAPermission perms) override;
void UnmapMemory(u64 address, std::size_t size) override;
void UnmapMemory(u64 address, size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
u128 GetExtReg(int index) const override;
void SetExtReg(int index, u128 value) override;
u32 GetVFPReg(int index) const override;
void SetVFPReg(int index, u32 value) override;
u32 GetCPSR() const override;
void Run() override;
void Step() override;
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -57,7 +57,7 @@ private:
std::unique_ptr<Dynarmic::A64::Jit> jit;
ARM_Unicorn inner_unicorn;
std::size_t core_index;
size_t core_index;
std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
Memory::PageTable* current_page_table = nullptr;
@@ -65,21 +65,19 @@ private:
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public:
explicit DynarmicExclusiveMonitor(std::size_t core_count);
explicit DynarmicExclusiveMonitor(size_t core_count);
~DynarmicExclusiveMonitor();
void SetExclusive(std::size_t core_index, VAddr addr) override;
void SetExclusive(size_t core_index, VAddr addr) override;
void ClearExclusive() override;
bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override;
bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override;
bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) override;
bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) override;
bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) override;
bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) override;
bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) override;
private:
friend class ARM_Dynarmic;
Dynarmic::A64::ExclusiveMonitor monitor;
};
} // namespace Core

View File

@@ -4,8 +4,4 @@
#include "core/arm/exclusive_monitor.h"
namespace Core {
ExclusiveMonitor::~ExclusiveMonitor() = default;
} // namespace Core

View File

@@ -6,20 +6,16 @@
#include "common/common_types.h"
namespace Core {
class ExclusiveMonitor {
public:
virtual ~ExclusiveMonitor();
virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0;
virtual void SetExclusive(size_t core_index, VAddr addr) = 0;
virtual void ClearExclusive() = 0;
virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0;
virtual bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) = 0;
virtual bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0;
virtual bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) = 0;
virtual bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) = 0;
virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0;
};
} // namespace Core

View File

@@ -11,8 +11,6 @@
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
namespace Core {
// Load Unicorn DLL once on Windows using RAII
#ifdef _MSC_VER
#include <unicorn_dynload.h>
@@ -90,12 +88,12 @@ ARM_Unicorn::~ARM_Unicorn() {
CHECKED(uc_close(uc));
}
void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory,
void ARM_Unicorn::MapBackingMemory(VAddr address, 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) {
void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) {
CHECKED(uc_mem_unmap(uc, address, size));
}
@@ -131,24 +129,33 @@ void ARM_Unicorn::SetReg(int regn, u64 val) {
CHECKED(uc_reg_write(uc, treg, &val));
}
u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
u128 ARM_Unicorn::GetExtReg(int /*index*/) const {
UNIMPLEMENTED();
static constexpr u128 res{};
return res;
}
void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) {
UNIMPLEMENTED();
}
u32 ARM_Unicorn::GetPSTATE() const {
u32 ARM_Unicorn::GetVFPReg(int /*index*/) const {
UNIMPLEMENTED();
return {};
}
void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) {
UNIMPLEMENTED();
}
u32 ARM_Unicorn::GetCPSR() const {
u64 nzcv{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
return static_cast<u32>(nzcv);
}
void ARM_Unicorn::SetPSTATE(u32 pstate) {
u64 nzcv = pstate;
void ARM_Unicorn::SetCPSR(u32 cpsr) {
u64 nzcv = cpsr;
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
}
@@ -184,10 +191,10 @@ void ARM_Unicorn::Step() {
ExecuteInstructions(1);
}
MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
MICROPROFILE_SCOPE(ARM_Jit);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
CoreTiming::AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
@@ -204,13 +211,13 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
}
void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
int uregs[32];
void* tregs[32];
CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp));
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc));
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.cpsr));
for (auto i = 0; i < 29; ++i) {
uregs[i] = UC_ARM64_REG_X0 + i;
@@ -225,19 +232,19 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
for (int i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = &ctx.vector_registers[i];
tregs[i] = &ctx.fpu_registers[i];
}
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
}
void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
void ARM_Unicorn::LoadContext(const ARM_Interface::ThreadContext& ctx) {
int uregs[32];
void* tregs[32];
CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp));
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc));
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.cpsr));
for (int i = 0; i < 29; ++i) {
uregs[i] = UC_ARM64_REG_X0 + i;
@@ -252,7 +259,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
for (auto i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = (void*)&ctx.vector_registers[i];
tregs[i] = (void*)&ctx.fpu_registers[i];
}
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
@@ -270,5 +277,3 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt = bkpt;
last_bkpt_hit = true;
}
} // namespace Core

View File

@@ -9,23 +9,23 @@
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
namespace Core {
class ARM_Unicorn final : public ARM_Interface {
public:
ARM_Unicorn();
~ARM_Unicorn();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
void MapBackingMemory(VAddr address, size_t size, u8* memory,
Kernel::VMAPermission perms) override;
void UnmapMemory(VAddr address, std::size_t size) override;
void UnmapMemory(VAddr address, size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
u128 GetExtReg(int index) const override;
void SetExtReg(int index, u128 value) override;
u32 GetVFPReg(int index) const override;
void SetVFPReg(int index, u32 value) override;
u32 GetCPSR() const override;
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -46,5 +46,3 @@ private:
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit;
};
} // namespace Core

View File

@@ -2,35 +2,24 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <map>
#include <memory>
#include <thread>
#include <utility>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
#include "file_sys/vfs_concat.h"
#include "file_sys/vfs_real.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -38,9 +27,71 @@ namespace Core {
/*static*/ System System::s_instance;
namespace {
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
System::System() = default;
System::~System() = default;
/// Runs a CPU core while the system is powered on
static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
}
Cpu& System::CurrentCpuCore() {
// If multicore is enabled, use host thread to figure out the current CPU core
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core];
}
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return ResultStatus::Success;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files.
std::string dir_name;
std::string filename;
@@ -70,405 +121,168 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
/// Runs a CPU core while the system is powered on
void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
System::ResultStatus System::Load(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;
}
}
} // Anonymous namespace
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
struct System::Impl {
Cpu& CurrentCpuCore() {
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core];
}
ResultStatus RunLoop(bool tight_loop) {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return ResultStatus::Success;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
ResultStatus Init(Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
kernel.Initialize();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main"));
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// 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()] = cpu_cores[0];
if (Settings::values.use_multi_core) {
for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
}
}
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
ResultStatus Load(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<boost::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));
if (system_mode.second != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second));
if (system_mode.second != Loader::ResultStatus::Success)
return ResultStatus::ErrorSystemMode;
}
}
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
Shutdown();
return init_result;
}
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
System::Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
System::Shutdown();
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();
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
status = ResultStatus::Success;
return status;
}
void Shutdown() {
// Log last frame performance stats
auto perf_results = GetAndResetPerfStats();
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
perf_results.game_fps);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
// Close all CPU/threading state
cpu_barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : cpu_core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_barrier.reset();
// Shutdown kernel and core timing
kernel.Shutdown();
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
return app_loader->ReadTitle(out);
}
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
PerfStatsResults GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
}
Kernel::KernelCore kernel;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
std::size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
};
System::System() : impl{std::make_unique<Impl>()} {}
System::~System() = default;
Cpu& System::CurrentCpuCore() {
return impl->CurrentCpuCore();
}
System::ResultStatus System::RunLoop(bool tight_loop) {
return impl->RunLoop(tight_loop);
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
void System::InvalidateCpuInstructionCaches() {
for (auto& cpu : impl->cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
return impl->Load(emu_window, filepath);
}
bool System::IsPoweredOn() const {
return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
status = ResultStatus::Success;
return status;
}
void System::PrepareReschedule() {
CurrentCpuCore().PrepareReschedule();
}
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
PerfStats::Results System::GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
}
Core::TelemetrySession& System::TelemetrySession() const {
return *impl->telemetry_session;
}
ARM_Interface& System::CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
Kernel::Scheduler& System::CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
}
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) {
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return impl->cpu_cores[core_index]->Scheduler();
return cpu_cores[core_index]->Scheduler();
}
Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
return impl->kernel.CurrentProcess();
}
const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const {
return impl->kernel.CurrentProcess();
}
ARM_Interface& System::ArmInterface(std::size_t core_index) {
ARM_Interface& System::ArmInterface(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return impl->cpu_cores[core_index]->ArmInterface();
return cpu_cores[core_index]->ArmInterface();
}
Cpu& System::CpuCore(std::size_t core_index) {
Cpu& System::CpuCore(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return *impl->cpu_cores[core_index];
}
ExclusiveMonitor& System::Monitor() {
return *impl->cpu_exclusive_monitor;
}
Tegra::GPU& System::GPU() {
return *impl->gpu_core;
}
const Tegra::GPU& System::GPU() const {
return *impl->gpu_core;
}
VideoCore::RendererBase& System::Renderer() {
return *impl->renderer;
}
const VideoCore::RendererBase& System::Renderer() const {
return *impl->renderer;
}
Kernel::KernelCore& System::Kernel() {
return impl->kernel;
}
const Kernel::KernelCore& System::Kernel() const {
return impl->kernel;
}
Core::PerfStats& System::GetPerfStats() {
return impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
return impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
return impl->frame_limiter;
}
const Core::FrameLimiter& System::FrameLimiter() const {
return impl->frame_limiter;
}
Loader::ResultStatus System::GetGameName(std::string& out) const {
return impl->GetGameName(out);
}
void System::SetStatus(ResultStatus new_status, const char* details) {
impl->SetStatus(new_status, details);
}
const std::string& System::GetStatusDetails() const {
return impl->status_details;
}
Loader::AppLoader& System::GetAppLoader() const {
return *impl->app_loader;
}
void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
impl->debug_context = std::move(context);
}
Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
return *cpu_cores[core_index];
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(emu_window);
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Kernel::Init();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// 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()] = cpu_cores[0];
if (Settings::values.use_multi_core) {
for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
}
}
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
void System::Shutdown() {
impl->Shutdown();
// Log last frame performance stats
auto perf_results = GetAndResetPerfStats();
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
perf_results.game_fps);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
// Close all CPU/threading state
cpu_barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : cpu_core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_barrier.reset();
// Close core timing
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
Service::SM::ServiceManager& System::ServiceManager() {
return *impl->service_manager;
return *service_manager;
}
const Service::SM::ServiceManager& System::ServiceManager() const {
return *impl->service_manager;
return *service_manager;
}
} // namespace Core

View File

@@ -4,64 +4,42 @@
#pragma once
#include <cstddef>
#include <array>
#include <memory>
#include <string>
#include <thread>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "file_sys/vfs_real.h"
#include "hle/service/filesystem/filesystem.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
class ARM_Interface;
namespace Core::Frontend {
class EmuWindow;
} // namespace Core::Frontend
namespace FileSys {
class VfsFilesystem;
} // namespace FileSys
namespace Kernel {
class KernelCore;
class Process;
class Scheduler;
} // namespace Kernel
namespace Loader {
class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
}
namespace Service::SM {
class ServiceManager;
} // namespace Service::SM
namespace Tegra {
class DebugContext;
class GPU;
} // namespace Tegra
}
namespace VideoCore {
class RendererBase;
} // namespace VideoCore
}
namespace Core {
class ARM_Interface;
class Cpu;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
class TelemetrySession;
struct PerfStatsResults;
class System {
public:
System(const System&) = delete;
System& operator=(const System&) = delete;
System(System&&) = delete;
System& operator=(System&&) = delete;
~System();
/**
@@ -108,7 +86,11 @@ public:
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
*/
void InvalidateCpuInstructionCaches();
void InvalidateCpuInstructionCaches() {
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
/// Shutdown the emulated system.
void Shutdown();
@@ -127,96 +109,114 @@ public:
* application).
* @returns True if the emulated system is powered on, otherwise false.
*/
bool IsPoweredOn() const;
bool IsPoweredOn() const {
return cpu_barrier && cpu_barrier->IsAlive();
}
/**
* Returns a reference to the telemetry session for this emulation session.
* @returns Reference to the telemetry session.
*/
Core::TelemetrySession& TelemetrySession() const;
Core::TelemetrySession& TelemetrySession() const {
return *telemetry_session;
}
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
/// Gets and resets core performance statistics
PerfStatsResults GetAndResetPerfStats();
PerfStats::Results GetAndResetPerfStats();
/// Gets an ARM interface to the CPU core that is currently running
ARM_Interface& CurrentArmInterface();
ARM_Interface& CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
/// Gets the index of the currently running CPU core
std::size_t CurrentCoreIndex();
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler();
size_t CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
/// Gets an ARM interface to the CPU core with the specified index
ARM_Interface& ArmInterface(std::size_t core_index);
ARM_Interface& ArmInterface(size_t core_index);
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(std::size_t core_index);
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor();
Cpu& CpuCore(size_t core_index);
/// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU();
Tegra::GPU& GPU() {
return *gpu_core;
}
/// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const;
const Tegra::GPU& GPU() const {
return *gpu_core;
}
/// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer();
VideoCore::RendererBase& Renderer() {
return *renderer;
}
/// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const;
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
}
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor() {
return *cpu_exclusive_monitor;
}
/// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index);
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
/// Provides a reference to the current process
Kernel::SharedPtr<Kernel::Process>& CurrentProcess();
/// Gets the current process
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
return current_process;
}
/// Provides a constant reference to the current process.
const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const;
PerfStats perf_stats;
FrameLimiter frame_limiter;
/// Provides a reference to the kernel instance.
Kernel::KernelCore& Kernel();
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
/// Provides a constant reference to the kernel instance.
const Kernel::KernelCore& Kernel() const;
const std::string& GetStatusDetails() const {
return status_details;
}
/// Provides a reference to the internal PerfStats instance.
Core::PerfStats& GetPerfStats();
/// Provides a constant reference to the internal PerfStats instance.
const Core::PerfStats& GetPerfStats() const;
/// Provides a reference to the frame limiter;
Core::FrameLimiter& FrameLimiter();
/// Provides a constant referent to the frame limiter
const Core::FrameLimiter& FrameLimiter() const;
/// Gets the name of the current game
Loader::ResultStatus GetGameName(std::string& out) const;
void SetStatus(ResultStatus new_status, const char* details);
const std::string& GetStatusDetails() const;
Loader::AppLoader& GetAppLoader() const;
Loader::AppLoader& GetAppLoader() const {
return *app_loader;
}
Service::SM::ServiceManager& ServiceManager();
const Service::SM::ServiceManager& ServiceManager() const;
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
debug_context = std::move(context);
}
Tegra::DebugContext* GetGPUDebugContext() const;
std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const {
return debug_context;
}
void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
void SetFilesystem(FileSys::VirtualFilesystem vfs) {
virtual_filesystem = std::move(vfs);
}
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
FileSys::VirtualFilesystem GetFilesystem() const {
return virtual_filesystem;
}
private:
System();
@@ -232,10 +232,33 @@ private:
*/
ResultStatus Init(Frontend::EmuWindow& emu_window);
struct Impl;
std::unique_ptr<Impl> impl;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
static System s_instance;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
};
inline ARM_Interface& CurrentArmInterface() {

View File

@@ -9,7 +9,6 @@
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h"
#endif
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
@@ -50,7 +49,7 @@ bool CpuBarrier::Rendezvous() {
}
Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index)
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
: cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
@@ -67,9 +66,7 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
}
Cpu::~Cpu() = default;
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
return std::make_shared<DynarmicExclusiveMonitor>(num_cores);

View File

@@ -6,10 +6,13 @@
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <memory>
#include <mutex>
#include <string>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
class ARM_Interface;
namespace Kernel {
class Scheduler;
@@ -17,9 +20,6 @@ class Scheduler;
namespace Core {
class ARM_Interface;
class ExclusiveMonitor;
constexpr unsigned NUM_CPU_CORES{4};
class CpuBarrier {
@@ -42,8 +42,7 @@ private:
class Cpu {
public:
Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
~Cpu();
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
void RunLoop(bool tight_loop = true);
@@ -67,11 +66,11 @@ public:
return core_index == 0;
}
std::size_t CoreIndex() const {
size_t CoreIndex() const {
return core_index;
}
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores);
private:
void Reschedule();
@@ -81,7 +80,7 @@ private:
std::shared_ptr<Kernel::Scheduler> scheduler;
std::atomic<bool> reschedule_pending = false;
std::size_t core_index;
size_t core_index;
};
} // namespace Core

View File

@@ -10,9 +10,9 @@
namespace Core::Crypto {
namespace {
std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) {
std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (std::size_t i = 0xF; i <= 0xF; --i) {
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
@@ -20,14 +20,11 @@ std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) {
}
} // Anonymous namespace
static_assert(static_cast<std::size_t>(Mode::CTR) ==
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_CTR),
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
"CTR has incorrect value.");
static_assert(static_cast<std::size_t>(Mode::ECB) ==
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_ECB),
static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB),
"ECB has incorrect value.");
static_assert(static_cast<std::size_t>(Mode::XTS) ==
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_XTS),
static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS),
"XTS has incorrect value.");
// Structure to hide mbedtls types from header file
@@ -36,7 +33,7 @@ struct CipherContext {
mbedtls_cipher_context_t decryption_context;
};
template <typename Key, std::size_t KeySize>
template <typename Key, size_t KeySize>
Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
: ctx(std::make_unique<CipherContext>()) {
mbedtls_cipher_init(&ctx->encryption_context);
@@ -57,26 +54,26 @@ Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
//"Failed to set key on mbedtls ciphers.");
}
template <typename Key, std::size_t KeySize>
template <typename Key, size_t KeySize>
AESCipher<Key, KeySize>::~AESCipher() {
mbedtls_cipher_free(&ctx->encryption_context);
mbedtls_cipher_free(&ctx->decryption_context);
}
template <typename Key, std::size_t KeySize>
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
"Failed to set IV on mbedtls ciphers.");
}
template <typename Key, std::size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
mbedtls_cipher_reset(context);
std::size_t written = 0;
size_t written = 0;
if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
mbedtls_cipher_update(context, src, size, dest, &written);
if (written != size) {
@@ -85,25 +82,11 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
}
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
if (size < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src, size);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest, block.data(), size);
return;
}
for (std::size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<std::size_t>(block_size, size - offset);
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) {
if (length < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src + offset, length);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest + offset, block.data(), length);
return;
}
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
@@ -113,12 +96,15 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
mbedtls_cipher_finish(context, nullptr, nullptr);
}
template <typename Key, std::size_t KeySize>
void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8* dest,
std::size_t sector_id, std::size_t sector_size, Op op) {
ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size.");
template <typename Key, size_t KeySize>
void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id,
size_t sector_size, Op op) {
if (size % sector_size > 0) {
LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size.");
return;
}
for (std::size_t i = 0; i < size; i += sector_size) {
for (size_t i = 0; i < size; i += sector_size) {
SetIV(CalculateNintendoTweak(sector_id++));
Transcode<u8, u8>(src + i, sector_size, dest + i, op);
}
@@ -126,4 +112,4 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto
} // namespace Core::Crypto

View File

@@ -25,7 +25,7 @@ enum class Op {
Decrypt,
};
template <typename Key, std::size_t KeySize = sizeof(Key)>
template <typename Key, size_t KeySize = sizeof(Key)>
class AESCipher {
static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8.");
static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
@@ -38,25 +38,25 @@ public:
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const {
void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"Transcode source and destination types must be trivially copyable.");
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
}
void Transcode(const u8* src, std::size_t size, u8* dest, Op op) const;
void Transcode(const u8* src, size_t size, u8* dest, Op op) const;
template <typename Source, typename Dest>
void XTSTranscode(const Source* src, std::size_t size, Dest* dest, std::size_t sector_id,
std::size_t sector_size, Op op) {
void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
size_t sector_size, Op op) {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"XTSTranscode source and destination types must be trivially copyable.");
XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
sector_size, op);
}
void XTSTranscode(const u8* src, std::size_t size, u8* dest, std::size_t sector_id,
std::size_t sector_size, Op op);
void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size,
Op op);
private:
std::unique_ptr<CipherContext> ctx;

View File

@@ -8,12 +8,11 @@
namespace Core::Crypto {
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_,
std::size_t base_offset)
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset)
: EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
iv(16, 0) {}
std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length == 0)
return 0;
@@ -21,7 +20,9 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
if (sector_offset == 0) {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt);
if (raw.size() != length)
return Read(data, raw.size(), offset);
cipher.Transcode(raw.data(), length, data, Op::Decrypt);
return length;
}
@@ -29,11 +30,11 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset);
UpdateIV(base_offset + offset - sector_offset);
cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt);
std::size_t read = 0x10 - sector_offset;
size_t read = 0x10 - sector_offset;
if (length + sector_offset < 0x10) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return std::min<u64>(length, read);
return read;
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);
@@ -44,9 +45,9 @@ void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
iv.assign(iv_.cbegin(), iv_.cbegin() + length);
}
void CTREncryptionLayer::UpdateIV(std::size_t offset) const {
void CTREncryptionLayer::UpdateIV(size_t offset) const {
offset >>= 4;
for (std::size_t i = 0; i < 8; ++i) {
for (size_t i = 0; i < 8; ++i) {
iv[16 - i - 1] = offset & 0xFF;
offset >>= 8;
}

View File

@@ -14,20 +14,20 @@ namespace Core::Crypto {
// Sits on top of a VirtualFile and provides CTR-mode AES decription.
class CTREncryptionLayer : public EncryptionLayer {
public:
CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset);
CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset);
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
size_t Read(u8* data, size_t length, size_t offset) const override;
void SetIV(const std::vector<u8>& iv);
private:
std::size_t base_offset;
size_t base_offset;
// Must be mutable as operations modify cipher contexts.
mutable AESCipher<Key128> cipher;
mutable std::vector<u8> iv;
void UpdateIV(std::size_t offset) const;
void UpdateIV(size_t offset) const;
};
} // namespace Core::Crypto

View File

@@ -12,11 +12,11 @@ std::string EncryptionLayer::GetName() const {
return base->GetName();
}
std::size_t EncryptionLayer::GetSize() const {
size_t EncryptionLayer::GetSize() const {
return base->GetSize();
}
bool EncryptionLayer::Resize(std::size_t new_size) {
bool EncryptionLayer::Resize(size_t new_size) {
return false;
}
@@ -32,7 +32,7 @@ bool EncryptionLayer::IsReadable() const {
return true;
}
std::size_t EncryptionLayer::Write(const u8* data, std::size_t length, std::size_t offset) {
size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) {
return 0;
}

View File

@@ -15,15 +15,15 @@ class EncryptionLayer : public FileSys::VfsFile {
public:
explicit EncryptionLayer(FileSys::VirtualFile base);
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override = 0;
size_t Read(u8* data, size_t length, size_t offset) const override = 0;
std::string GetName() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
protected:

View File

@@ -8,119 +8,15 @@
#include <locale>
#include <sstream>
#include <string_view>
#include <tuple>
#include <vector>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace Core::Crypto {
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
Key128 out{};
AESCipher<Key128> cipher1(master, Mode::ECB);
cipher1.Transcode(kek_seed.data(), kek_seed.size(), out.data(), Op::Decrypt);
AESCipher<Key128> cipher2(out, Mode::ECB);
cipher2.Transcode(source.data(), source.size(), out.data(), Op::Decrypt);
if (key_seed != Key128{}) {
AESCipher<Key128> cipher3(out, Mode::ECB);
cipher3.Transcode(key_seed.data(), key_seed.size(), out.data(), Op::Decrypt);
}
return out;
}
boost::optional<Key128> DeriveSDSeed() {
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000043",
"rb+");
if (!save_43.IsOpen())
return boost::none;
const FileUtil::IOFile sd_private(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
if (!sd_private.IsOpen())
return boost::none;
sd_private.Seek(0, SEEK_SET);
std::array<u8, 0x10> private_seed{};
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10)
return boost::none;
std::array<u8, 0x10> buffer{};
std::size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
save_43.Seek(offset, SEEK_SET);
save_43.ReadBytes(buffer.data(), buffer.size());
if (buffer == private_seed)
break;
}
if (offset + 0x10 >= save_43.GetSize())
return boost::none;
Key128 seed{};
save_43.Seek(offset + 0x10, SEEK_SET);
save_43.ReadBytes(seed.data(), seed.size());
return seed;
}
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) {
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)))
return Loader::ResultStatus::ErrorMissingSDKEKSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)))
return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
const auto sd_kek_source =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK));
const auto aes_kek_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration));
const auto aes_key_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
const auto master_00 = keys.GetKey(S128KeyType::Master);
const auto sd_kek =
GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
if (!keys.HasKey(S128KeyType::SDSeed))
return Loader::ResultStatus::ErrorMissingSDSeed;
const auto sd_seed = keys.GetKey(S128KeyType::SDSeed);
if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)))
return Loader::ResultStatus::ErrorMissingSDSaveKeySource;
if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA)))
return Loader::ResultStatus::ErrorMissingSDNCAKeySource;
std::array<Key256, 2> sd_key_sources{
keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)),
keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA)),
};
// Combine sources and seed
for (auto& source : sd_key_sources) {
for (std::size_t i = 0; i < source.size(); ++i)
source[i] ^= sd_seed[i & 0xF];
}
AESCipher<Key128> cipher(sd_kek, Mode::ECB);
// The transform manipulates sd_keys as part of the Transcode, so the return/output is
// unnecessary. This does not alter sd_keys_sources.
std::transform(sd_key_sources.begin(), sd_key_sources.end(), sd_keys.begin(),
sd_key_sources.begin(), [&cipher](const Key256& source, Key256& out) {
cipher.Transcode(source.data(), source.size(), out.data(), Op::Decrypt);
return source; ///< Return unaltered source to satisfy output requirement.
});
return Loader::ResultStatus::Success;
}
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -128,15 +24,12 @@ KeyManager::KeyManager() {
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "dev.keys_autogenerated", false);
} else {
dev_mode = false;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "prod.keys_autogenerated", false);
}
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -159,21 +52,21 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
if (is_title_keys) {
auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
auto rights_id_raw = HexStringToArray<16>(out[0]);
u128 rights_id{};
std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
Key128 key = Common::HexStringToArray<16>(out[1]);
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
Key128 key = HexStringToArray<16>(out[1]);
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
} else {
std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
if (s128_file_id.find(out[0]) != s128_file_id.end()) {
const auto index = s128_file_id.at(out[0]);
Key128 key = Common::HexStringToArray<16>(out[1]);
s128_keys[{index.type, index.field1, index.field2}] = key;
Key128 key = HexStringToArray<16>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
} else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
const auto index = s256_file_id.at(out[0]);
Key256 key = Common::HexStringToArray<32>(out[1]);
s256_keys[{index.type, index.field1, index.field2}] = key;
Key256 key = HexStringToArray<32>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
}
}
}
@@ -207,60 +100,11 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.at({id, field1, field2});
}
template <std::size_t Size>
void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
const std::array<u8, Size>& key) {
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
std::string filename = "title.keys_autogenerated";
if (!title_key)
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
if (!file.is_open())
return;
if (add_info_text) {
file
<< "# This file is autogenerated by Yuzu\n"
<< "# It serves to store keys that were automatically generated from the normal keys\n"
<< "# If you are experiencing issues involving keys, it may help to delete this file\n";
}
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key);
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
return;
if (id == S128KeyType::Titlekey) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
}
const auto iter2 = std::find_if(
s128_file_id.begin(), s128_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter2 != s128_file_id.end())
WriteKeyToFile(false, iter2->first, key);
s128_keys[{id, field1, field2}] = key;
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
return;
const auto iter = std::find_if(
s256_file_id.begin(), s256_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter != s256_file_id.end())
WriteKeyToFile(false, iter->first, key);
s256_keys[{id, field1, field2}] = key;
}
@@ -281,16 +125,7 @@ bool KeyManager::KeyFileExists(bool title) {
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
}
void KeyManager::DeriveSDSeedLazy() {
if (HasKey(S128KeyType::SDSeed))
return;
const auto res = DeriveSDSeed();
if (res != boost::none)
SetKey(S128KeyType::SDSeed, res.get());
}
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"master_key_00", {S128KeyType::Master, 0, 0}},
{"master_key_01", {S128KeyType::Master, 1, 0}},
{"master_key_02", {S128KeyType::Master, 2, 0}},
@@ -332,17 +167,11 @@ const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager:
{"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
{"aes_kek_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}},
{"aes_key_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
{"sd_seed", {S128KeyType::SDSeed, 0, 0}},
};
const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
{"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
{"sd_card_save_key", {S256KeyType::SDSave, 0, 0}},
{"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}},
};
} // namespace Core::Crypto

View File

@@ -6,19 +6,14 @@
#include <array>
#include <string>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
namespace Loader {
enum class ResultStatus : u16;
}
namespace Core::Crypto {
constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
@@ -27,8 +22,9 @@ static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
enum class S256KeyType : u64 {
Header, //
SDKeySource, // f1=SDKeyType
Header, //
SDSave, //
SDNCA, //
};
enum class S128KeyType : u64 {
@@ -40,7 +36,6 @@ enum class S128KeyType : u64 {
KeyArea, // f1=crypto revision f2=type {app, ocean, system}
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
Source, // f1=source type, f2= sub id
};
enum class KeyAreaKeyType : u8 {
@@ -49,17 +44,6 @@ enum class KeyAreaKeyType : u8 {
System,
};
enum class SourceKeyType : u8 {
SDKEK,
AESKEKGeneration,
AESKeyGeneration,
};
enum class SDKeyType : u8 {
Save,
NCA,
};
template <typename KeyType>
struct KeyIndex {
KeyType type;
@@ -75,12 +59,34 @@ struct KeyIndex {
}
};
// boost flat_map requires operator< for O(log(n)) lookups.
// The following two (== and hash) are so KeyIndex can be a key in unordered_map
template <typename KeyType>
bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return std::tie(lhs.type, lhs.field1, lhs.field2) < std::tie(rhs.type, rhs.field1, rhs.field2);
bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2);
}
template <typename KeyType>
bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return !operator==(lhs, rhs);
}
} // namespace Core::Crypto
namespace std {
template <typename KeyType>
struct hash<Core::Crypto::KeyIndex<KeyType>> {
size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const {
using std::hash;
return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^
(hash<u64>()(k.field2) << 1);
}
};
} // namespace std
namespace Core::Crypto {
class KeyManager {
public:
KeyManager();
@@ -96,27 +102,16 @@ public:
static bool KeyFileExists(bool title);
// Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system save
// 8*43 and the private file to exist.
void DeriveSDSeedLazy();
private:
boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys;
boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys;
std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
template <std::size_t Size>
void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);
static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
boost::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys);
} // namespace Core::Crypto

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