Compare commits
176 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08819ec70a | ||
|
|
6a0612f2bf | ||
|
|
b33ce787b7 | ||
|
|
d85130d7be | ||
|
|
c9942fe46e | ||
|
|
bf957d5345 | ||
|
|
c768535463 | ||
|
|
e9abbcae85 | ||
|
|
bd88d4108f | ||
|
|
0284cbe7ec | ||
|
|
6415f81bb8 | ||
|
|
9155c8daeb | ||
|
|
733c47623b | ||
|
|
c2cf784376 | ||
|
|
c7a4bf5074 | ||
|
|
de9604d63e | ||
|
|
1a2d33eeb4 | ||
|
|
a8b1c7763b | ||
|
|
717889e93c | ||
|
|
528e5cee67 | ||
|
|
f85ab0a123 | ||
|
|
ead2a4eeb4 | ||
|
|
b51e7e0288 | ||
|
|
71b48cb00f | ||
|
|
b6867602ca | ||
|
|
567a5524b9 | ||
|
|
9a8dbba1e5 | ||
|
|
fafc80d72e | ||
|
|
4a84986bc3 | ||
|
|
e4bb759c4b | ||
|
|
e2ac8fb36d | ||
|
|
aac77bbd18 | ||
|
|
f7308a6c25 | ||
|
|
2fd124bc93 | ||
|
|
1adbcd54fe | ||
|
|
31e52113b3 | ||
|
|
55a4756766 | ||
|
|
a94b623dfb | ||
|
|
2c9c0d70a3 | ||
|
|
2bfb9fd0e6 | ||
|
|
692e7cee4f | ||
|
|
27fe8159c5 | ||
|
|
fc46183e03 | ||
|
|
59beb540ae | ||
|
|
e6367ab955 | ||
|
|
3476ba2aee | ||
|
|
e561afdcd5 | ||
|
|
e7af0f69da | ||
|
|
076add4ccd | ||
|
|
3be048e50a | ||
|
|
e833301e4c | ||
|
|
08af788a57 | ||
|
|
240e756962 | ||
|
|
9dbe9d4291 | ||
|
|
13930f0c33 | ||
|
|
51b5619079 | ||
|
|
2845348608 | ||
|
|
ba480ea2fb | ||
|
|
daee15b058 | ||
|
|
0019a36b41 | ||
|
|
c878a819d7 | ||
|
|
54ddb37b3c | ||
|
|
3bd8563f2d | ||
|
|
63c2e32e20 | ||
|
|
f8e994354f | ||
|
|
fc669a97d4 | ||
|
|
b5218d9986 | ||
|
|
da64da367b | ||
|
|
4f8756edd0 | ||
|
|
accd1f17e4 | ||
|
|
ae128f0375 | ||
|
|
496c67fd73 | ||
|
|
7bd2faad9a | ||
|
|
df5a44a40b | ||
|
|
fb65076b0f | ||
|
|
9969a5db1e | ||
|
|
3ef134a092 | ||
|
|
50a5d09d32 | ||
|
|
0258b444ef | ||
|
|
2ea45fe75b | ||
|
|
a0e51d8b98 | ||
|
|
8e7497d5bb | ||
|
|
2ec9fbc2d4 | ||
|
|
a42376dfad | ||
|
|
ce97d8ef6c | ||
|
|
60899b80f0 | ||
|
|
938aa5779c | ||
|
|
926dd41587 | ||
|
|
49c4fe1f2f | ||
|
|
4a43fb7e1d | ||
|
|
d9e21eebe8 | ||
|
|
cc50857460 | ||
|
|
bb5eb4f20a | ||
|
|
957ddab679 | ||
|
|
a99d9db32f | ||
|
|
79217f9870 | ||
|
|
0821a210c4 | ||
|
|
44fac34697 | ||
|
|
9f2bcdbb76 | ||
|
|
fbe462099b | ||
|
|
3c5c292592 | ||
|
|
9b3bc0b282 | ||
|
|
04d723baf9 | ||
|
|
e89c22c147 | ||
|
|
a9b953e6d4 | ||
|
|
3a2567c97c | ||
|
|
475222a496 | ||
|
|
3ee4fa557f | ||
|
|
c245150439 | ||
|
|
89825766ee | ||
|
|
522a11a11f | ||
|
|
4a9acc87f9 | ||
|
|
7bb226f22d | ||
|
|
0e61e8362f | ||
|
|
7fe10dea3e | ||
|
|
c243bc09d4 | ||
|
|
bad035e9a3 | ||
|
|
c061e27155 | ||
|
|
429217248f | ||
|
|
325c259fc5 | ||
|
|
cdddd71d08 | ||
|
|
3e973bc4c6 | ||
|
|
5c676dc884 | ||
|
|
3f0922715a | ||
|
|
2b48cfd44b | ||
|
|
e926757c8f | ||
|
|
1470b85af9 | ||
|
|
2f0ff4d25b | ||
|
|
143525dcb9 | ||
|
|
46ba1bc40f | ||
|
|
6ac955a0b4 | ||
|
|
4c3bd33be2 | ||
|
|
3bac3051fc | ||
|
|
804115b2a4 | ||
|
|
d6e8e16a66 | ||
|
|
12445b476d | ||
|
|
d884e805c5 | ||
|
|
4bea6657ef | ||
|
|
ae0c95efcc | ||
|
|
c1b8cd9058 | ||
|
|
0cfb0bacb2 | ||
|
|
d2f788762a | ||
|
|
17f8059fea | ||
|
|
c560043581 | ||
|
|
4c0b1cc1ae | ||
|
|
035e6bd407 | ||
|
|
1c34498368 | ||
|
|
ac959799e4 | ||
|
|
49b15af054 | ||
|
|
f9e468d891 | ||
|
|
7ddd5b765d | ||
|
|
50c191439d | ||
|
|
3b8a0bc146 | ||
|
|
136040ee15 | ||
|
|
e58855c7a4 | ||
|
|
00131e752d | ||
|
|
223ddb2008 | ||
|
|
fcf81147e7 | ||
|
|
73a2d71f44 | ||
|
|
bd8065295c | ||
|
|
0acf9b351f | ||
|
|
073a21ac0b | ||
|
|
64e45b04e0 | ||
|
|
55af5bda55 | ||
|
|
3dcedb36b4 | ||
|
|
6d64ecf359 | ||
|
|
5ff72a48a7 | ||
|
|
82a313a14c | ||
|
|
fdb199290b | ||
|
|
1aa195a9c0 | ||
|
|
e51bd49f87 | ||
|
|
7e697ab7ff | ||
|
|
6d9dd1dc6d | ||
|
|
112351d557 | ||
|
|
a6efff8b02 | ||
|
|
975226e7ff |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -31,3 +31,6 @@
|
||||
[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
|
||||
|
||||
@@ -431,6 +431,9 @@ 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
|
||||
# =========================
|
||||
|
||||
6
externals/CMakeLists.txt
vendored
6
externals/CMakeLists.txt
vendored
@@ -43,10 +43,16 @@ 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
|
||||
|
||||
16
externals/open_source_archives/CMakeLists.txt
vendored
Normal file
16
externals/open_source_archives/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
4
externals/open_source_archives/Readme.md
vendored
Normal file
4
externals/open_source_archives/Readme.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
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
|
||||
6
externals/open_source_archives/include/FontChineseSimplified.h
vendored
Normal file
6
externals/open_source_archives/include/FontChineseSimplified.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontChineseSimplified;
|
||||
|
||||
6
externals/open_source_archives/include/FontChineseTraditional.h
vendored
Normal file
6
externals/open_source_archives/include/FontChineseTraditional.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 222236> FontChineseTraditional;
|
||||
|
||||
6
externals/open_source_archives/include/FontExtendedChineseSimplified.h
vendored
Normal file
6
externals/open_source_archives/include/FontExtendedChineseSimplified.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 293516> FontExtendedChineseSimplified;
|
||||
|
||||
6
externals/open_source_archives/include/FontKorean.h
vendored
Normal file
6
externals/open_source_archives/include/FontKorean.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontKorean;
|
||||
|
||||
6
externals/open_source_archives/include/FontNintendoExtended.h
vendored
Normal file
6
externals/open_source_archives/include/FontNintendoExtended.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 172064> FontNintendoExtended;
|
||||
|
||||
6
externals/open_source_archives/include/FontStandard.h
vendored
Normal file
6
externals/open_source_archives/include/FontStandard.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontStandard;
|
||||
|
||||
18112
externals/open_source_archives/src/FontChineseSimplified.cpp
vendored
Normal file
18112
externals/open_source_archives/src/FontChineseSimplified.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18525
externals/open_source_archives/src/FontChineseTraditional.cpp
vendored
Normal file
18525
externals/open_source_archives/src/FontChineseTraditional.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24465
externals/open_source_archives/src/FontExtendedChineseSimplified.cpp
vendored
Normal file
24465
externals/open_source_archives/src/FontExtendedChineseSimplified.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18112
externals/open_source_archives/src/FontKorean.cpp
vendored
Normal file
18112
externals/open_source_archives/src/FontKorean.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
14344
externals/open_source_archives/src/FontNintendoExtended.cpp
vendored
Normal file
14344
externals/open_source_archives/src/FontNintendoExtended.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18112
externals/open_source_archives/src/FontStandard.cpp
vendored
Normal file
18112
externals/open_source_archives/src/FontStandard.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
externals/soundtouch
vendored
Submodule
1
externals/soundtouch
vendored
Submodule
Submodule externals/soundtouch added at 060181eaf2
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: 71b75f653f...1de435ed04
@@ -17,6 +17,8 @@ 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>
|
||||
)
|
||||
@@ -24,6 +26,7 @@ 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)
|
||||
|
||||
@@ -35,12 +35,12 @@ 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 size_t num_frames = signal.size() / 2;
|
||||
for (size_t i = 0; i < num_frames; i++) {
|
||||
const std::size_t num_frames = signal.size() / 2;
|
||||
for (std::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 (size_t ch = 0; ch < channel_count; ch++) {
|
||||
for (std::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] -
|
||||
@@ -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(size_t total_count, size_t index) {
|
||||
static double CascadingBiquadQ(std::size_t total_count, std::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, size_t cascade_size) {
|
||||
CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size) {
|
||||
std::vector<Filter> cascade(cascade_size);
|
||||
for (size_t i = 0; i < cascade_size; i++) {
|
||||
for (std::size_t i = 0; i < cascade_size; i++) {
|
||||
cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i));
|
||||
}
|
||||
return CascadingFilter{std::move(cascade)};
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
void Process(std::vector<s16>& signal);
|
||||
|
||||
private:
|
||||
static constexpr size_t channel_count = 2;
|
||||
static constexpr std::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, size_t cascade_size);
|
||||
static CascadingFilter LowPass(double cutoff, std::size_t cascade_size);
|
||||
|
||||
/// Passthrough.
|
||||
CascadingFilter();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace AudioCore {
|
||||
|
||||
/// The Lanczos kernel
|
||||
static double Lanczos(size_t a, double x) {
|
||||
static double Lanczos(std::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 size_t taps = InterpolationState::lanczos_taps;
|
||||
const size_t num_frames = input.size() / 2;
|
||||
constexpr std::size_t taps = InterpolationState::lanczos_taps;
|
||||
const std::size_t num_frames = input.size() / 2;
|
||||
|
||||
std::vector<s16> output;
|
||||
output.reserve(static_cast<size_t>(input.size() / ratio + 4));
|
||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + 4));
|
||||
|
||||
double& pos = state.position;
|
||||
auto& h = state.history;
|
||||
for (size_t i = 0; i < num_frames; ++i) {
|
||||
for (std::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 (size_t j = 0; j < h.size(); j++) {
|
||||
for (std::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];
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
namespace AudioCore {
|
||||
|
||||
struct InterpolationState {
|
||||
static constexpr size_t lanczos_taps = 4;
|
||||
static constexpr size_t history_size = lanczos_taps * 2 - 1;
|
||||
static constexpr std::size_t lanczos_taps = 4;
|
||||
static constexpr std::size_t history_size = lanczos_taps * 2 - 1;
|
||||
|
||||
double current_ratio = 0.0;
|
||||
CascadingFilter nyquist;
|
||||
|
||||
@@ -39,7 +39,8 @@ 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, size_t max_count) {
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
|
||||
std::size_t max_count) {
|
||||
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||
}
|
||||
|
||||
|
||||
@@ -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, size_t max_count);
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
||||
|
||||
/// Starts an audio stream for playback
|
||||
void StartStream(StreamPtr stream);
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
// 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 {
|
||||
@@ -13,6 +16,41 @@ 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) {
|
||||
@@ -27,6 +65,8 @@ AudioRenderer::AudioRenderer(AudioRendererParameter params,
|
||||
QueueMixedBuffer(2);
|
||||
}
|
||||
|
||||
AudioRenderer::~AudioRenderer() = default;
|
||||
|
||||
u32 AudioRenderer::GetSampleRate() const {
|
||||
return worker_params.sample_rate;
|
||||
}
|
||||
@@ -52,8 +92,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
memory_pool_count * sizeof(MemoryPoolInfo));
|
||||
|
||||
// Copy VoiceInfo structs
|
||||
size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
|
||||
config.voice_resource_size};
|
||||
std::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);
|
||||
@@ -72,7 +112,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
|
||||
// Update memory pool state
|
||||
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
|
||||
for (size_t index = 0; index < memory_pool.size(); ++index) {
|
||||
for (std::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) {
|
||||
@@ -93,7 +133,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
response_data.memory_pools_size);
|
||||
|
||||
// Copy output voice status
|
||||
size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
|
||||
std::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));
|
||||
@@ -103,12 +143,12 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
return output_params;
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
|
||||
void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
|
||||
wave_index = index & 3;
|
||||
is_refresh_pending = true;
|
||||
}
|
||||
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
|
||||
if (!IsPlaying()) {
|
||||
return {};
|
||||
}
|
||||
@@ -117,9 +157,9 @@ std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count)
|
||||
RefreshBuffer();
|
||||
}
|
||||
|
||||
const size_t max_size{samples.size() - offset};
|
||||
const size_t dequeue_offset{offset};
|
||||
size_t size{sample_count * STREAM_NUM_CHANNELS};
|
||||
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};
|
||||
if (size > max_size) {
|
||||
size = max_size;
|
||||
}
|
||||
@@ -184,7 +224,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
case 1:
|
||||
// 1 channel is upsampled to 2 channel
|
||||
samples.resize(new_samples.size() * 2);
|
||||
for (size_t index = 0; index < new_samples.size(); ++index) {
|
||||
for (std::size_t index = 0; index < new_samples.size(); ++index) {
|
||||
samples[index * 2] = new_samples[index];
|
||||
samples[index * 2 + 1] = new_samples[index];
|
||||
}
|
||||
@@ -210,7 +250,7 @@ static constexpr s16 ClampToS16(s32 value) {
|
||||
}
|
||||
|
||||
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
constexpr size_t BUFFER_SIZE{512};
|
||||
constexpr std::size_t BUFFER_SIZE{512};
|
||||
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
|
||||
|
||||
for (auto& voice : voices) {
|
||||
@@ -218,7 +258,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t offset{};
|
||||
std::size_t offset{};
|
||||
s64 samples_remaining{BUFFER_SIZE};
|
||||
while (samples_remaining > 0) {
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
|
||||
|
||||
@@ -8,16 +8,20 @@
|
||||
#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/event.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioOut;
|
||||
|
||||
enum class PlayState : u8 {
|
||||
Started = 0,
|
||||
Stopped = 1,
|
||||
@@ -158,6 +162,8 @@ 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();
|
||||
@@ -166,45 +172,12 @@ public:
|
||||
u32 GetMixBufferCount() const;
|
||||
|
||||
private:
|
||||
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{};
|
||||
};
|
||||
class VoiceState;
|
||||
|
||||
AudioRendererParameter worker_params;
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||
std::vector<VoiceState> voices;
|
||||
std::unique_ptr<AudioCore::AudioOut> audio_out;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
AudioCore::StreamPtr stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,27 +8,27 @@
|
||||
|
||||
namespace AudioCore::Codec {
|
||||
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, std::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 size_t FRAME_LEN = 8;
|
||||
constexpr size_t SAMPLES_PER_FRAME = 14;
|
||||
constexpr std::size_t FRAME_LEN = 8;
|
||||
constexpr std::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 size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
|
||||
const size_t ret_size =
|
||||
const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
|
||||
const std::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 size_t NUM_FRAMES =
|
||||
const std::size_t NUM_FRAMES =
|
||||
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
|
||||
for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
|
||||
for (std::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, size_t size, const ADPCM_Coef
|
||||
return static_cast<s16>(val);
|
||||
};
|
||||
|
||||
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) {
|
||||
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) {
|
||||
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
|
||||
ret[outputi] = sample1;
|
||||
outputi++;
|
||||
|
||||
@@ -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, size_t size, const ADPCM_Coeff& coeff,
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
|
||||
ADPCMState& state);
|
||||
|
||||
}; // namespace AudioCore::Codec
|
||||
|
||||
@@ -3,27 +3,23 @@
|
||||
// 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 SinkStreamImpl final : public SinkStream {
|
||||
class CubebSinkStream final : public SinkStream {
|
||||
public:
|
||||
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;
|
||||
}
|
||||
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} {
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.rate = sample_rate;
|
||||
@@ -38,7 +34,7 @@ public:
|
||||
|
||||
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
|
||||
¶ms, std::max(512u, minimum_latency),
|
||||
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
|
||||
&CubebSinkStream::DataCallback, &CubebSinkStream::StateCallback,
|
||||
this) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
|
||||
return;
|
||||
@@ -50,7 +46,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~SinkStreamImpl() {
|
||||
~CubebSinkStream() {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
@@ -62,27 +58,32 @@ public:
|
||||
cubeb_stream_destroy(stream_backend);
|
||||
}
|
||||
|
||||
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
|
||||
if (!ctx) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock{queue_mutex};
|
||||
queue.Push(samples);
|
||||
}
|
||||
|
||||
queue.reserve(queue.size() + samples.size() * GetNumChannels());
|
||||
std::size_t SamplesInQueue(u32 num_channels) const override {
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
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));
|
||||
}
|
||||
return queue.Size() / num_channels;
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
should_flush = true;
|
||||
}
|
||||
|
||||
u32 GetNumChannels() const {
|
||||
@@ -95,10 +96,11 @@ private:
|
||||
cubeb* ctx{};
|
||||
cubeb_stream* stream_backend{};
|
||||
u32 num_channels{};
|
||||
bool is_6_channel{};
|
||||
|
||||
std::mutex queue_mutex;
|
||||
std::vector<s16> queue;
|
||||
Common::RingBuffer<s16, 0x10000> queue;
|
||||
std::array<s16, 2> last_frame;
|
||||
std::atomic<bool> should_flush{};
|
||||
TimeStretcher time_stretch;
|
||||
|
||||
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames);
|
||||
@@ -117,10 +119,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& device) {
|
||||
return target_device_name == device.friendly_name;
|
||||
})};
|
||||
const auto device{
|
||||
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||
return target_device_name == info.friendly_name;
|
||||
})};
|
||||
if (device != collection_end) {
|
||||
output_device = device->devid;
|
||||
}
|
||||
@@ -144,44 +146,59 @@ CubebSink::~CubebSink() {
|
||||
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) {
|
||||
sink_streams.push_back(
|
||||
std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
|
||||
std::make_unique<CubebSinkStream>(ctx, sample_rate, num_channels, output_device, name));
|
||||
return *sink_streams.back();
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
u8* buffer = reinterpret_cast<u8*>(output_buffer);
|
||||
|
||||
if (!impl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::lock_guard lock{impl->queue_mutex};
|
||||
const std::size_t num_channels = impl->GetNumChannels();
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
|
||||
const size_t frames_to_write{
|
||||
std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))};
|
||||
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;
|
||||
|
||||
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 (impl->should_flush) {
|
||||
impl->time_stretch.Flush();
|
||||
impl->should_flush = false;
|
||||
}
|
||||
} else {
|
||||
samples_written = impl->queue.Pop(buffer, samples_to_write);
|
||||
}
|
||||
|
||||
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());
|
||||
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));
|
||||
}
|
||||
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
|
||||
void CubebSinkStream::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, "Citra Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return {};
|
||||
}
|
||||
@@ -190,7 +207,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 (size_t i = 0; i < collection.count; i++) {
|
||||
for (std::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);
|
||||
|
||||
@@ -21,6 +21,12 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const std::vector<SinkDetails> g_sink_details = {
|
||||
[] { return std::vector<std::string>{"null"}; }},
|
||||
};
|
||||
|
||||
const SinkDetails& GetSinkDetails(std::string sink_id) {
|
||||
const SinkDetails& GetSinkDetails(std::string_view 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; });
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -30,6 +32,6 @@ struct SinkDetails {
|
||||
|
||||
extern const std::vector<SinkDetails> g_sink_details;
|
||||
|
||||
const SinkDetails& GetSinkDetails(std::string sink_id);
|
||||
const SinkDetails& GetSinkDetails(std::string_view sink_id);
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -25,6 +25,10 @@ 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>;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#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"
|
||||
@@ -17,7 +18,7 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr size_t MaxAudioBufferCount{32};
|
||||
constexpr std::size_t MaxAudioBufferCount{32};
|
||||
|
||||
u32 Stream::GetNumChannels() const {
|
||||
switch (format) {
|
||||
@@ -52,7 +53,7 @@ void Stream::Stop() {
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
}
|
||||
|
||||
@@ -73,6 +74,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -83,6 +85,7 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
if (queued_buffers.empty()) {
|
||||
// No queued buffers - we are effectively paused
|
||||
sink_stream.Flush();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -90,6 +93,7 @@ void Stream::PlayNextBuffer() {
|
||||
queued_buffers.pop();
|
||||
|
||||
VolumeAdjustSamples(active_buffer->Samples());
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
@@ -119,9 +123,9 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
||||
std::vector<Buffer::Tag> tags;
|
||||
for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
released_buffers.pop();
|
||||
}
|
||||
|
||||
@@ -11,13 +11,16 @@
|
||||
#include <queue>
|
||||
|
||||
#include "audio_core/buffer.h"
|
||||
#include "audio_core/sink_stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SinkStream;
|
||||
|
||||
/**
|
||||
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
|
||||
*/
|
||||
@@ -49,7 +52,7 @@ public:
|
||||
bool ContainsBuffer(Buffer::Tag tag) const;
|
||||
|
||||
/// Returns a vector of recently released buffers specified by tag
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||
|
||||
/// Returns true if the stream is currently playing
|
||||
bool IsPlaying() const {
|
||||
@@ -57,7 +60,7 @@ public:
|
||||
}
|
||||
|
||||
/// Returns the number of queued buffers
|
||||
size_t GetQueueSize() const {
|
||||
std::size_t GetQueueSize() const {
|
||||
return queued_buffers.size();
|
||||
}
|
||||
|
||||
|
||||
69
src/audio_core/time_stretch.cpp
Normal file
69
src/audio_core/time_stretch.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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
|
||||
35
src/audio_core/time_stretch.h
Normal file
35
src/audio_core/time_stretch.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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
|
||||
@@ -71,6 +71,7 @@ add_library(common STATIC
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
quaternion.h
|
||||
ring_buffer.h
|
||||
scm_rev.cpp
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, size_t size) {
|
||||
constexpr T AlignUp(T value, std::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, size_t size) {
|
||||
constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
@@ -129,8 +129,8 @@ private:
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr size_t position = Position;
|
||||
static constexpr size_t bits = Bits;
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
|
||||
/**
|
||||
|
||||
@@ -170,14 +170,14 @@ public:
|
||||
m_val |= (IntTy)1 << bit;
|
||||
}
|
||||
|
||||
static BitSet AllTrue(size_t count) {
|
||||
static BitSet AllTrue(std::size_t count) {
|
||||
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
Ref operator[](size_t bit) {
|
||||
Ref operator[](std::size_t bit) {
|
||||
return Ref(this, (IntTy)1 << bit);
|
||||
}
|
||||
const Ref operator[](size_t bit) const {
|
||||
const Ref operator[](std::size_t bit) const {
|
||||
return (*const_cast<BitSet*>(this))[bit];
|
||||
}
|
||||
bool operator==(BitSet other) const {
|
||||
|
||||
@@ -114,7 +114,7 @@ static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
|
||||
return b;
|
||||
}
|
||||
|
||||
static uint64 HashLen0to16(const char* s, size_t len) {
|
||||
static uint64 HashLen0to16(const char* s, std::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, 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, size_t len) {
|
||||
static uint64 HashLen17to32(const char* s, std::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, size_t len) {
|
||||
static uint64 HashLen33to64(const char* s, std::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, size_t len) {
|
||||
return b + x;
|
||||
}
|
||||
|
||||
uint64 CityHash64(const char* s, size_t len) {
|
||||
uint64 CityHash64(const char* s, std::size_t len) {
|
||||
if (len <= 32) {
|
||||
if (len <= 16) {
|
||||
return HashLen0to16(s, len);
|
||||
@@ -212,7 +212,7 @@ uint64 CityHash64(const char* s, 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<size_t>(63);
|
||||
len = (len - 1) & ~static_cast<std::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, size_t len) {
|
||||
HashLen16(v.second, w.second) + x);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) {
|
||||
uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
|
||||
return CityHash64WithSeeds(s, len, k2, seed);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) {
|
||||
uint64 CityHash64WithSeeds(const char* s, std::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, size_t len, uint128 seed) {
|
||||
static uint128 CityMurmur(const char* s, std::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, size_t len, uint128 seed) {
|
||||
return uint128(a ^ b, HashLen16(b, a));
|
||||
}
|
||||
|
||||
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
|
||||
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
if (len < 128) {
|
||||
return CityMurmur(s, len, seed);
|
||||
}
|
||||
@@ -313,7 +313,7 @@ uint128 CityHash128WithSeed(const char* s, 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 (size_t tail_done = 0; tail_done < len;) {
|
||||
for (std::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, 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, size_t len) {
|
||||
uint128 CityHash128(const char* s, std::size_t len) {
|
||||
return len >= 16
|
||||
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
|
||||
: CityHash128WithSeed(s, len, uint128(k0, k1));
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
#include <utility>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> // for size_t.
|
||||
#include <stdlib.h> // for std::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, size_t len);
|
||||
uint64_t CityHash64(const char* buf, std::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, size_t len, uint64_t seed);
|
||||
uint64_t CityHash64WithSeed(const char* buf, std::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, size_t len, uint64_t seed0, uint64_t seed1);
|
||||
uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1);
|
||||
|
||||
// Hash function for a byte array.
|
||||
uint128 CityHash128(const char* s, size_t len);
|
||||
uint128 CityHash128(const char* s, std::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, size_t len, uint128 seed);
|
||||
uint128 CityHash128WithSeed(const char* s, std::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.
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace FileUtil {
|
||||
// Modifies argument.
|
||||
static void StripTailDirSlashes(std::string& fname) {
|
||||
if (fname.length() > 1) {
|
||||
size_t i = fname.length();
|
||||
std::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;
|
||||
}
|
||||
|
||||
size_t position = 0;
|
||||
std::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
|
||||
size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
|
||||
std::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
|
||||
size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
|
||||
std::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,11 +756,11 @@ std::string GetNANDRegistrationDir(bool system) {
|
||||
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
|
||||
}
|
||||
|
||||
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
|
||||
}
|
||||
|
||||
size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
IOFile file(filename, text_file ? "r" : "rb");
|
||||
|
||||
if (!file.IsOpen())
|
||||
@@ -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('/');
|
||||
size_t name_index;
|
||||
std::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 size_t index = name.rfind('.');
|
||||
const std::size_t index = name.rfind('.');
|
||||
|
||||
if (index == std::string_view::npos) {
|
||||
return {};
|
||||
|
||||
@@ -143,8 +143,9 @@ const std::string& GetExeDirectory();
|
||||
std::string AppDataRoamingDirectory();
|
||||
#endif
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
@@ -177,10 +178,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, size_t first, size_t last) {
|
||||
std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
|
||||
if (first >= last)
|
||||
return {};
|
||||
last = std::min<size_t>(last, vector.size());
|
||||
last = std::min<std::size_t>(last, vector.size());
|
||||
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
||||
}
|
||||
|
||||
@@ -213,47 +214,47 @@ public:
|
||||
bool Close();
|
||||
|
||||
template <typename T>
|
||||
size_t ReadArray(T* data, size_t length) const {
|
||||
std::size_t ReadArray(T* data, std::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<size_t>::max();
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
}
|
||||
|
||||
return std::fread(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteArray(const T* data, size_t length) {
|
||||
std::size_t WriteArray(const T* data, std::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<size_t>::max();
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
}
|
||||
|
||||
return std::fwrite(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t ReadBytes(T* data, size_t length) const {
|
||||
std::size_t ReadBytes(T* data, std::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>
|
||||
size_t WriteBytes(const T* data, size_t length) {
|
||||
std::size_t WriteBytes(const T* data, std::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>
|
||||
size_t WriteObject(const T& object) {
|
||||
std::size_t WriteObject(const T& object) {
|
||||
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
size_t WriteString(const std::string& str) {
|
||||
std::size_t WriteString(const std::string& str) {
|
||||
return WriteArray(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
|
||||
@@ -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, size_t len) {
|
||||
static inline u64 ComputeHash64(const void* data, std::size_t len) {
|
||||
return CityHash64(static_cast<const char*>(data), len);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ struct HashableStruct {
|
||||
return !(*this == o);
|
||||
};
|
||||
|
||||
size_t Hash() const {
|
||||
std::size_t Hash() const {
|
||||
return Common::ComputeStructHash64(state);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ u8 ToHexNibble(char c1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::array<u8, 16> operator""_array16(const char* str, size_t len) {
|
||||
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, "
|
||||
@@ -29,7 +29,7 @@ std::array<u8, 16> operator""_array16(const char* str, size_t len) {
|
||||
return HexStringToArray<16>(str);
|
||||
}
|
||||
|
||||
std::array<u8, 32> operator""_array32(const char* str, size_t len) {
|
||||
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, "
|
||||
|
||||
@@ -14,20 +14,20 @@ namespace Common {
|
||||
|
||||
u8 ToHexNibble(char c1);
|
||||
|
||||
template <size_t Size, bool le = false>
|
||||
template <std::size_t Size, bool le = false>
|
||||
std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||
std::array<u8, Size> out{};
|
||||
if constexpr (le) {
|
||||
for (size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
|
||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
} else {
|
||||
for (size_t i = 0; i < 2 * Size; i += 2)
|
||||
for (std::size_t i = 0; i < 2 * Size; i += 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
template <std::size_t Size>
|
||||
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
|
||||
std::string out;
|
||||
for (u8 c : array)
|
||||
@@ -35,7 +35,7 @@ std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
|
||||
std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);
|
||||
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
|
||||
|
||||
@@ -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 size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
|
||||
constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
|
||||
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
|
||||
private:
|
||||
FileUtil::IOFile file;
|
||||
size_t bytes_written;
|
||||
std::size_t bytes_written;
|
||||
};
|
||||
|
||||
void AddBackend(std::unique_ptr<Backend> backend);
|
||||
|
||||
@@ -71,7 +71,7 @@ void Filter::ResetAll(Level level) {
|
||||
}
|
||||
|
||||
void Filter::SetClassLevel(Class log_class, Level level) {
|
||||
class_levels[static_cast<size_t>(log_class)] = level;
|
||||
class_levels[static_cast<std::size_t>(log_class)] = level;
|
||||
}
|
||||
|
||||
void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
@@ -93,7 +93,8 @@ 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<size_t>(log_class)]);
|
||||
return static_cast<u8>(level) >=
|
||||
static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
|
||||
}
|
||||
|
||||
bool Filter::IsDebug() const {
|
||||
|
||||
@@ -49,6 +49,6 @@ public:
|
||||
bool IsDebug() const;
|
||||
|
||||
private:
|
||||
std::array<Level, static_cast<size_t>(Class::Count)> class_levels;
|
||||
std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels;
|
||||
};
|
||||
} // namespace Log
|
||||
|
||||
@@ -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(size_t size, bool low) {
|
||||
void* AllocateExecutableMemory(std::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(size_t size, bool low) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(size_t size) {
|
||||
void* AllocateMemoryPages(std::size_t size) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
#else
|
||||
@@ -90,7 +90,7 @@ void* AllocateMemoryPages(size_t size) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateAlignedMemory(size_t size, size_t alignment) {
|
||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = _aligned_malloc(size, alignment);
|
||||
#else
|
||||
@@ -109,7 +109,7 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* ptr, size_t size) {
|
||||
void FreeMemoryPages(void* ptr, std::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, size_t size, bool allowExecute) {
|
||||
void WriteProtectMemory(void* ptr, std::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, size_t size, bool allowExecute) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
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* 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 FreeAlignedMemory(void* ptr);
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
|
||||
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
|
||||
std::string MemUsage();
|
||||
|
||||
inline int GetPageSize() {
|
||||
|
||||
@@ -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 size_t buff_size = 255;
|
||||
static const std::size_t buff_size = 255;
|
||||
char err_str[buff_size];
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
111
src/common/ring_buffer.h
Normal file
111
src/common/ring_buffer.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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
|
||||
@@ -37,7 +37,7 @@ std::string ToUpper(std::string str) {
|
||||
}
|
||||
|
||||
// For Debugging. Read out an u8 array.
|
||||
std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) {
|
||||
std::string ArrayToString(const u8* data, std::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 size_t s = str.find_first_not_of(" \t\r\n");
|
||||
const std::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;
|
||||
|
||||
size_t dir_end = full_path.find_last_of("/"
|
||||
std::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;
|
||||
|
||||
size_t fname_end = full_path.rfind('.');
|
||||
std::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) {
|
||||
size_t i = 0;
|
||||
std::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) {
|
||||
size_t pos = 0;
|
||||
std::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 size_t in_bytes = sizeof(T) * input.size();
|
||||
const std::size_t in_bytes = sizeof(T) * input.size();
|
||||
// Multiply by 4, which is the max number of bytes to encode a codepoint
|
||||
const size_t out_buffer_size = 4 * in_bytes;
|
||||
const std::size_t out_buffer_size = 4 * in_bytes;
|
||||
|
||||
std::string out_buffer(out_buffer_size, '\0');
|
||||
|
||||
auto src_buffer = &input[0];
|
||||
size_t src_bytes = in_bytes;
|
||||
std::size_t src_bytes = in_bytes;
|
||||
auto dst_buffer = &out_buffer[0];
|
||||
size_t dst_bytes = out_buffer.size();
|
||||
std::size_t dst_bytes = out_buffer.size();
|
||||
|
||||
while (0 != src_bytes) {
|
||||
size_t const iconv_result =
|
||||
std::size_t const iconv_result =
|
||||
iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes);
|
||||
|
||||
if (static_cast<size_t>(-1) == iconv_result) {
|
||||
if (static_cast<std::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 size_t in_bytes = sizeof(char) * input.size();
|
||||
const std::size_t in_bytes = sizeof(char) * input.size();
|
||||
// Multiply by 4, which is the max number of bytes to encode a codepoint
|
||||
const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
|
||||
const std::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]);
|
||||
size_t src_bytes = in_bytes;
|
||||
std::size_t src_bytes = in_bytes;
|
||||
char* dst_buffer = (char*)(&out_buffer[0]);
|
||||
size_t dst_bytes = out_buffer.size();
|
||||
std::size_t dst_bytes = out_buffer.size();
|
||||
|
||||
while (0 != src_bytes) {
|
||||
size_t const iconv_result =
|
||||
std::size_t const iconv_result =
|
||||
iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes);
|
||||
|
||||
if (static_cast<size_t>(-1) == iconv_result) {
|
||||
if (static_cast<std::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, size_t max_len) {
|
||||
size_t len = 0;
|
||||
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < max_len && buffer[len] != '\0')
|
||||
++len;
|
||||
|
||||
|
||||
@@ -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, size_t size, int line_len = 20, bool spaces = true);
|
||||
std::string ArrayToString(const u8* data, std::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, size_t max_len);
|
||||
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
|
||||
|
||||
/**
|
||||
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
|
||||
|
||||
@@ -60,12 +60,12 @@ private:
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {}
|
||||
explicit Barrier(std::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 size_t current_generation = generation;
|
||||
const std::size_t current_generation = generation;
|
||||
|
||||
if (++waiting == count) {
|
||||
generation++;
|
||||
@@ -80,9 +80,9 @@ public:
|
||||
private:
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
const size_t count;
|
||||
size_t waiting;
|
||||
size_t generation; // Incremented once each time the barrier is used
|
||||
const std::size_t count;
|
||||
std::size_t waiting;
|
||||
std::size_t generation; // Incremented once each time the barrier is used
|
||||
};
|
||||
|
||||
void SleepCurrentThread(int ms);
|
||||
|
||||
@@ -97,7 +97,7 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
constexpr size_t ABI_SHADOW_SPACE = 0x20;
|
||||
constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
|
||||
|
||||
#else
|
||||
|
||||
@@ -147,22 +147,23 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
Xbyak::util::r15,
|
||||
});
|
||||
|
||||
constexpr size_t ABI_SHADOW_SPACE = 0;
|
||||
constexpr std::size_t ABI_SHADOW_SPACE = 0;
|
||||
|
||||
#endif
|
||||
|
||||
inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size,
|
||||
s32* out_subtraction, s32* out_xmm_offset) {
|
||||
inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size, s32* out_subtraction,
|
||||
s32* out_xmm_offset) {
|
||||
int count = (regs & ABI_ALL_GPRS).Count();
|
||||
rsp_alignment -= count * 8;
|
||||
size_t subtraction = 0;
|
||||
std::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;
|
||||
size_t xmm_base_subtraction = subtraction;
|
||||
std::size_t xmm_base_subtraction = subtraction;
|
||||
subtraction += needed_frame_size;
|
||||
subtraction += ABI_SHADOW_SPACE;
|
||||
// Final alignment.
|
||||
@@ -173,8 +174,9 @@ inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t n
|
||||
*out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
|
||||
}
|
||||
|
||||
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
|
||||
std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size = 0) {
|
||||
s32 subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
|
||||
|
||||
@@ -195,7 +197,8 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet
|
||||
}
|
||||
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size = 0) {
|
||||
s32 subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
|
||||
|
||||
|
||||
@@ -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.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
std::size_t addr = reinterpret_cast<std::size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
|
||||
@@ -388,7 +388,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)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_sources(core PRIVATE
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace Core {
|
||||
|
||||
/// Generic ARM11 CPU interface
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface : NonCopyable {
|
||||
public:
|
||||
virtual ~ARM_Interface() {}
|
||||
@@ -19,9 +19,9 @@ public:
|
||||
std::array<u64, 31> cpu_registers;
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
u64 cpsr;
|
||||
std::array<u128, 32> fpu_registers;
|
||||
u64 fpscr;
|
||||
u64 pstate;
|
||||
std::array<u128, 32> vector_registers;
|
||||
u64 fpcr;
|
||||
};
|
||||
|
||||
/// Runs the CPU until an event happens
|
||||
@@ -31,11 +31,11 @@ public:
|
||||
virtual void Step() = 0;
|
||||
|
||||
/// Maps a backing memory region for the CPU
|
||||
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) = 0;
|
||||
|
||||
/// Unmaps a region of memory that was previously mapped using MapBackingMemory
|
||||
virtual void UnmapMemory(VAddr address, size_t size) = 0;
|
||||
virtual void UnmapMemory(VAddr address, std::size_t size) = 0;
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
@@ -69,42 +69,50 @@ public:
|
||||
*/
|
||||
virtual void SetReg(int index, u64 value) = 0;
|
||||
|
||||
virtual u128 GetExtReg(int index) const = 0;
|
||||
|
||||
virtual void SetExtReg(int index, u128 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;
|
||||
|
||||
/**
|
||||
* Gets the value of a VFP register
|
||||
* @param index Register index (0-31)
|
||||
* @return Returns the value in the register
|
||||
* 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.
|
||||
*/
|
||||
virtual u32 GetVFPReg(int index) const = 0;
|
||||
virtual void SetVectorReg(int index, u128 value) = 0;
|
||||
|
||||
/**
|
||||
* Sets a VFP register to the given value
|
||||
* @param index Register index (0-31)
|
||||
* @param value Value to set register to
|
||||
* Get the current PSTATE register
|
||||
* @return Returns the value of the PSTATE register
|
||||
*/
|
||||
virtual void SetVFPReg(int index, u32 value) = 0;
|
||||
virtual u32 GetPSTATE() const = 0;
|
||||
|
||||
/**
|
||||
* Get the current CPSR register
|
||||
* @return Returns the value of the CPSR register
|
||||
* Set the current PSTATE register
|
||||
* @param pstate Value to set PSTATE to
|
||||
*/
|
||||
virtual u32 GetCPSR() const = 0;
|
||||
|
||||
/**
|
||||
* Set the current CPSR register
|
||||
* @param cpsr Value to set CPSR to
|
||||
*/
|
||||
virtual void SetCPSR(u32 cpsr) = 0;
|
||||
virtual void SetPSTATE(u32 pstate) = 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;
|
||||
|
||||
/**
|
||||
@@ -119,6 +127,7 @@ 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)
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
Memory::Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, size_t num_instructions) override {
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
return;
|
||||
default:
|
||||
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})",
|
||||
static_cast<size_t>(exception), pc);
|
||||
static_cast<std::size_t>(exception), pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
size_t num_interpreted_instructions = 0;
|
||||
std::size_t num_interpreted_instructions = 0;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
};
|
||||
@@ -157,7 +157,8 @@ void ARM_Dynarmic::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
|
||||
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
|
||||
std::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;
|
||||
@@ -168,12 +169,12 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
|
||||
void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory,
|
||||
void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) {
|
||||
inner_unicorn.MapBackingMemory(address, size, memory, perms);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) {
|
||||
void ARM_Dynarmic::UnmapMemory(u64 address, std::size_t size) {
|
||||
inner_unicorn.UnmapMemory(address, size);
|
||||
}
|
||||
|
||||
@@ -193,29 +194,20 @@ void ARM_Dynarmic::SetReg(int index, u64 value) {
|
||||
jit->SetRegister(index, value);
|
||||
}
|
||||
|
||||
u128 ARM_Dynarmic::GetExtReg(int index) const {
|
||||
u128 ARM_Dynarmic::GetVectorReg(int index) const {
|
||||
return jit->GetVector(index);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetExtReg(int index, u128 value) {
|
||||
void ARM_Dynarmic::SetVectorReg(int index, u128 value) {
|
||||
jit->SetVector(index, value);
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic::GetCPSR() const {
|
||||
u32 ARM_Dynarmic::GetPSTATE() const {
|
||||
return jit->GetPstate();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetCPSR(u32 cpsr) {
|
||||
jit->SetPstate(cpsr);
|
||||
void ARM_Dynarmic::SetPSTATE(u32 pstate) {
|
||||
jit->SetPstate(pstate);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetTlsAddress() const {
|
||||
@@ -238,18 +230,18 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
ctx.pc = jit->GetPC();
|
||||
ctx.cpsr = jit->GetPstate();
|
||||
ctx.fpu_registers = jit->GetVectors();
|
||||
ctx.fpscr = jit->GetFpcr();
|
||||
ctx.pstate = jit->GetPstate();
|
||||
ctx.vector_registers = jit->GetVectors();
|
||||
ctx.fpcr = 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.cpsr));
|
||||
jit->SetVectors(ctx.fpu_registers);
|
||||
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
|
||||
jit->SetPstate(static_cast<u32>(ctx.pstate));
|
||||
jit->SetVectors(ctx.vector_registers);
|
||||
jit->SetFpcr(static_cast<u32>(ctx.fpcr));
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PrepareReschedule() {
|
||||
@@ -269,10 +261,10 @@ void ARM_Dynarmic::PageTableChanged() {
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
}
|
||||
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {}
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
|
||||
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
|
||||
|
||||
void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) {
|
||||
void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) {
|
||||
// Size doesn't actually matter.
|
||||
monitor.Mark(core_index, addr, 16);
|
||||
}
|
||||
@@ -281,30 +273,30 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
|
||||
monitor.Clear();
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
|
||||
[&] { Memory::Write8(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
|
||||
[&] { Memory::Write16(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
|
||||
[&] { Memory::Write32(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
|
||||
[&] { Memory::Write64(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) {
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
|
||||
Memory::Write64(vaddr, value[0]);
|
||||
Memory::Write64(vaddr, value[1]);
|
||||
Memory::Write64(vaddr + 0, value[0]);
|
||||
Memory::Write64(vaddr + 8, value[1]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,24 +19,22 @@ class DynarmicExclusiveMonitor;
|
||||
|
||||
class ARM_Dynarmic final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index);
|
||||
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index);
|
||||
~ARM_Dynarmic();
|
||||
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
void UnmapMemory(u64 address, size_t size) override;
|
||||
void UnmapMemory(u64 address, std::size_t size) override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
void SetReg(int index, u64 value) 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;
|
||||
u128 GetVectorReg(int index) const override;
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) 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;
|
||||
@@ -59,7 +57,7 @@ private:
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
size_t core_index;
|
||||
std::size_t core_index;
|
||||
std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
|
||||
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
@@ -67,17 +65,17 @@ private:
|
||||
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
public:
|
||||
explicit DynarmicExclusiveMonitor(size_t core_count);
|
||||
explicit DynarmicExclusiveMonitor(std::size_t core_count);
|
||||
~DynarmicExclusiveMonitor();
|
||||
|
||||
void SetExclusive(size_t core_index, VAddr addr) override;
|
||||
void SetExclusive(std::size_t core_index, VAddr addr) override;
|
||||
void ClearExclusive() 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;
|
||||
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;
|
||||
|
||||
private:
|
||||
friend class ARM_Dynarmic;
|
||||
|
||||
@@ -12,14 +12,14 @@ class ExclusiveMonitor {
|
||||
public:
|
||||
virtual ~ExclusiveMonitor();
|
||||
|
||||
virtual void SetExclusive(size_t core_index, VAddr addr) = 0;
|
||||
virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual void ClearExclusive() = 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;
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -90,12 +90,12 @@ ARM_Unicorn::~ARM_Unicorn() {
|
||||
CHECKED(uc_close(uc));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) {
|
||||
CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) {
|
||||
void ARM_Unicorn::UnmapMemory(VAddr address, std::size_t size) {
|
||||
CHECKED(uc_mem_unmap(uc, address, size));
|
||||
}
|
||||
|
||||
@@ -131,33 +131,24 @@ void ARM_Unicorn::SetReg(int regn, u64 val) {
|
||||
CHECKED(uc_reg_write(uc, treg, &val));
|
||||
}
|
||||
|
||||
u128 ARM_Unicorn::GetExtReg(int /*index*/) const {
|
||||
u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
|
||||
UNIMPLEMENTED();
|
||||
static constexpr u128 res{};
|
||||
return res;
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) {
|
||||
void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
u32 ARM_Unicorn::GetVFPReg(int /*index*/) const {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
u32 ARM_Unicorn::GetCPSR() const {
|
||||
u32 ARM_Unicorn::GetPSTATE() const {
|
||||
u64 nzcv{};
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
|
||||
return static_cast<u32>(nzcv);
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetCPSR(u32 cpsr) {
|
||||
u64 nzcv = cpsr;
|
||||
void ARM_Unicorn::SetPSTATE(u32 pstate) {
|
||||
u64 nzcv = pstate;
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
|
||||
}
|
||||
|
||||
@@ -219,7 +210,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
|
||||
|
||||
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.cpsr));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
|
||||
|
||||
for (auto i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
@@ -234,7 +225,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = &ctx.fpu_registers[i];
|
||||
tregs[i] = &ctx.vector_registers[i];
|
||||
}
|
||||
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
|
||||
@@ -246,7 +237,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
|
||||
|
||||
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.cpsr));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
|
||||
|
||||
for (int i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
@@ -261,7 +252,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.fpu_registers[i];
|
||||
tregs[i] = (void*)&ctx.vector_registers[i];
|
||||
}
|
||||
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
|
||||
|
||||
@@ -15,19 +15,17 @@ class ARM_Unicorn final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Unicorn();
|
||||
~ARM_Unicorn();
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
void UnmapMemory(VAddr address, size_t size) override;
|
||||
void UnmapMemory(VAddr address, std::size_t size) override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
void SetReg(int index, u64 value) 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;
|
||||
u128 GetVectorReg(int index) const override;
|
||||
void SetVectorReg(int index, u128 value) override;
|
||||
u32 GetPSTATE() const override;
|
||||
void SetPSTATE(u32 pstate) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
void SetTPIDR_EL0(u64 value) override;
|
||||
|
||||
@@ -140,7 +140,7 @@ struct System::Impl {
|
||||
|
||||
cpu_barrier = std::make_shared<CpuBarrier>();
|
||||
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
|
||||
for (size_t index = 0; index < cpu_cores.size(); ++index) {
|
||||
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
|
||||
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ struct System::Impl {
|
||||
// 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) {
|
||||
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];
|
||||
@@ -285,7 +285,7 @@ struct System::Impl {
|
||||
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
|
||||
std::size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
@@ -348,7 +348,7 @@ ARM_Interface& System::CurrentArmInterface() {
|
||||
return CurrentCpuCore().ArmInterface();
|
||||
}
|
||||
|
||||
size_t System::CurrentCoreIndex() {
|
||||
std::size_t System::CurrentCoreIndex() {
|
||||
return CurrentCpuCore().CoreIndex();
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ Kernel::Scheduler& System::CurrentScheduler() {
|
||||
return *CurrentCpuCore().Scheduler();
|
||||
}
|
||||
|
||||
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
|
||||
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return impl->cpu_cores[core_index]->Scheduler();
|
||||
}
|
||||
@@ -369,12 +369,12 @@ const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const {
|
||||
return impl->kernel.CurrentProcess();
|
||||
}
|
||||
|
||||
ARM_Interface& System::ArmInterface(size_t core_index) {
|
||||
ARM_Interface& System::ArmInterface(std::size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return impl->cpu_cores[core_index]->ArmInterface();
|
||||
}
|
||||
|
||||
Cpu& System::CpuCore(size_t core_index) {
|
||||
Cpu& System::CpuCore(std::size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return *impl->cpu_cores[core_index];
|
||||
}
|
||||
|
||||
@@ -145,16 +145,16 @@ public:
|
||||
ARM_Interface& CurrentArmInterface();
|
||||
|
||||
/// Gets the index of the currently running CPU core
|
||||
size_t CurrentCoreIndex();
|
||||
std::size_t CurrentCoreIndex();
|
||||
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
Kernel::Scheduler& CurrentScheduler();
|
||||
|
||||
/// Gets an ARM interface to the CPU core with the specified index
|
||||
ARM_Interface& ArmInterface(size_t core_index);
|
||||
ARM_Interface& ArmInterface(std::size_t core_index);
|
||||
|
||||
/// Gets a CPU interface to the CPU core with the specified index
|
||||
Cpu& CpuCore(size_t core_index);
|
||||
Cpu& CpuCore(std::size_t core_index);
|
||||
|
||||
/// Gets the exclusive monitor
|
||||
ExclusiveMonitor& Monitor();
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
const VideoCore::RendererBase& Renderer() const;
|
||||
|
||||
/// Gets the scheduler for the CPU core with the specified index
|
||||
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
|
||||
const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index);
|
||||
|
||||
/// Provides a reference to the current process
|
||||
Kernel::SharedPtr<Kernel::Process>& CurrentProcess();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#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"
|
||||
@@ -49,7 +50,7 @@ bool CpuBarrier::Rendezvous() {
|
||||
}
|
||||
|
||||
Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index)
|
||||
: cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
|
||||
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
@@ -66,7 +67,9 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
|
||||
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
|
||||
}
|
||||
|
||||
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) {
|
||||
Cpu::~Cpu() = default;
|
||||
|
||||
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Scheduler;
|
||||
@@ -19,6 +18,7 @@ class Scheduler;
|
||||
namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
class ExclusiveMonitor;
|
||||
|
||||
constexpr unsigned NUM_CPU_CORES{4};
|
||||
|
||||
@@ -42,7 +42,8 @@ private:
|
||||
class Cpu {
|
||||
public:
|
||||
Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);
|
||||
std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
|
||||
~Cpu();
|
||||
|
||||
void RunLoop(bool tight_loop = true);
|
||||
|
||||
@@ -66,11 +67,11 @@ public:
|
||||
return core_index == 0;
|
||||
}
|
||||
|
||||
size_t CoreIndex() const {
|
||||
std::size_t CoreIndex() const {
|
||||
return core_index;
|
||||
}
|
||||
|
||||
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores);
|
||||
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
|
||||
|
||||
private:
|
||||
void Reschedule();
|
||||
@@ -80,7 +81,7 @@ private:
|
||||
std::shared_ptr<Kernel::Scheduler> scheduler;
|
||||
|
||||
std::atomic<bool> reschedule_pending = false;
|
||||
size_t core_index;
|
||||
std::size_t core_index;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
namespace Core::Crypto {
|
||||
namespace {
|
||||
std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
|
||||
std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) {
|
||||
std::vector<u8> out(0x10);
|
||||
for (size_t i = 0xF; i <= 0xF; --i) {
|
||||
for (std::size_t i = 0xF; i <= 0xF; --i) {
|
||||
out[i] = sector_id & 0xFF;
|
||||
sector_id >>= 8;
|
||||
}
|
||||
@@ -20,11 +20,14 @@ std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
|
||||
static_assert(static_cast<std::size_t>(Mode::CTR) ==
|
||||
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_CTR),
|
||||
"CTR has incorrect value.");
|
||||
static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB),
|
||||
static_assert(static_cast<std::size_t>(Mode::ECB) ==
|
||||
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_ECB),
|
||||
"ECB has incorrect value.");
|
||||
static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS),
|
||||
static_assert(static_cast<std::size_t>(Mode::XTS) ==
|
||||
static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_XTS),
|
||||
"XTS has incorrect value.");
|
||||
|
||||
// Structure to hide mbedtls types from header file
|
||||
@@ -33,7 +36,7 @@ struct CipherContext {
|
||||
mbedtls_cipher_context_t decryption_context;
|
||||
};
|
||||
|
||||
template <typename Key, size_t KeySize>
|
||||
template <typename Key, std::size_t KeySize>
|
||||
Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
|
||||
: ctx(std::make_unique<CipherContext>()) {
|
||||
mbedtls_cipher_init(&ctx->encryption_context);
|
||||
@@ -54,26 +57,26 @@ Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
|
||||
//"Failed to set key on mbedtls ciphers.");
|
||||
}
|
||||
|
||||
template <typename Key, size_t KeySize>
|
||||
template <typename Key, std::size_t KeySize>
|
||||
AESCipher<Key, KeySize>::~AESCipher() {
|
||||
mbedtls_cipher_free(&ctx->encryption_context);
|
||||
mbedtls_cipher_free(&ctx->decryption_context);
|
||||
}
|
||||
|
||||
template <typename Key, size_t KeySize>
|
||||
template <typename Key, std::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, size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
|
||||
template <typename Key, std::size_t KeySize>
|
||||
void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
|
||||
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
|
||||
|
||||
mbedtls_cipher_reset(context);
|
||||
|
||||
size_t written = 0;
|
||||
std::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) {
|
||||
@@ -90,8 +93,8 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t offset = 0; offset < size; offset += block_size) {
|
||||
auto length = std::min<size_t>(block_size, size - offset);
|
||||
for (std::size_t offset = 0; offset < size; offset += block_size) {
|
||||
auto length = std::min<std::size_t>(block_size, size - offset);
|
||||
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
|
||||
if (written != length) {
|
||||
if (length < block_size) {
|
||||
@@ -110,12 +113,12 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
|
||||
mbedtls_cipher_finish(context, nullptr, nullptr);
|
||||
}
|
||||
|
||||
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) {
|
||||
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.");
|
||||
|
||||
for (size_t i = 0; i < size; i += sector_size) {
|
||||
for (std::size_t i = 0; i < size; i += sector_size) {
|
||||
SetIV(CalculateNintendoTweak(sector_id++));
|
||||
Transcode<u8, u8>(src + i, sector_size, dest + i, op);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ enum class Op {
|
||||
Decrypt,
|
||||
};
|
||||
|
||||
template <typename Key, size_t KeySize = sizeof(Key)>
|
||||
template <typename Key, std::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, size_t size, Dest* dest, Op op) const {
|
||||
void Transcode(const Source* src, std::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, size_t size, u8* dest, Op op) const;
|
||||
void Transcode(const u8* src, std::size_t size, u8* dest, Op op) const;
|
||||
|
||||
template <typename Source, typename Dest>
|
||||
void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
|
||||
size_t sector_size, Op op) {
|
||||
void XTSTranscode(const Source* src, std::size_t size, Dest* dest, std::size_t sector_id,
|
||||
std::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, size_t size, u8* dest, size_t sector_id, size_t sector_size,
|
||||
Op op);
|
||||
void XTSTranscode(const u8* src, std::size_t size, u8* dest, std::size_t sector_id,
|
||||
std::size_t sector_size, Op op);
|
||||
|
||||
private:
|
||||
std::unique_ptr<CipherContext> ctx;
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
|
||||
namespace Core::Crypto {
|
||||
|
||||
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset)
|
||||
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_,
|
||||
std::size_t base_offset)
|
||||
: EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
|
||||
iv(16, 0) {}
|
||||
|
||||
size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
|
||||
std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
@@ -28,7 +29,7 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
|
||||
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);
|
||||
size_t read = 0x10 - sector_offset;
|
||||
std::size_t read = 0x10 - sector_offset;
|
||||
|
||||
if (length + sector_offset < 0x10) {
|
||||
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
|
||||
@@ -43,9 +44,9 @@ void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
|
||||
iv.assign(iv_.cbegin(), iv_.cbegin() + length);
|
||||
}
|
||||
|
||||
void CTREncryptionLayer::UpdateIV(size_t offset) const {
|
||||
void CTREncryptionLayer::UpdateIV(std::size_t offset) const {
|
||||
offset >>= 4;
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
for (std::size_t i = 0; i < 8; ++i) {
|
||||
iv[16 - i - 1] = offset & 0xFF;
|
||||
offset >>= 8;
|
||||
}
|
||||
|
||||
@@ -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, size_t base_offset);
|
||||
CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset);
|
||||
|
||||
size_t Read(u8* data, size_t length, size_t offset) const override;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
|
||||
void SetIV(const std::vector<u8>& iv);
|
||||
|
||||
private:
|
||||
size_t base_offset;
|
||||
std::size_t base_offset;
|
||||
|
||||
// Must be mutable as operations modify cipher contexts.
|
||||
mutable AESCipher<Key128> cipher;
|
||||
mutable std::vector<u8> iv;
|
||||
|
||||
void UpdateIV(size_t offset) const;
|
||||
void UpdateIV(std::size_t offset) const;
|
||||
};
|
||||
|
||||
} // namespace Core::Crypto
|
||||
|
||||
@@ -12,11 +12,11 @@ std::string EncryptionLayer::GetName() const {
|
||||
return base->GetName();
|
||||
}
|
||||
|
||||
size_t EncryptionLayer::GetSize() const {
|
||||
std::size_t EncryptionLayer::GetSize() const {
|
||||
return base->GetSize();
|
||||
}
|
||||
|
||||
bool EncryptionLayer::Resize(size_t new_size) {
|
||||
bool EncryptionLayer::Resize(std::size_t new_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ bool EncryptionLayer::IsReadable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) {
|
||||
std::size_t EncryptionLayer::Write(const u8* data, std::size_t length, std::size_t offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,15 +15,15 @@ class EncryptionLayer : public FileSys::VfsFile {
|
||||
public:
|
||||
explicit EncryptionLayer(FileSys::VirtualFile base);
|
||||
|
||||
size_t Read(u8* data, size_t length, size_t offset) const override = 0;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override = 0;
|
||||
|
||||
std::string GetName() const override;
|
||||
size_t GetSize() const override;
|
||||
bool Resize(size_t new_size) override;
|
||||
std::size_t GetSize() const override;
|
||||
bool Resize(std::size_t new_size) override;
|
||||
std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
size_t Write(const u8* data, size_t length, size_t offset) override;
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -54,7 +54,7 @@ boost::optional<Key128> DeriveSDSeed() {
|
||||
return boost::none;
|
||||
|
||||
std::array<u8, 0x10> buffer{};
|
||||
size_t offset = 0;
|
||||
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());
|
||||
@@ -105,7 +105,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
|
||||
|
||||
// Combine sources and seed
|
||||
for (auto& source : sd_key_sources) {
|
||||
for (size_t i = 0; i < source.size(); ++i)
|
||||
for (std::size_t i = 0; i < source.size(); ++i)
|
||||
source[i] ^= sd_seed[i & 0xF];
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
|
||||
return s256_keys.at({id, field1, field2});
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
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);
|
||||
|
||||
@@ -108,7 +108,7 @@ private:
|
||||
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 <size_t Size>
|
||||
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;
|
||||
|
||||
@@ -14,7 +14,7 @@ constexpr u64 XTS_SECTOR_SIZE = 0x4000;
|
||||
XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_)
|
||||
: EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {}
|
||||
|
||||
size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
|
||||
std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
@@ -46,7 +46,7 @@ size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
|
||||
block.resize(XTS_SECTOR_SIZE);
|
||||
cipher.XTSTranscode(block.data(), block.size(), block.data(),
|
||||
(offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
|
||||
const size_t read = XTS_SECTOR_SIZE - sector_offset;
|
||||
const std::size_t read = XTS_SECTOR_SIZE - sector_offset;
|
||||
|
||||
if (length + sector_offset < XTS_SECTOR_SIZE) {
|
||||
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
|
||||
|
||||
@@ -15,7 +15,7 @@ class XTSEncryptionLayer : public EncryptionLayer {
|
||||
public:
|
||||
XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key);
|
||||
|
||||
size_t Read(u8* data, size_t length, size_t offset) const override;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
|
||||
private:
|
||||
// Must be mutable as operations modify cipher contexts.
|
||||
|
||||
@@ -41,13 +41,14 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
|
||||
|
||||
for (XCIPartition partition :
|
||||
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
|
||||
auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
|
||||
auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
|
||||
if (raw != nullptr)
|
||||
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
|
||||
partitions[static_cast<std::size_t>(partition)] =
|
||||
std::make_shared<PartitionFilesystem>(raw);
|
||||
}
|
||||
|
||||
secure_partition = std::make_shared<NSP>(
|
||||
main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)]));
|
||||
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
|
||||
|
||||
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
|
||||
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
|
||||
@@ -92,7 +93,7 @@ Loader::ResultStatus XCI::GetProgramNCAStatus() const {
|
||||
}
|
||||
|
||||
VirtualDir XCI::GetPartition(XCIPartition partition) const {
|
||||
return partitions[static_cast<size_t>(partition)];
|
||||
return partitions[static_cast<std::size_t>(partition)];
|
||||
}
|
||||
|
||||
std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
|
||||
@@ -168,11 +169,11 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
}
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
if (partitions[static_cast<size_t>(part)] == nullptr) {
|
||||
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
|
||||
return Loader::ResultStatus::ErrorXCIMissingPartition;
|
||||
}
|
||||
|
||||
for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
|
||||
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
continue;
|
||||
auto nca = std::make_shared<NCA>(file);
|
||||
@@ -187,7 +188,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
} else {
|
||||
const u16 error_id = static_cast<u16>(nca->GetStatus());
|
||||
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
|
||||
partition_names[static_cast<size_t>(part)], nca->GetName(), error_id,
|
||||
partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
|
||||
nca->GetStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,11 +298,11 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
|
||||
auto section = sections[i];
|
||||
|
||||
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
|
||||
const size_t base_offset =
|
||||
const std::size_t base_offset =
|
||||
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
|
||||
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
|
||||
const size_t romfs_offset = base_offset + ivfc_offset;
|
||||
const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
|
||||
const std::size_t romfs_offset = base_offset + ivfc_offset;
|
||||
const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
|
||||
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
|
||||
auto dec = Decrypt(section, raw, romfs_offset);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ enum EntryType : u8 {
|
||||
struct Entry {
|
||||
Entry(std::string_view view, EntryType entry_type, u64 entry_size)
|
||||
: type{entry_type}, file_size{entry_size} {
|
||||
const size_t copy_size = view.copy(filename, std::size(filename) - 1);
|
||||
const std::size_t copy_size = view.copy(filename, std::size(filename) - 1);
|
||||
filename[copy_size] = '\0';
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
namespace FileSys {
|
||||
|
||||
bool operator>=(TitleType lhs, TitleType rhs) {
|
||||
return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs);
|
||||
return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
|
||||
}
|
||||
|
||||
bool operator<=(TitleType lhs, TitleType rhs) {
|
||||
return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs);
|
||||
return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
|
||||
}
|
||||
|
||||
CNMT::CNMT(VirtualFile file) {
|
||||
|
||||
@@ -22,11 +22,11 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel
|
||||
base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)),
|
||||
encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_),
|
||||
section_ctr(section_ctr_) {
|
||||
for (size_t i = 0; i < relocation.number_buckets - 1; ++i) {
|
||||
for (std::size_t i = 0; i < relocation.number_buckets - 1; ++i) {
|
||||
relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0});
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < subsection.number_buckets - 1; ++i) {
|
||||
for (std::size_t i = 0; i < subsection.number_buckets - 1; ++i) {
|
||||
subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch,
|
||||
{0},
|
||||
subsection_buckets[i + 1].entries[0].ctr});
|
||||
@@ -37,7 +37,7 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel
|
||||
|
||||
BKTR::~BKTR() = default;
|
||||
|
||||
size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
|
||||
std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||
// Read out of bounds.
|
||||
if (offset >= relocation.size)
|
||||
return 0;
|
||||
@@ -69,14 +69,14 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
|
||||
std::vector<u8> iv(16);
|
||||
auto subsection_ctr = subsection.ctr;
|
||||
auto offset_iv = section_offset + base_offset;
|
||||
for (size_t i = 0; i < section_ctr.size(); ++i)
|
||||
for (std::size_t i = 0; i < section_ctr.size(); ++i)
|
||||
iv[i] = section_ctr[0x8 - i - 1];
|
||||
offset_iv >>= 4;
|
||||
for (size_t i = 0; i < sizeof(u64); ++i) {
|
||||
for (std::size_t i = 0; i < sizeof(u64); ++i) {
|
||||
iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF);
|
||||
offset_iv >>= 8;
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(u32); ++i) {
|
||||
for (std::size_t i = 0; i < sizeof(u32); ++i) {
|
||||
iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF);
|
||||
subsection_ctr >>= 8;
|
||||
}
|
||||
@@ -110,8 +110,8 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
|
||||
}
|
||||
|
||||
template <bool Subsection, typename BlockType, typename BucketType>
|
||||
std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
|
||||
BucketType buckets) const {
|
||||
std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
|
||||
BucketType buckets) const {
|
||||
if constexpr (Subsection) {
|
||||
const auto last_bucket = buckets[block.number_buckets - 1];
|
||||
if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
|
||||
@@ -120,18 +120,18 @@ std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
|
||||
ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
|
||||
}
|
||||
|
||||
size_t bucket_id = std::count_if(block.base_offsets.begin() + 1,
|
||||
block.base_offsets.begin() + block.number_buckets,
|
||||
[&offset](u64 base_offset) { return base_offset <= offset; });
|
||||
std::size_t bucket_id = std::count_if(
|
||||
block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
|
||||
[&offset](u64 base_offset) { return base_offset <= offset; });
|
||||
|
||||
const auto bucket = buckets[bucket_id];
|
||||
|
||||
if (bucket.number_entries == 1)
|
||||
return {bucket_id, 0};
|
||||
|
||||
size_t low = 0;
|
||||
size_t mid = 0;
|
||||
size_t high = bucket.number_entries - 1;
|
||||
std::size_t low = 0;
|
||||
std::size_t mid = 0;
|
||||
std::size_t high = bucket.number_entries - 1;
|
||||
while (low <= high) {
|
||||
mid = (low + high) / 2;
|
||||
if (bucket.entries[mid].address_patch > offset) {
|
||||
@@ -179,11 +179,11 @@ std::string BKTR::GetName() const {
|
||||
return base_romfs->GetName();
|
||||
}
|
||||
|
||||
size_t BKTR::GetSize() const {
|
||||
std::size_t BKTR::GetSize() const {
|
||||
return relocation.size;
|
||||
}
|
||||
|
||||
bool BKTR::Resize(size_t new_size) {
|
||||
bool BKTR::Resize(std::size_t new_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ bool BKTR::IsReadable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t BKTR::Write(const u8* data, size_t length, size_t offset) {
|
||||
std::size_t BKTR::Write(const u8* data, std::size_t length, std::size_t offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,13 +98,13 @@ public:
|
||||
Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr);
|
||||
~BKTR() override;
|
||||
|
||||
size_t Read(u8* data, size_t length, size_t offset) const override;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
|
||||
std::string GetName() const override;
|
||||
|
||||
size_t GetSize() const override;
|
||||
std::size_t GetSize() const override;
|
||||
|
||||
bool Resize(size_t new_size) override;
|
||||
bool Resize(std::size_t new_size) override;
|
||||
|
||||
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
|
||||
|
||||
@@ -112,14 +112,14 @@ public:
|
||||
|
||||
bool IsReadable() const override;
|
||||
|
||||
size_t Write(const u8* data, size_t length, size_t offset) override;
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
private:
|
||||
template <bool Subsection, typename BlockType, typename BucketType>
|
||||
std::pair<size_t, size_t> SearchBucketEntry(u64 offset, BlockType block,
|
||||
BucketType buckets) const;
|
||||
std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block,
|
||||
BucketType buckets) const;
|
||||
|
||||
RelocationEntry GetRelocationEntry(u64 offset) const;
|
||||
RelocationEntry GetNextRelocationEntry(u64 offset) const;
|
||||
|
||||
@@ -42,21 +42,21 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
|
||||
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
|
||||
|
||||
size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
|
||||
size_t metadata_size =
|
||||
std::size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
|
||||
std::size_t metadata_size =
|
||||
sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size;
|
||||
|
||||
// Actually read in now...
|
||||
std::vector<u8> file_data = file->ReadBytes(metadata_size);
|
||||
const size_t total_size = file_data.size();
|
||||
const std::size_t total_size = file_data.size();
|
||||
|
||||
if (total_size != metadata_size) {
|
||||
status = Loader::ResultStatus::ErrorIncorrectPFSFileSize;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t entries_offset = sizeof(Header);
|
||||
size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
|
||||
std::size_t entries_offset = sizeof(Header);
|
||||
std::size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
|
||||
content_offset = strtab_offset + pfs_header.strtab_size;
|
||||
for (u16 i = 0; i < pfs_header.num_entries; i++) {
|
||||
FSEntry entry;
|
||||
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
|
||||
Header pfs_header{};
|
||||
bool is_hfs = false;
|
||||
size_t content_offset = 0;
|
||||
std::size_t content_offset = 0;
|
||||
|
||||
std::vector<VirtualFile> pfs_files;
|
||||
std::vector<VirtualDir> pfs_dirs;
|
||||
|
||||
@@ -21,7 +21,7 @@ constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
|
||||
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
|
||||
std::array<u8, sizeof(u32)> bytes{};
|
||||
bytes[0] = version % SINGLE_BYTE_MODULUS;
|
||||
for (size_t i = 1; i < bytes.size(); ++i) {
|
||||
for (std::size_t i = 1; i < bytes.size(); ++i) {
|
||||
version /= SINGLE_BYTE_MODULUS;
|
||||
bytes[i] = version % SINGLE_BYTE_MODULUS;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
|
||||
};
|
||||
|
||||
std::string FormatPatchTypeName(PatchType type) {
|
||||
return PATCH_TYPE_NAMES.at(static_cast<size_t>(type));
|
||||
return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type));
|
||||
}
|
||||
|
||||
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace FileSys {
|
||||
|
||||
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
size_t total_size = static_cast<size_t>(file->GetSize());
|
||||
std::size_t total_size = static_cast<std::size_t>(file->GetSize());
|
||||
if (total_size < sizeof(Header))
|
||||
return Loader::ResultStatus::ErrorBadNPDMHeader;
|
||||
|
||||
|
||||
@@ -62,11 +62,11 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
|
||||
"" ///< Currently unknown 'DeltaTitle'
|
||||
};
|
||||
|
||||
auto index = static_cast<size_t>(type);
|
||||
auto index = static_cast<std::size_t>(type);
|
||||
// If the index is after the jump in TitleType, subtract it out.
|
||||
if (index >= static_cast<size_t>(TitleType::Application)) {
|
||||
index -= static_cast<size_t>(TitleType::Application) -
|
||||
static_cast<size_t>(TitleType::FirmwarePackageB);
|
||||
if (index >= static_cast<std::size_t>(TitleType::Application)) {
|
||||
index -= static_cast<std::size_t>(TitleType::Application) -
|
||||
static_cast<std::size_t>(TitleType::FirmwarePackageB);
|
||||
}
|
||||
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
} else {
|
||||
std::vector<VirtualFile> concat;
|
||||
// Since the files are a two-digit hex number, max is FF.
|
||||
for (size_t i = 0; i < 0x100; ++i) {
|
||||
for (std::size_t i = 0; i < 0x100; ++i) {
|
||||
auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
|
||||
if (next != nullptr) {
|
||||
concat.push_back(std::move(next));
|
||||
|
||||
@@ -49,7 +49,7 @@ struct FileEntry {
|
||||
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
|
||||
|
||||
template <typename Entry>
|
||||
static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) {
|
||||
static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
|
||||
Entry entry{};
|
||||
if (file->ReadObject(&entry, offset) != sizeof(Entry))
|
||||
return {};
|
||||
@@ -59,8 +59,8 @@ static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t of
|
||||
return {entry, string};
|
||||
}
|
||||
|
||||
void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset,
|
||||
std::shared_ptr<VectorVfsDirectory> parent) {
|
||||
void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
|
||||
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
|
||||
while (true) {
|
||||
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
|
||||
|
||||
@@ -74,8 +74,9 @@ void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 t
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset,
|
||||
u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) {
|
||||
void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset,
|
||||
std::size_t data_offset, u32 this_dir_offset,
|
||||
std::shared_ptr<VectorVfsDirectory> parent) {
|
||||
while (true) {
|
||||
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
|
||||
auto current = std::make_shared<VectorVfsDirectory>(
|
||||
|
||||
@@ -167,18 +167,18 @@ std::string VfsFile::GetExtension() const {
|
||||
|
||||
VfsDirectory::~VfsDirectory() = default;
|
||||
|
||||
boost::optional<u8> VfsFile::ReadByte(size_t offset) const {
|
||||
boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
|
||||
u8 out{};
|
||||
size_t size = Read(&out, 1, offset);
|
||||
std::size_t size = Read(&out, 1, offset);
|
||||
if (size == 1)
|
||||
return out;
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const {
|
||||
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
|
||||
std::vector<u8> out(size);
|
||||
size_t read_size = Read(out.data(), size, offset);
|
||||
std::size_t read_size = Read(out.data(), size, offset);
|
||||
out.resize(read_size);
|
||||
return out;
|
||||
}
|
||||
@@ -187,11 +187,11 @@ std::vector<u8> VfsFile::ReadAllBytes() const {
|
||||
return ReadBytes(GetSize());
|
||||
}
|
||||
|
||||
bool VfsFile::WriteByte(u8 data, size_t offset) {
|
||||
bool VfsFile::WriteByte(u8 data, std::size_t offset) {
|
||||
return Write(&data, 1, offset) == 1;
|
||||
}
|
||||
|
||||
size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
|
||||
std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) {
|
||||
return Write(data.data(), data.size(), offset);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) co
|
||||
}
|
||||
|
||||
auto dir = GetSubdirectory(vec[0]);
|
||||
for (size_t component = 1; component < vec.size() - 1; ++component) {
|
||||
for (std::size_t component = 1; component < vec.size() - 1; ++component) {
|
||||
if (dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -249,7 +249,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_vie
|
||||
}
|
||||
|
||||
auto dir = GetSubdirectory(vec[0]);
|
||||
for (size_t component = 1; component < vec.size(); ++component) {
|
||||
for (std::size_t component = 1; component < vec.size(); ++component) {
|
||||
if (dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -286,7 +286,7 @@ bool VfsDirectory::IsRoot() const {
|
||||
return GetParentDirectory() == nullptr;
|
||||
}
|
||||
|
||||
size_t VfsDirectory::GetSize() const {
|
||||
std::size_t VfsDirectory::GetSize() const {
|
||||
const auto& files = GetFiles();
|
||||
const auto sum_sizes = [](const auto& range) {
|
||||
return std::accumulate(range.begin(), range.end(), 0ULL,
|
||||
@@ -434,13 +434,13 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) {
|
||||
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) {
|
||||
if (file1->GetSize() != file2->GetSize())
|
||||
return false;
|
||||
|
||||
std::vector<u8> f1_v(block_size);
|
||||
std::vector<u8> f2_v(block_size);
|
||||
for (size_t i = 0; i < file1->GetSize(); i += block_size) {
|
||||
for (std::size_t i = 0; i < file1->GetSize(); i += block_size) {
|
||||
auto f1_vs = file1->Read(f1_v.data(), block_size, i);
|
||||
auto f2_vs = file2->Read(f2_v.data(), block_size, i);
|
||||
|
||||
|
||||
@@ -92,9 +92,9 @@ public:
|
||||
// Retrieves the extension of the file name.
|
||||
virtual std::string GetExtension() const;
|
||||
// Retrieves the size of the file.
|
||||
virtual size_t GetSize() const = 0;
|
||||
virtual std::size_t GetSize() const = 0;
|
||||
// Resizes the file to new_size. Returns whether or not the operation was successful.
|
||||
virtual bool Resize(size_t new_size) = 0;
|
||||
virtual bool Resize(std::size_t new_size) = 0;
|
||||
// Gets a pointer to the directory containing this file, returning nullptr if there is none.
|
||||
virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0;
|
||||
|
||||
@@ -105,15 +105,15 @@ public:
|
||||
|
||||
// The primary method of reading from the file. Reads length bytes into data starting at offset
|
||||
// into file. Returns number of bytes successfully read.
|
||||
virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0;
|
||||
virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0;
|
||||
// The primary method of writing to the file. Writes length bytes from data starting at offset
|
||||
// into file. Returns number of bytes successfully written.
|
||||
virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0;
|
||||
virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
|
||||
|
||||
// Reads exactly one byte at the offset provided, returning boost::none on error.
|
||||
virtual boost::optional<u8> ReadByte(size_t offset = 0) const;
|
||||
virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
|
||||
// Reads size bytes starting at offset in file into a vector.
|
||||
virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const;
|
||||
virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
|
||||
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
|
||||
// 0)'
|
||||
virtual std::vector<u8> ReadAllBytes() const;
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
// Reads an array of type T, size number_elements starting at offset.
|
||||
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
|
||||
template <typename T>
|
||||
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
|
||||
std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
// Reads size bytes into the memory starting at data starting at offset into the file.
|
||||
// Returns the number of bytes read successfully.
|
||||
template <typename T>
|
||||
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
|
||||
std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Read(reinterpret_cast<u8*>(data), size, offset);
|
||||
}
|
||||
@@ -138,22 +138,22 @@ public:
|
||||
// Reads one object of type T starting at offset in file.
|
||||
// Returns the number of bytes read successfully (sizeof(T)).
|
||||
template <typename T>
|
||||
size_t ReadObject(T* data, size_t offset = 0) const {
|
||||
std::size_t ReadObject(T* data, std::size_t offset = 0) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
|
||||
}
|
||||
|
||||
// Writes exactly one byte to offset in file and retuns whether or not the byte was written
|
||||
// successfully.
|
||||
virtual bool WriteByte(u8 data, size_t offset = 0);
|
||||
virtual bool WriteByte(u8 data, std::size_t offset = 0);
|
||||
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
|
||||
// written.
|
||||
virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0);
|
||||
virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0);
|
||||
|
||||
// Writes an array of type T, size number_elements to offset in file.
|
||||
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
|
||||
template <typename T>
|
||||
size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) {
|
||||
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(data, number_elements * sizeof(T), offset);
|
||||
}
|
||||
@@ -161,7 +161,7 @@ public:
|
||||
// Writes size bytes starting at memory location data to offset in file.
|
||||
// Returns the number of bytes written successfully.
|
||||
template <typename T>
|
||||
size_t WriteBytes(const T* data, size_t size, size_t offset = 0) {
|
||||
std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(reinterpret_cast<const u8*>(data), size, offset);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public:
|
||||
// Writes one object of type T to offset in file.
|
||||
// Returns the number of bytes written successfully (sizeof(T)).
|
||||
template <typename T>
|
||||
size_t WriteObject(const T& data, size_t offset = 0) {
|
||||
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return Write(&data, sizeof(T), offset);
|
||||
}
|
||||
@@ -221,7 +221,7 @@ public:
|
||||
// Returns the name of the directory.
|
||||
virtual std::string GetName() const = 0;
|
||||
// Returns the total size of all files and subdirectories in this directory.
|
||||
virtual size_t GetSize() const;
|
||||
virtual std::size_t GetSize() const;
|
||||
// Returns the parent directory of this directory. Returns nullptr if this directory is root or
|
||||
// has no parent.
|
||||
virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0;
|
||||
@@ -311,7 +311,7 @@ public:
|
||||
};
|
||||
|
||||
// Compare the two files, byte-for-byte, in increments specificed by block_size
|
||||
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200);
|
||||
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size = 0x200);
|
||||
|
||||
// A method that copies the raw data between two different implementations of VirtualFile. If you
|
||||
// are using the same implementation, it is probably better to use the Copy method in the parent
|
||||
|
||||
@@ -20,7 +20,7 @@ VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
|
||||
|
||||
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
|
||||
: name(std::move(name)) {
|
||||
size_t next_offset = 0;
|
||||
std::size_t next_offset = 0;
|
||||
for (const auto& file : files_) {
|
||||
files[next_offset] = file;
|
||||
next_offset += file->GetSize();
|
||||
@@ -35,13 +35,13 @@ std::string ConcatenatedVfsFile::GetName() const {
|
||||
return files.begin()->second->GetName();
|
||||
}
|
||||
|
||||
size_t ConcatenatedVfsFile::GetSize() const {
|
||||
std::size_t ConcatenatedVfsFile::GetSize() const {
|
||||
if (files.empty())
|
||||
return 0;
|
||||
return files.rbegin()->first + files.rbegin()->second->GetSize();
|
||||
}
|
||||
|
||||
bool ConcatenatedVfsFile::Resize(size_t new_size) {
|
||||
bool ConcatenatedVfsFile::Resize(std::size_t new_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ bool ConcatenatedVfsFile::IsReadable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const {
|
||||
std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||
auto entry = files.end();
|
||||
for (auto iter = files.begin(); iter != files.end(); ++iter) {
|
||||
if (iter->first > offset) {
|
||||
@@ -84,7 +84,7 @@ size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const {
|
||||
return entry->second->Read(data, length, offset - entry->first);
|
||||
}
|
||||
|
||||
size_t ConcatenatedVfsFile::Write(const u8* data, size_t length, size_t offset) {
|
||||
std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,13 +23,13 @@ class ConcatenatedVfsFile : public VfsFile {
|
||||
|
||||
public:
|
||||
std::string GetName() const override;
|
||||
size_t GetSize() const override;
|
||||
bool Resize(size_t new_size) override;
|
||||
std::size_t GetSize() const override;
|
||||
bool Resize(std::size_t new_size) override;
|
||||
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
size_t Read(u8* data, size_t length, size_t offset) const override;
|
||||
size_t Write(const u8* data, size_t length, size_t offset) override;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
private:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user