Compare commits

..

1 Commits

Author SHA1 Message Date
bunnei
30f121d1ac Revert "core/memory: Get rid of 3DS leftovers" 2018-08-03 23:05:02 -04:00
207 changed files with 5429 additions and 8009 deletions

7
.gitmodules vendored
View File

@@ -25,9 +25,6 @@
[submodule "unicorn"]
path = externals/unicorn
url = https://github.com/yuzu-emu/unicorn
[submodule "mbedtls"]
path = externals/mbedtls
url = https://github.com/DarkLordZach/mbedtls
[submodule "opus"]
path = externals/opus
url = https://github.com/ogniK5377/opus.git
path = externals/opus
url = https://github.com/ogniK5377/opus.git

View File

@@ -4,10 +4,8 @@ function(copy_yuzu_Qt5_deps target_dir)
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
set(PLATFORMS ${DLL_DEST}platforms/)
set(STYLES ${DLL_DEST}styles/)
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
@@ -19,5 +17,4 @@ function(copy_yuzu_Qt5_deps target_dir)
)
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
endfunction(copy_yuzu_Qt5_deps)

View File

@@ -117,7 +117,6 @@ after_build:
mkdir $RELEASE_DIST
mkdir $RELEASE_DIST/platforms
mkdir $RELEASE_DIST/styles
mkdir $RELEASE_DIST/imageformats
# copy the compiled binaries and other release files to the release folder
Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
@@ -141,9 +140,6 @@ after_build:
# copy the qt windows vista style dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
# copy the qt jpeg imageformat dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
7z a $MINGW_SEVENZIP $RELEASE_DIST
}

View File

@@ -32,13 +32,9 @@ add_subdirectory(inih)
# lz4
set(LZ4_BUNDLED_MODE ON)
add_subdirectory(lz4/contrib/cmake_unofficial EXCLUDE_FROM_ALL)
add_subdirectory(lz4/contrib/cmake_unofficial)
target_include_directories(lz4_static INTERFACE ./lz4/lib)
# mbedtls
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
# MicroProfile
add_library(microprofile INTERFACE)
target_include_directories(microprofile INTERFACE ./microprofile)
@@ -62,5 +58,5 @@ target_include_directories(opus INTERFACE ./opus/include)
# Cubeb
if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "")
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
add_subdirectory(cubeb)
endif()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
externals/mbedtls vendored

Submodule externals/mbedtls deleted from d409b75a4c

View File

@@ -1,11 +1,9 @@
add_library(audio_core STATIC
audio_out.cpp
audio_out.h
audio_renderer.cpp
audio_renderer.h
buffer.h
codec.cpp
codec.h
cubeb_sink.cpp
cubeb_sink.h
null_sink.h
stream.cpp
stream.h
@@ -13,8 +11,6 @@ add_library(audio_core STATIC
sink_details.cpp
sink_details.h
sink_stream.h
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
)
create_target_directory_groups(audio_core)

View File

@@ -27,16 +27,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
return {};
}
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
Stream::ReleaseCallback&& release_callback) {
if (!sink) {
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
sink = sink_details.factory(Settings::values.audio_device_id);
}
return std::make_shared<Stream>(
sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
std::move(release_callback),
sink->AcquireSinkStream(sample_rate, num_channels));
}
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
@@ -51,7 +51,7 @@ void AudioOut::StopStream(StreamPtr stream) {
stream->Stop();
}
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
}

View File

@@ -5,7 +5,6 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "audio_core/buffer.h"
@@ -21,7 +20,7 @@ namespace AudioCore {
class AudioOut {
public:
/// Opens a new audio stream
StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
Stream::ReleaseCallback&& release_callback);
/// Returns a vector of recently released buffers specified by tag for the specified stream
@@ -34,7 +33,7 @@ public:
void StopStream(StreamPtr stream);
/// Queues a buffer into the specified audio stream, returns true on success
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
private:
SinkPtr sink;

View File

@@ -1,234 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "audio_core/audio_renderer.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/memory.h"
namespace AudioCore {
constexpr u32 STREAM_SAMPLE_RATE{48000};
constexpr u32 STREAM_NUM_CHANNELS{2};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
audio_core = std::make_unique<AudioCore::AudioOut>();
stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
[=]() { buffer_event->Signal(); });
audio_core->StartStream(stream);
QueueMixedBuffer(0);
QueueMixedBuffer(1);
QueueMixedBuffer(2);
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
// Copy MemoryPoolInfo structs
std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
std::memcpy(mem_pool_info.data(),
input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
memory_pool_count * sizeof(MemoryPoolInfo));
// Copy VoiceInfo structs
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);
}
// Update voices
for (auto& voice : voices) {
voice.UpdateState();
if (!voice.GetInfo().is_in_use) {
continue;
}
if (voice.GetInfo().is_new) {
voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
}
}
// Update memory pool state
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (size_t index = 0; index < memory_pool.size(); ++index) {
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
memory_pool[index].state = MemoryPoolStates::Attached;
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
memory_pool[index].state = MemoryPoolStates::Detached;
}
}
// Release previous buffers and queue next ones for playback
ReleaseAndQueueBuffers();
// Copy output header
UpdateDataHeader response_data{worker_params};
std::vector<u8> output_params(response_data.total_size);
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
// Copy output memory pool entries
std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
response_data.memory_pools_size);
// Copy output voice status
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));
voice_out_status_offset += sizeof(VoiceOutStatus);
}
return output_params;
}
void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
wave_index = index & 3;
is_refresh_pending = true;
}
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
if (!IsPlaying()) {
return {};
}
if (is_refresh_pending) {
RefreshBuffer();
}
const size_t max_size{samples.size() - offset};
const size_t dequeue_offset{offset};
size_t size{sample_count * STREAM_NUM_CHANNELS};
if (size > max_size) {
size = max_size;
}
out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
offset += size;
const auto& wave_buffer{info.wave_buffer[wave_index]};
if (offset == samples.size()) {
offset = 0;
if (!wave_buffer.is_looping) {
SetWaveIndex(wave_index + 1);
}
out_status.wave_buffer_consumed++;
if (wave_buffer.end_of_stream) {
info.play_state = PlayState::Paused;
}
}
return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
}
void AudioRenderer::VoiceState::UpdateState() {
if (is_in_use && !info.is_in_use) {
// No longer in use, reset state
is_refresh_pending = true;
wave_index = 0;
offset = 0;
out_status = {};
}
is_in_use = info.is_in_use;
}
void AudioRenderer::VoiceState::RefreshBuffer() {
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
info.wave_buffer[wave_index].buffer_sz);
switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
case Codec::PcmFormat::Int16: {
// PCM16 is played as-is
break;
}
case Codec::PcmFormat::Adpcm: {
// Decode ADPCM to PCM16
Codec::ADPCM_Coeff coeffs;
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
new_samples.size() * sizeof(s16), coeffs, adpcm_state);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
UNREACHABLE();
break;
}
switch (info.channel_count) {
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) {
samples[index * 2] = new_samples[index];
samples[index * 2 + 1] = new_samples[index];
}
break;
case 2: {
// 2 channel is played as is
samples = std::move(new_samples);
break;
}
default:
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
UNREACHABLE();
break;
}
is_refresh_pending = false;
}
static constexpr s16 ClampToS16(s32 value) {
return static_cast<s16>(std::clamp(value, -32768, 32767));
}
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
constexpr size_t BUFFER_SIZE{512};
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
for (auto& voice : voices) {
if (!voice.IsPlaying()) {
continue;
}
size_t offset{};
s64 samples_remaining{BUFFER_SIZE};
while (samples_remaining > 0) {
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
if (samples.empty()) {
break;
}
samples_remaining -= samples.size();
for (const auto& sample : samples) {
const s32 buffer_sample{buffer[offset]};
buffer[offset++] =
ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
}
}
}
audio_core->QueueBuffer(stream, tag, std::move(buffer));
}
void AudioRenderer::ReleaseAndQueueBuffers() {
const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
for (const auto& tag : released_buffers) {
QueueMixedBuffer(tag);
}
}
} // namespace AudioCore

View File

@@ -1,206 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "audio_core/audio_out.h"
#include "audio_core/codec.h"
#include "audio_core/stream.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/event.h"
namespace AudioCore {
enum class PlayState : u8 {
Started = 0,
Stopped = 1,
Paused = 2,
};
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
u32_le unknown_8;
u32_le unknown_c;
u32_le voice_count;
u32_le sink_count;
u32_le effect_count;
u32_le unknown_1c;
u8 unknown_20;
INSERT_PADDING_BYTES(3);
u32_le splitter_count;
u32_le unknown_2c;
INSERT_PADDING_WORDS(1);
u32_le revision;
};
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
enum class MemoryPoolStates : u32 { // Should be LE
Invalid = 0x0,
Unknown = 0x1,
RequestDetach = 0x2,
Detached = 0x3,
RequestAttach = 0x4,
Attached = 0x5,
Released = 0x6,
};
struct MemoryPoolEntry {
MemoryPoolStates state;
u32_le unknown_4;
u32_le unknown_8;
u32_le unknown_c;
};
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
struct MemoryPoolInfo {
u64_le pool_address;
u64_le pool_size;
MemoryPoolStates pool_state;
INSERT_PADDING_WORDS(3); // Unknown
};
static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
struct BiquadFilter {
u8 enable;
INSERT_PADDING_BYTES(1);
std::array<s16_le, 3> numerator;
std::array<s16_le, 2> denominator;
};
static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
struct WaveBuffer {
u64_le buffer_addr;
u64_le buffer_sz;
s32_le start_sample_offset;
s32_le end_sample_offset;
u8 is_looping;
u8 end_of_stream;
u8 sent_to_server;
INSERT_PADDING_BYTES(5);
u64 context_addr;
u64 context_sz;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
struct VoiceInfo {
u32_le id;
u32_le node_id;
u8 is_new;
u8 is_in_use;
PlayState play_state;
u8 sample_format;
u32_le sample_rate;
u32_le priority;
u32_le sorting_order;
u32_le channel_count;
float_le pitch;
float_le volume;
std::array<BiquadFilter, 2> biquad_filter;
u32_le wave_buffer_count;
u32_le wave_buffer_head;
INSERT_PADDING_WORDS(1);
u64_le additional_params_addr;
u64_le additional_params_sz;
u32_le mix_id;
u32_le splitter_info_id;
std::array<WaveBuffer, 4> wave_buffer;
std::array<u32_le, 6> voice_channel_resource_ids;
INSERT_PADDING_BYTES(24);
};
static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
struct VoiceOutStatus {
u64_le played_sample_count;
u32_le wave_buffer_consumed;
u32_le voice_drops_count;
};
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
struct UpdateDataHeader {
UpdateDataHeader() {}
explicit UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;
voice_resource_size = 0x0;
effects_size = config.effect_count * 0x10;
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
effects_size + sinks_size + performance_manager_size;
}
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
u32_le total_size;
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
class AudioRenderer {
public:
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
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{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};
};
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::unique_ptr<AudioCore::AudioOut> audio_core;
AudioCore::StreamPtr stream;
};
} // namespace AudioCore

View File

@@ -18,16 +18,11 @@ class Buffer {
public:
using Tag = u64;
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
/// Returns the raw audio data for the buffer
std::vector<s16>& Samples() {
return samples;
}
/// Returns the raw audio data for the buffer
const std::vector<s16>& GetSamples() const {
return samples;
const std::vector<u8>& GetData() const {
return data;
}
/// Returns the buffer tag, this is provided by the game to the audout service
@@ -37,7 +32,7 @@ public:
private:
Tag tag;
std::vector<s16> samples;
std::vector<u8> data;
};
using BufferPtr = std::shared_ptr<Buffer>;

View File

@@ -1,77 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "audio_core/codec.h"
namespace AudioCore::Codec {
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state) {
// GC-ADPCM with scale factor and variable coefficients.
// Frames are 8 bytes long containing 14 samples each.
// Samples are 4 bits (one nibble) long.
constexpr size_t FRAME_LEN = 8;
constexpr size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const size_t ret_size =
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
std::vector<s16> ret(ret_size);
int yn1 = state.yn1, yn2 = state.yn2;
const size_t NUM_FRAMES =
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
const int frame_header = data[framei * FRAME_LEN];
const int scale = 1 << (frame_header & 0xF);
const int idx = (frame_header >> 4) & 0x7;
// Coefficients are fixed point with 11 bits fractional part.
const int coef1 = coeff[idx * 2 + 0];
const int coef2 = coeff[idx * 2 + 1];
// Decodes an audio sample. One nibble produces one sample.
const auto decode_sample = [&](const int nibble) -> s16 {
const int xn = nibble * scale;
// We first transform everything into 11 bit fixed point, perform the second order
// digital filter, then transform back.
// 0x400 == 0.5 in 11 bit fixed point.
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
// Clamp to output range.
val = std::clamp<s32>(val, -32768, 32767);
// Advance output feedback.
yn2 = yn1;
yn1 = val;
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) {
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
ret[outputi] = sample1;
outputi++;
const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
ret[outputi] = sample2;
outputi++;
datai++;
}
}
state.yn1 = yn1;
state.yn2 = yn2;
return ret;
}
} // namespace AudioCore::Codec

View File

@@ -1,44 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
namespace AudioCore::Codec {
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
/// See: Codec::DecodeADPCM
struct ADPCMState {
// Two historical samples from previous processed buffer,
// required for ADPCM decoding
s16 yn1; ///< y[n-1]
s16 yn2; ///< y[n-2]
};
using ADPCM_Coeff = std::array<s16, 16>;
/**
* @param data Pointer to buffer that contains ADPCM data to decode
* @param size Size of buffer in bytes
* @param coeff ADPCM coefficients
* @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,
ADPCMState& state);
}; // namespace AudioCore::Codec

View File

@@ -13,30 +13,20 @@ namespace AudioCore {
class SinkStreamImpl 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;
}
cubeb_stream_params params{};
params.rate = sample_rate;
params.channels = num_channels;
SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
cubeb_stream_params params;
params.rate = 48000;
params.channels = GetNumChannels();
params.format = CUBEB_SAMPLE_S16NE;
params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
params.layout = CUBEB_LAYOUT_STEREO;
u32 minimum_latency{};
u32 minimum_latency = 0;
if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
}
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
&params, std::max(512u, minimum_latency),
if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
output_device, &params, std::max(512u, minimum_latency),
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
this) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
@@ -61,29 +51,33 @@ public:
cubeb_stream_destroy(stream_backend);
}
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
if (!ctx) {
return;
}
queue.reserve(queue.size() + samples.size() * GetNumChannels());
queue.reserve(queue.size() + sample_count * GetNumChannels());
if (is_6_channel) {
if (num_channels == 2) {
// Copy as-is
std::copy(samples, samples + sample_count * GetNumChannels(),
std::back_inserter(queue));
} else if (num_channels == 6) {
// Downsample 6 channels to 2
const size_t sample_count_copy_size = samples.size() * 2;
const size_t sample_count_copy_size = sample_count * num_channels * 2;
queue.reserve(sample_count_copy_size);
for (size_t i = 0; i < samples.size(); i += num_channels) {
for (size_t i = 0; i < sample_count * num_channels; 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));
ASSERT_MSG(false, "Unimplemented");
}
}
u32 GetNumChannels() const {
return num_channels;
// Only support 2-channel stereo output for now
return 2;
}
private:
@@ -91,8 +85,6 @@ private:
cubeb* ctx{};
cubeb_stream* stream_backend{};
u32 num_channels{};
bool is_6_channel{};
std::vector<s16> queue;
@@ -137,10 +129,8 @@ CubebSink::~CubebSink() {
cubeb_destroy(ctx);
}
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));
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
return *sink_streams.back();
}

View File

@@ -18,8 +18,7 @@ public:
explicit CubebSink(std::string device_id);
~CubebSink() override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
private:
cubeb* ctx{};

View File

@@ -13,14 +13,14 @@ public:
explicit NullSink(std::string){};
~NullSink() override = default;
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
const std::string& /*name*/) override {
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
return null_sink_stream;
}
private:
struct NullSinkStreamImpl final : SinkStream {
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
size_t /*sample_count*/) override {}
} null_sink_stream;
};

View File

@@ -5,7 +5,6 @@
#pragma once
#include <memory>
#include <string>
#include "audio_core/sink_stream.h"
#include "common/common_types.h"
@@ -22,8 +21,7 @@ constexpr char auto_device_name[] = "auto";
class Sink {
public:
virtual ~Sink() = default;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
const std::string& name) = 0;
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
};
using SinkPtr = std::unique_ptr<Sink>;

View File

@@ -5,7 +5,6 @@
#pragma once
#include <memory>
#include <vector>
#include "common/common_types.h"
@@ -23,8 +22,9 @@ public:
* Feed stereo samples to sink.
* @param num_channels Number of channels used.
* @param samples Samples in interleaved stereo PCM16 format.
* @param sample_count Number of samples.
*/
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
};
using SinkStreamPtr = std::unique_ptr<SinkStream>;

View File

@@ -32,13 +32,17 @@ u32 Stream::GetNumChannels() const {
return {};
}
u32 Stream::GetSampleSize() const {
return GetNumChannels() * 2;
}
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream, std::string&& name_)
SinkStream& sink_stream)
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
sink_stream{sink_stream}, name{std::move(name_)} {
sink_stream{sink_stream} {
release_event = CoreTiming::RegisterEvent(
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
}
void Stream::Play() {
@@ -51,15 +55,17 @@ void Stream::Stop() {
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
}
static void VolumeAdjustSamples(std::vector<s16>& samples) {
static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
std::vector<s16> samples(data.size() / sizeof(s16));
std::memcpy(samples.data(), data.data(), data.size());
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
if (volume == 1.0f) {
return;
return samples;
}
// Implementation of a volume slider with a dynamic range of 60 dB
@@ -67,6 +73,8 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
for (auto& sample : samples) {
sample = static_cast<s16>(sample * volume_scale_factor);
}
return samples;
}
void Stream::PlayNextBuffer() {
@@ -88,14 +96,14 @@ void Stream::PlayNextBuffer() {
active_buffer = queued_buffers.front();
queued_buffers.pop();
VolumeAdjustSamples(active_buffer->Samples());
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
sink_stream.EnqueueSamples(
GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
void Stream::ReleaseActiveBuffer() {
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
PlayNextBuffer();

View File

@@ -6,7 +6,6 @@
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <queue>
@@ -34,7 +33,7 @@ public:
using ReleaseCallback = std::function<void()>;
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
SinkStream& sink_stream, std::string&& name_);
SinkStream& sink_stream);
/// Plays the audio stream
void Play();
@@ -69,6 +68,9 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
/// Gets the sample size in bytes
u32 GetSampleSize() const;
private:
/// Current state of the stream
enum class State {
@@ -94,7 +96,6 @@ private:
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
std::string name; ///< Name of the stream, must be unique
};
using StreamPtr = std::shared_ptr<Stream>;

View File

@@ -9,13 +9,13 @@ namespace Common {
template <typename T>
constexpr T AlignUp(T value, size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
static_assert(std::is_unsigned<T>::value, "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) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
return static_cast<T>(value - value % size);
}

View File

@@ -96,7 +96,7 @@ static inline int LeastSignificantSetBit(u64 val) {
template <typename IntTy>
class BitSet {
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
public:
// A reference to a particular bit, returned from operator[].

View File

@@ -4,8 +4,6 @@
#pragma once
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "common/vector_math.h"
@@ -57,7 +55,7 @@ constexpr u8 Convert8To6(u8 value) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return {bytes[3], bytes[2], bytes[1], bytes[0]};
}
@@ -66,7 +64,7 @@ inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
return {bytes[2], bytes[1], bytes[0], 255};
}
@@ -75,7 +73,7 @@ inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline Math::Vec4<u8> DecodeRG8(const u8* bytes) {
inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
return {bytes[1], bytes[0], 0, 255};
}
@@ -84,9 +82,8 @@ inline Math::Vec4<u8> DecodeRG8(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255};
}
@@ -96,9 +93,8 @@ inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
}
@@ -108,9 +104,8 @@ inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
* @param bytes Pointer to encoded source color
* @return Result color decoded as Math::Vec4<u8>
*/
inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
}
@@ -121,9 +116,7 @@ inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
* @return Depth value as an u32
*/
inline u32 DecodeD16(const u8* bytes) {
u16_le data;
std::memcpy(&data, bytes, sizeof(data));
return data;
return *reinterpret_cast<const u16_le*>(bytes);
}
/**
@@ -140,7 +133,7 @@ inline u32 DecodeD24(const u8* bytes) {
* @param bytes Pointer to encoded source values
* @return Resulting values stored as a Math::Vec2
*/
inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
}
@@ -182,10 +175,8 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
const u16_le data =
*reinterpret_cast<u16_le*>(bytes) =
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
std::memcpy(bytes, &data, sizeof(data));
}
/**
@@ -194,10 +185,9 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
std::memcpy(bytes, &data, sizeof(data));
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
(Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
}
/**
@@ -206,10 +196,9 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
std::memcpy(bytes, &data, sizeof(data));
*reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
(Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
}
/**
@@ -218,8 +207,7 @@ inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD16(u32 value, u8* bytes) {
const u16_le data = static_cast<u16>(value);
std::memcpy(bytes, &data, sizeof(data));
*reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF;
}
/**

View File

@@ -32,7 +32,6 @@
#define SDMC_DIR "sdmc"
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
#define KEYS_DIR "keys"
#define LOG_DIR "log"
// Filenames

View File

@@ -706,7 +706,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS
paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
}
@@ -737,19 +736,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
return paths[path];
}
std::string GetHactoolConfigurationPath() {
#ifdef _WIN32
PWSTR pw_local_path = nullptr;
if (SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &pw_local_path) != S_OK)
return "";
std::string local_path = Common::UTF16ToUTF8(pw_local_path);
CoTaskMemFree(pw_local_path);
return local_path + "\\.switch";
#else
return GetHomeDirectory() + "/.switch";
#endif
}
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());
}
@@ -884,21 +870,11 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
return path;
}
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string SanitizePath(std::string_view path_) {
std::string path(path_);
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
if (directory_separator == DirectorySeparator::PlatformDefault) {
#ifdef _WIN32
type1 = '/';
type2 = '\\';
#endif
}
std::replace(path.begin(), path.end(), type1, type2);
std::replace(path.begin(), path.end(), '\\', '/');
path.erase(std::unique(path.begin(), path.end(),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
path.end());
return std::string(RemoveTrailingSlash(path));
}

View File

@@ -8,7 +8,6 @@
#include <cstdio>
#include <fstream>
#include <functional>
#include <limits>
#include <string>
#include <string_view>
#include <type_traits>
@@ -24,7 +23,6 @@ namespace FileUtil {
enum class UserPath {
CacheDir,
ConfigDir,
KeysDir,
LogDir,
NANDDir,
RootDir,
@@ -127,8 +125,6 @@ bool SetCurrentDir(const std::string& directory);
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
std::string GetHactoolConfigurationPath();
// Returns the path to where the sys file are
std::string GetSysDirectory();
@@ -182,12 +178,8 @@ std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t la
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
}
enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
std::string SanitizePath(std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
std::string SanitizePath(std::string_view path);
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
@@ -212,42 +204,39 @@ public:
template <typename T>
size_t ReadArray(T* data, size_t length) const {
static_assert(std::is_trivially_copyable_v<T>,
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
return std::numeric_limits<size_t>::max();
}
if (!IsOpen())
return -1;
return std::fread(data, sizeof(T), length, m_file);
}
template <typename T>
size_t WriteArray(const T* data, size_t length) {
static_assert(std::is_trivially_copyable_v<T>,
static_assert(std::is_trivially_copyable<T>(),
"Given array does not consist of trivially copyable objects");
if (!IsOpen()) {
return std::numeric_limits<size_t>::max();
}
if (!IsOpen())
return -1;
return std::fwrite(data, sizeof(T), length, m_file);
}
template <typename T>
size_t ReadBytes(T* data, size_t length) const {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
static_assert(std::is_trivially_copyable<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) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
return WriteArray(reinterpret_cast<const char*>(data), length);
}
template <typename T>
size_t WriteObject(const T& object) {
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
static_assert(!std::is_pointer<T>::value, "Given object is a pointer");
return WriteArray(&object, 1);
}

View File

@@ -28,7 +28,7 @@ static inline u64 ComputeHash64(const void* data, size_t len) {
*/
template <typename T>
static inline u64 ComputeStructHash64(const T& data) {
static_assert(std::is_trivially_copyable_v<T>,
static_assert(std::is_trivially_copyable<T>(),
"Type passed to ComputeStructHash64 must be trivially copyable");
return ComputeHash64(&data, sizeof(data));
}
@@ -38,7 +38,7 @@ template <typename T>
struct HashableStruct {
// In addition to being trivially copyable, T must also have a trivial default constructor,
// because any member initialization would be overridden by memset
static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial");
static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial");
/*
* We use a union because "implicitly-defined copy/move constructor for a union X copies the
* object representation of X." and "implicitly-defined copy assignment operator for a union X

View File

@@ -168,24 +168,17 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, AM) \
SUB(Service, AOC) \
SUB(Service, APM) \
SUB(Service, ARP) \
SUB(Service, BCAT) \
SUB(Service, BPC) \
SUB(Service, BTDRV) \
SUB(Service, BTM) \
SUB(Service, Capture) \
SUB(Service, ERPT) \
SUB(Service, ETicket) \
SUB(Service, EUPLD) \
SUB(Service, Fatal) \
SUB(Service, FGM) \
SUB(Service, Friend) \
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \
SUB(Service, LM) \
SUB(Service, Migration) \
SUB(Service, Mii) \
@@ -194,13 +187,11 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, NFC) \
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
SUB(Service, PM) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, SET) \
@@ -208,7 +199,6 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, Time) \
SUB(Service, USB) \
SUB(Service, VI) \
SUB(Service, WLAN) \
CLS(HW) \
@@ -227,7 +217,6 @@ void FileBackend::Write(const Entry& entry) {
CLS(Input) \
CLS(Network) \
CLS(Loader) \
CLS(Crypto) \
CLS(WebService)
// GetClassName is a macro defined by Windows.h, grrr...

View File

@@ -54,25 +54,18 @@ enum class Class : ClassType {
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
Service_APM, ///< The APM (Performance) service
Service_ARP, ///< The ARP service
Service_Audio, ///< The Audio (Audio control) service
Service_BCAT, ///< The BCAT service
Service_BPC, ///< The BPC service
Service_BTDRV, ///< The Bluetooth driver service
Service_BTM, ///< The BTM service
Service_Capture, ///< The capture service
Service_ERPT, ///< The error reporting service
Service_ETicket, ///< The ETicket service
Service_EUPLD, ///< The error upload service
Service_Fatal, ///< The Fatal service
Service_FGM, ///< The FGM service
Service_Friend, ///< The friend service
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service
Service_LM, ///< The LM (Logger) service
Service_Migration, ///< The migration service
Service_Mii, ///< The Mii service
@@ -81,13 +74,11 @@ enum class Class : ClassType {
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
Service_PM, ///< The PM service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_SET, ///< The SET (Settings) service
@@ -95,7 +86,6 @@ enum class Class : ClassType {
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_Time, ///< The time service
Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service
Service_WLAN, ///< The WLAN (Wireless local area network) service
HW, ///< Low-level hardware emulation
@@ -112,7 +102,6 @@ enum class Class : ClassType {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
Network, ///< Network emulation
WebService, ///< Interface to yuzu Web Services

View File

@@ -42,136 +42,140 @@ class Vec3;
template <typename T>
class Vec4;
template <typename T>
static inline Vec2<T> MakeVec(const T& x, const T& y);
template <typename T>
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z);
template <typename T>
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w);
template <typename T>
class Vec2 {
public:
T x{};
T y{};
constexpr Vec2() = default;
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
Vec2() = default;
Vec2(const T& _x, const T& _y) : x(_x), y(_y) {}
template <typename T2>
constexpr Vec2<T2> Cast() const {
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
Vec2<T2> Cast() const {
return Vec2<T2>((T2)x, (T2)y);
}
static constexpr Vec2 AssignToAll(const T& f) {
return Vec2{f, f};
static Vec2 AssignToAll(const T& f) {
return Vec2<T>(f, f);
}
constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
return {x + other.x, y + other.y};
Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
return MakeVec(x + other.x, y + other.y);
}
constexpr Vec2& operator+=(const Vec2& other) {
void operator+=(const Vec2& other) {
x += other.x;
y += other.y;
return *this;
}
constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return {x - other.x, y - other.y};
Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return MakeVec(x - other.x, y - other.y);
}
constexpr Vec2& operator-=(const Vec2& other) {
void operator-=(const Vec2& other) {
x -= other.x;
y -= other.y;
return *this;
}
template <typename U = T>
constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y};
Vec2<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
return MakeVec(-x, -y);
}
constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
return {x * other.x, y * other.y};
Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
return MakeVec(x * other.x, y * other.y);
}
template <typename V>
constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f};
Vec2<decltype(T{} * V{})> operator*(const V& f) const {
return MakeVec(x * f, y * f);
}
template <typename V>
constexpr Vec2& operator*=(const V& f) {
void operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f};
Vec2<decltype(T{} / V{})> operator/(const V& f) const {
return MakeVec(x / f, y / f);
}
template <typename V>
constexpr Vec2& operator/=(const V& f) {
void operator/=(const V& f) {
*this = *this / f;
return *this;
}
constexpr T Length2() const {
T Length2() const {
return x * x + y * y;
}
// Only implemented for T=float
float Length() const;
void SetLength(const float l);
Vec2 WithLength(const float l) const;
float Distance2To(Vec2& other);
Vec2 Normalized() const;
float Normalize(); // returns the previous length, which is often useful
constexpr T& operator[](std::size_t i) {
T& operator[](int i) // allow vector[1] = 3 (vector.y=3)
{
return *((&x) + i);
}
constexpr const T& operator[](std::size_t i) const {
T operator[](const int i) const {
return *((&x) + i);
}
constexpr void SetZero() {
void SetZero() {
x = 0;
y = 0;
}
// Common aliases: UV (texel coordinates), ST (texture coordinates)
constexpr T& u() {
T& u() {
return x;
}
constexpr T& v() {
T& v() {
return y;
}
constexpr T& s() {
T& s() {
return x;
}
constexpr T& t() {
T& t() {
return y;
}
constexpr const T& u() const {
const T& u() const {
return x;
}
constexpr const T& v() const {
const T& v() const {
return y;
}
constexpr const T& s() const {
const T& s() const {
return x;
}
constexpr const T& t() const {
const T& t() const {
return y;
}
// swizzlers - create a subvector of specific components
constexpr Vec2 yx() const {
const Vec2 yx() const {
return Vec2(y, x);
}
constexpr Vec2 vu() const {
const Vec2 vu() const {
return Vec2(y, x);
}
constexpr Vec2 ts() const {
const Vec2 ts() const {
return Vec2(y, x);
}
};
template <typename T, typename V>
constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
return Vec2<T>(f * vec.x, f * vec.y);
}
using Vec2f = Vec2<float>;
typedef Vec2<float> Vec2f;
template <>
inline float Vec2<float>::Length() const {
@@ -192,151 +196,147 @@ public:
T y{};
T z{};
constexpr Vec3() = default;
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
Vec3() = default;
Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {}
template <typename T2>
constexpr Vec3<T2> Cast() const {
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
Vec3<T2> Cast() const {
return MakeVec<T2>((T2)x, (T2)y, (T2)z);
}
static constexpr Vec3 AssignToAll(const T& f) {
return Vec3(f, f, f);
// Only implemented for T=int and T=float
static Vec3 FromRGB(unsigned int rgb);
unsigned int ToRGB() const; // alpha bits set to zero
static Vec3 AssignToAll(const T& f) {
return MakeVec(f, f, f);
}
constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return {x + other.x, y + other.y, z + other.z};
Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return MakeVec(x + other.x, y + other.y, z + other.z);
}
constexpr Vec3& operator+=(const Vec3& other) {
void operator+=(const Vec3& other) {
x += other.x;
y += other.y;
z += other.z;
return *this;
}
constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return {x - other.x, y - other.y, z - other.z};
Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return MakeVec(x - other.x, y - other.y, z - other.z);
}
constexpr Vec3& operator-=(const Vec3& other) {
void operator-=(const Vec3& other) {
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
}
template <typename U = T>
constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z};
Vec3<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
return MakeVec(-x, -y, -z);
}
constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return {x * other.x, y * other.y, z * other.z};
Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return MakeVec(x * other.x, y * other.y, z * other.z);
}
template <typename V>
constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f};
Vec3<decltype(T{} * V{})> operator*(const V& f) const {
return MakeVec(x * f, y * f, z * f);
}
template <typename V>
constexpr Vec3& operator*=(const V& f) {
void operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f};
Vec3<decltype(T{} / V{})> operator/(const V& f) const {
return MakeVec(x / f, y / f, z / f);
}
template <typename V>
constexpr Vec3& operator/=(const V& f) {
void operator/=(const V& f) {
*this = *this / f;
return *this;
}
constexpr T Length2() const {
T Length2() const {
return x * x + y * y + z * z;
}
// Only implemented for T=float
float Length() const;
void SetLength(const float l);
Vec3 WithLength(const float l) const;
float Distance2To(Vec3& other);
Vec3 Normalized() const;
float Normalize(); // returns the previous length, which is often useful
constexpr T& operator[](std::size_t i) {
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
{
return *((&x) + i);
}
T operator[](const int i) const {
return *((&x) + i);
}
constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
void SetZero() {
x = 0;
y = 0;
z = 0;
}
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
constexpr T& u() {
T& u() {
return x;
}
constexpr T& v() {
T& v() {
return y;
}
constexpr T& w() {
T& w() {
return z;
}
constexpr T& r() {
T& r() {
return x;
}
constexpr T& g() {
T& g() {
return y;
}
constexpr T& b() {
T& b() {
return z;
}
constexpr T& s() {
T& s() {
return x;
}
constexpr T& t() {
T& t() {
return y;
}
constexpr T& q() {
T& q() {
return z;
}
constexpr const T& u() const {
const T& u() const {
return x;
}
constexpr const T& v() const {
const T& v() const {
return y;
}
constexpr const T& w() const {
const T& w() const {
return z;
}
constexpr const T& r() const {
const T& r() const {
return x;
}
constexpr const T& g() const {
const T& g() const {
return y;
}
constexpr const T& b() const {
const T& b() const {
return z;
}
constexpr const T& s() const {
const T& s() const {
return x;
}
constexpr const T& t() const {
const T& t() const {
return y;
}
constexpr const T& q() const {
const T& q() const {
return z;
}
@@ -345,7 +345,7 @@ public:
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
// component names (x<->r) and permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
constexpr Vec2<T> name() const { \
const Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
@@ -366,7 +366,7 @@ public:
};
template <typename T, typename V>
constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
}
@@ -387,7 +387,7 @@ inline float Vec3<float>::Normalize() {
return length;
}
using Vec3f = Vec3<float>;
typedef Vec3<float> Vec3f;
template <typename T>
class Vec4 {
@@ -397,88 +397,86 @@ public:
T z{};
T w{};
constexpr Vec4() = default;
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
: x(x_), y(y_), z(z_), w(w_) {}
Vec4() = default;
Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {}
template <typename T2>
constexpr Vec4<T2> Cast() const {
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
static_cast<T2>(w));
Vec4<T2> Cast() const {
return Vec4<T2>((T2)x, (T2)y, (T2)z, (T2)w);
}
static constexpr Vec4 AssignToAll(const T& f) {
return Vec4(f, f, f, f);
// Only implemented for T=int and T=float
static Vec4 FromRGBA(unsigned int rgba);
unsigned int ToRGBA() const;
static Vec4 AssignToAll(const T& f) {
return Vec4<T>(f, f, f, f);
}
constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return {x + other.x, y + other.y, z + other.z, w + other.w};
Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return MakeVec(x + other.x, y + other.y, z + other.z, w + other.w);
}
constexpr Vec4& operator+=(const Vec4& other) {
void operator+=(const Vec4& other) {
x += other.x;
y += other.y;
z += other.z;
w += other.w;
return *this;
}
constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return {x - other.x, y - other.y, z - other.z, w - other.w};
Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return MakeVec(x - other.x, y - other.y, z - other.z, w - other.w);
}
constexpr Vec4& operator-=(const Vec4& other) {
void operator-=(const Vec4& other) {
x -= other.x;
y -= other.y;
z -= other.z;
w -= other.w;
return *this;
}
template <typename U = T>
constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z, -w};
Vec4<std::enable_if_t<std::is_signed<U>::value, U>> operator-() const {
return MakeVec(-x, -y, -z, -w);
}
constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return {x * other.x, y * other.y, z * other.z, w * other.w};
Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return MakeVec(x * other.x, y * other.y, z * other.z, w * other.w);
}
template <typename V>
constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
return {x * f, y * f, z * f, w * f};
Vec4<decltype(T{} * V{})> operator*(const V& f) const {
return MakeVec(x * f, y * f, z * f, w * f);
}
template <typename V>
constexpr Vec4& operator*=(const V& f) {
void operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
return {x / f, y / f, z / f, w / f};
Vec4<decltype(T{} / V{})> operator/(const V& f) const {
return MakeVec(x / f, y / f, z / f, w / f);
}
template <typename V>
constexpr Vec4& operator/=(const V& f) {
void operator/=(const V& f) {
*this = *this / f;
return *this;
}
constexpr T Length2() const {
T Length2() const {
return x * x + y * y + z * z + w * w;
}
constexpr T& operator[](std::size_t i) {
// Only implemented for T=float
float Length() const;
void SetLength(const float l);
Vec4 WithLength(const float l) const;
float Distance2To(Vec4& other);
Vec4 Normalized() const;
float Normalize(); // returns the previous length, which is often useful
T& operator[](int i) // allow vector[2] = 3 (vector.z=3)
{
return *((&x) + i);
}
T operator[](const int i) const {
return *((&x) + i);
}
constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
void SetZero() {
x = 0;
y = 0;
z = 0;
@@ -486,29 +484,29 @@ public:
}
// Common alias: RGBA (colors)
constexpr T& r() {
T& r() {
return x;
}
constexpr T& g() {
T& g() {
return y;
}
constexpr T& b() {
T& b() {
return z;
}
constexpr T& a() {
T& a() {
return w;
}
constexpr const T& r() const {
const T& r() const {
return x;
}
constexpr const T& g() const {
const T& g() const {
return y;
}
constexpr const T& b() const {
const T& b() const {
return z;
}
constexpr const T& a() const {
const T& a() const {
return w;
}
@@ -520,7 +518,7 @@ public:
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
// permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
constexpr Vec2<T> name() const { \
const Vec2<T> name() const { \
return Vec2<T>(a, b); \
}
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
@@ -547,7 +545,7 @@ public:
#undef _DEFINE_SWIZZLER2
#define _DEFINE_SWIZZLER3(a, b, c, name) \
constexpr Vec3<T> name() const { \
const Vec3<T> name() const { \
return Vec3<T>(a, b, c); \
}
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
@@ -581,51 +579,51 @@ public:
};
template <typename T, typename V>
constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
return MakeVec(f * vec.x, f * vec.y, f * vec.z, f * vec.w);
}
using Vec4f = Vec4<float>;
typedef Vec4<float> Vec4f;
template <typename T>
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
return a.x * b.x + a.y * b.y;
}
template <typename T>
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
template <typename T>
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
static inline decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}
template <typename T>
constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
static inline Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a, const Vec3<T>& b) {
return MakeVec(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
const float t) {
static inline decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
const float t) {
return begin * (1.f - t) + end * t;
}
// linear interpolation via int: 0=begin, base=end
template <typename X, int base>
constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
const int t) {
static inline decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin, const X& end,
const int t) {
return (begin * (base - t) + end * t) / base;
}
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
// interpolation.
template <typename X>
constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
const float t) {
inline auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11, const float s,
const float t) {
auto y0 = Lerp(x00, x01, s);
auto y1 = Lerp(x10, x11, s);
return Lerp(y0, y1, t);
@@ -633,42 +631,42 @@ constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X&
// Utility vector factories
template <typename T>
constexpr Vec2<T> MakeVec(const T& x, const T& y) {
static inline Vec2<T> MakeVec(const T& x, const T& y) {
return Vec2<T>{x, y};
}
template <typename T>
constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
static inline Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
return Vec3<T>{x, y, z};
}
template <typename T>
constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
static inline Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
return MakeVec(x, y, zw[0], zw[1]);
}
template <typename T>
constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
static inline Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
return MakeVec(xy[0], xy[1], z);
}
template <typename T>
constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
static inline Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
return MakeVec(x, yz[0], yz[1]);
}
template <typename T>
constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
static inline Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
return Vec4<T>{x, y, z, w};
}
template <typename T>
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
return MakeVec(xy[0], xy[1], z, w);
}
template <typename T>
constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
static inline Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
return MakeVec(x, yz[0], yz[1], w);
}
@@ -676,17 +674,17 @@ constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
// out soon enough due to misuse of the returned structure.
template <typename T>
constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
static inline Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
}
template <typename T>
constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
static inline Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
return MakeVec(xyz[0], xyz[1], xyz[2], w);
}
template <typename T>
constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
static inline Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
}

View File

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

View File

@@ -12,16 +12,6 @@ add_library(core STATIC
core_timing.h
core_timing_util.cpp
core_timing_util.h
crypto/aes_util.cpp
crypto/aes_util.h
crypto/encryption_layer.cpp
crypto/encryption_layer.h
crypto/key_manager.cpp
crypto/key_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -73,6 +63,8 @@ add_library(core STATIC
hle/kernel/hle_ipc.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory.cpp
hle/kernel/memory.h
hle/kernel/mutex.cpp
hle/kernel/mutex.h
hle/kernel/object.cpp
@@ -104,6 +96,8 @@ add_library(core STATIC
hle/lock.cpp
hle/lock.h
hle/result.h
hle/romfs.cpp
hle/romfs.h
hle/service/acc/acc.cpp
hle/service/acc/acc.h
hle/service/acc/acc_aa.cpp
@@ -132,8 +126,6 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
hle/service/arp/arp.cpp
hle/service/arp/arp.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp
@@ -313,8 +305,6 @@ add_library(core STATIC
hle/service/time/interface.h
hle/service/time/time.cpp
hle/service/time/time.h
hle/service/usb/usb.cpp
hle/service/usb/usb.h
hle/service/vi/vi.cpp
hle/service/vi/vi.h
hle/service/vi/vi_m.cpp
@@ -339,8 +329,6 @@ add_library(core STATIC
loader/nro.h
loader/nso.cpp
loader/nso.h
loader/xci.cpp
loader/xci.h
memory.cpp
memory.h
memory_hook.cpp
@@ -360,7 +348,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 opus unicorn)
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE

View File

@@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
if (last_bkpt_hit || (num_instructions == 1)) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);

View File

@@ -18,7 +18,6 @@
#include "core/loader/loader.h"
#include "core/settings.h"
#include "file_sys/vfs_real.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
@@ -62,6 +61,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
GDBStub::SetCpuStepFlag(false);
tight_loop = false;
} else {
return ResultStatus::Success;
@@ -77,10 +77,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
@@ -89,7 +85,7 @@ System::ResultStatus System::SingleStep() {
}
System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -103,10 +99,8 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
static_cast<int>(system_mode.second));
switch (system_mode.second) {
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -116,7 +110,7 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
}
}
ResultStatus init_result{Init(emu_window)};
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
@@ -130,10 +124,8 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
System::Shutdown();
switch (load_result) {
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorEncrypted:
return ResultStatus::ErrorLoader_ErrorEncrypted;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -169,15 +161,11 @@ Cpu& System::CpuCore(size_t core_index) {
return *cpu_cores[core_index];
}
System::ResultStatus System::Init(EmuWindow& emu_window) {
System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
@@ -186,20 +174,18 @@ System::ResultStatus System::Init(EmuWindow& emu_window) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
gpu_core = std::make_unique<Tegra::GPU>();
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Kernel::Init();
Service::Init(service_manager, virtual_filesystem);
Kernel::Init(system_mode);
Service::Init(service_manager);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
if (!VideoCore::Init(emu_window)) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -231,7 +217,7 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
VideoCore::Shutdown();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();

View File

@@ -17,8 +17,6 @@
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "file_sys/vfs_real.h"
#include "hle/service/filesystem/filesystem.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
@@ -29,10 +27,6 @@ namespace Service::SM {
class ServiceManager;
}
namespace VideoCore {
class RendererBase;
}
namespace Core {
class System {
@@ -49,14 +43,12 @@ public:
/// Enumeration representing the return values of the System Initialize and Load process.
enum class ResultStatus : u32 {
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorLoader, ///< Error loading the specified application
ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be
///< found.
ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorLoader, ///< Error loading the specified application
ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
/// invalid format
ErrorSystemFiles, ///< Error in finding system files
@@ -84,17 +76,6 @@ public:
*/
ResultStatus SingleStep();
/**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
*/
void InvalidateCpuInstructionCaches() {
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
/// Shutdown the emulated system.
void Shutdown();
@@ -146,26 +127,11 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index);
/// Gets a mutable reference to the GPU interface
/// Gets the GPU interface
Tegra::GPU& GPU() {
return *gpu_core;
}
/// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const {
return *gpu_core;
}
/// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer() {
return *renderer;
}
/// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
@@ -213,14 +179,6 @@ public:
return debug_context;
}
void SetFilesystem(FileSys::VirtualFilesystem vfs) {
virtual_filesystem = std::move(vfs);
}
FileSys::VirtualFilesystem GetFilesystem() const {
return virtual_filesystem;
}
private:
System();
@@ -231,15 +189,13 @@ private:
* Initialize the emulated system.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @param system_mode The system mode.
* @return ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Init(EmuWindow& emu_window);
ResultStatus Init(EmuWindow& emu_window, u32 system_mode);
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;

View File

@@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
ForceExceptionCheck(cycles_into_future);
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
@@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
}
@@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) {
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
}
@@ -190,7 +190,7 @@ void MoveEvents() {
for (Event ev; ts_queue.Pop(ev);) {
ev.fifo_order = event_fifo_id++;
event_queue.emplace_back(std::move(ev));
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
}
}
@@ -205,7 +205,7 @@ void Advance() {
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
event_queue.pop_back();
evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
}
@@ -226,8 +226,8 @@ void Idle() {
downcount = 0;
}
std::chrono::microseconds GetGlobalTimeUs() {
return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
u64 GetGlobalTimeUs() {
return GetTicks() * 1000000 / BASE_CLOCK_RATE;
}
int GetDowncount() {

View File

@@ -17,17 +17,12 @@
* ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
*/
#include <chrono>
#include <functional>
#include <string>
#include "common/common_types.h"
namespace CoreTiming {
struct EventType;
using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
/**
* CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
* required to end slice -1 and start slice 0 before the first cycle of code is executed.
@@ -35,6 +30,8 @@ using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
void Init();
void Shutdown();
typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
/**
* This should only be called from the emu thread, if you are calling it any other thread, you are
* doing something evil
@@ -43,6 +40,8 @@ u64 GetTicks();
u64 GetIdleTicks();
void AddTicks(u64 ticks);
struct EventType;
/**
* Returns the event_type identifier. if name is not unique, it will assert.
*/
@@ -87,7 +86,7 @@ void ClearPendingEvents();
void ForceExceptionCheck(s64 cycles);
std::chrono::microseconds GetGlobalTimeUs();
u64 GetGlobalTimeUs();
int GetDowncount();

View File

@@ -1,115 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <mbedtls/cipher.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
namespace Core::Crypto {
namespace {
std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
std::vector<u8> out(0x10);
for (size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
}
return out;
}
} // Anonymous namespace
static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
"CTR has incorrect value.");
static_assert(static_cast<size_t>(Mode::ECB) == static_cast<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),
"XTS has incorrect value.");
// Structure to hide mbedtls types from header file
struct CipherContext {
mbedtls_cipher_context_t encryption_context;
mbedtls_cipher_context_t decryption_context;
};
template <typename Key, size_t KeySize>
Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
: ctx(std::make_unique<CipherContext>()) {
mbedtls_cipher_init(&ctx->encryption_context);
mbedtls_cipher_init(&ctx->decryption_context);
ASSERT_MSG((mbedtls_cipher_setup(
&ctx->encryption_context,
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) ||
mbedtls_cipher_setup(
&ctx->decryption_context,
mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0,
"Failed to initialize mbedtls ciphers.");
ASSERT(
!mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT));
ASSERT(
!mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT));
//"Failed to set key on mbedtls ciphers.");
}
template <typename Key, size_t KeySize>
AESCipher<Key, KeySize>::~AESCipher() {
mbedtls_cipher_free(&ctx->encryption_context);
mbedtls_cipher_free(&ctx->decryption_context);
}
template <typename Key, 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 {
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
mbedtls_cipher_reset(context);
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) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
size, written);
}
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) {
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
}
}
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) {
if (size % sector_size > 0) {
LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size.");
return;
}
for (size_t i = 0; i < size; i += sector_size) {
SetIV(CalculateNintendoTweak(sector_id++));
Transcode<u8, u8>(src + i, sector_size, dest + i, op);
}
}
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto

View File

@@ -1,64 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <type_traits>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
struct CipherContext;
enum class Mode {
CTR = 11,
ECB = 2,
XTS = 70,
};
enum class Op {
Encrypt,
Decrypt,
};
template <typename Key, size_t KeySize = sizeof(Key)>
class AESCipher {
static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8.");
static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
public:
AESCipher(Key key, Mode mode);
~AESCipher();
void SetIV(std::vector<u8> iv);
template <typename Source, typename Dest>
void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
"Transcode source and destination types must be trivially copyable.");
Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
}
void Transcode(const u8* src, 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) {
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);
private:
std::unique_ptr<CipherContext> ctx;
};
} // namespace Core::Crypto

View File

@@ -1,56 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "core/crypto/ctr_encryption_layer.h"
namespace Core::Crypto {
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset)
: EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
iv(16, 0) {}
size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length == 0)
return 0;
const auto sector_offset = offset & 0xF;
if (sector_offset == 0) {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
if (raw.size() != length)
return Read(data, raw.size(), offset);
cipher.Transcode(raw.data(), length, data, Op::Decrypt);
return length;
}
// offset does not fall on block boundary (0x10)
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;
if (length + sector_offset < 0x10) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return read;
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);
}
void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
const auto length = std::min(iv_.size(), iv.size());
iv.assign(iv_.cbegin(), iv_.cbegin() + length);
}
void CTREncryptionLayer::UpdateIV(size_t offset) const {
offset >>= 4;
for (size_t i = 0; i < 8; ++i) {
iv[16 - i - 1] = offset & 0xFF;
offset >>= 8;
}
cipher.SetIV(iv);
}
} // namespace Core::Crypto

View File

@@ -1,33 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "core/crypto/aes_util.h"
#include "core/crypto/encryption_layer.h"
#include "core/crypto/key_manager.h"
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);
size_t Read(u8* data, size_t length, size_t offset) const override;
void SetIV(const std::vector<u8>& iv);
private:
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;
};
} // namespace Core::Crypto

View File

@@ -1,42 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/crypto/encryption_layer.h"
namespace Core::Crypto {
EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {}
std::string EncryptionLayer::GetName() const {
return base->GetName();
}
size_t EncryptionLayer::GetSize() const {
return base->GetSize();
}
bool EncryptionLayer::Resize(size_t new_size) {
return false;
}
std::shared_ptr<FileSys::VfsDirectory> EncryptionLayer::GetContainingDirectory() const {
return base->GetContainingDirectory();
}
bool EncryptionLayer::IsWritable() const {
return false;
}
bool EncryptionLayer::IsReadable() const {
return true;
}
size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) {
return 0;
}
bool EncryptionLayer::Rename(std::string_view name) {
return base->Rename(name);
}
} // namespace Core::Crypto

View File

@@ -1,33 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Core::Crypto {
// Basically non-functional class that implements all of the methods that are irrelevant to an
// EncryptionLayer. Reduces duplicate code.
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::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
protected:
FileSys::VirtualFile base;
};
} // namespace Core::Crypto

View File

@@ -1,208 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <fstream>
#include <locale>
#include <sstream>
#include <string_view>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/crypto/key_manager.h"
#include "core/settings.h"
namespace Core::Crypto {
static u8 ToHexNibble(char c1) {
if (c1 >= 65 && c1 <= 70)
return c1 - 55;
if (c1 >= 97 && c1 <= 102)
return c1 - 87;
if (c1 >= 48 && c1 <= 57)
return c1 - 48;
throw std::logic_error("Invalid hex digit");
}
template <size_t Size>
static std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
for (size_t i = 0; i < 2 * Size; i += 2) {
auto d1 = str[i];
auto d2 = str[i + 1];
out[i / 2] = (ToHexNibble(d1) << 4) | ToHexNibble(d2);
}
return out;
}
std::array<u8, 16> operator""_array16(const char* str, size_t len) {
if (len != 32)
throw std::logic_error("Not of correct size.");
return HexStringToArray<16>(str);
}
std::array<u8, 32> operator""_array32(const char* str, size_t len) {
if (len != 64)
throw std::logic_error("Not of correct size.");
return HexStringToArray<32>(str);
}
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
} else {
dev_mode = false;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false);
}
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
std::ifstream file(filename);
if (!file.is_open())
return;
std::string line;
while (std::getline(file, line)) {
std::vector<std::string> out;
std::stringstream stream(line);
std::string item;
while (std::getline(stream, item, '='))
out.push_back(std::move(item));
if (out.size() != 2)
continue;
out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
if (is_title_keys) {
auto rights_id_raw = HexStringToArray<16>(out[0]);
u128 rights_id{};
std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
Key128 key = HexStringToArray<16>(out[1]);
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
} else {
std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
if (s128_file_id.find(out[0]) != s128_file_id.end()) {
const auto index = s128_file_id.at(out[0]);
Key128 key = HexStringToArray<16>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
} else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
const auto index = s256_file_id.at(out[0]);
Key256 key = HexStringToArray<32>(out[1]);
SetKey(index.type, key, index.field1, index.field2);
}
}
}
}
void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title) {
if (FileUtil::Exists(dir1 + DIR_SEP + filename))
LoadFromFile(dir1 + DIR_SEP + filename, title);
else if (FileUtil::Exists(dir2 + DIR_SEP + filename))
LoadFromFile(dir2 + DIR_SEP + filename, title);
}
bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
return s128_keys.find({id, field1, field2}) != s128_keys.end();
}
bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.find({id, field1, field2}) != s256_keys.end();
}
Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
if (!HasKey(id, field1, field2))
return {};
return s128_keys.at({id, field1, field2});
}
Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
if (!HasKey(id, field1, field2))
return {};
return s256_keys.at({id, field1, field2});
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
s128_keys[{id, field1, field2}] = key;
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
s256_keys[{id, field1, field2}] = key;
}
bool KeyManager::KeyFileExists(bool title) {
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
if (title) {
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
}
if (Settings::values.use_dev_keys) {
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
}
return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
}
const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"master_key_00", {S128KeyType::Master, 0, 0}},
{"master_key_01", {S128KeyType::Master, 1, 0}},
{"master_key_02", {S128KeyType::Master, 2, 0}},
{"master_key_03", {S128KeyType::Master, 3, 0}},
{"master_key_04", {S128KeyType::Master, 4, 0}},
{"package1_key_00", {S128KeyType::Package1, 0, 0}},
{"package1_key_01", {S128KeyType::Package1, 1, 0}},
{"package1_key_02", {S128KeyType::Package1, 2, 0}},
{"package1_key_03", {S128KeyType::Package1, 3, 0}},
{"package1_key_04", {S128KeyType::Package1, 4, 0}},
{"package2_key_00", {S128KeyType::Package2, 0, 0}},
{"package2_key_01", {S128KeyType::Package2, 1, 0}},
{"package2_key_02", {S128KeyType::Package2, 2, 0}},
{"package2_key_03", {S128KeyType::Package2, 3, 0}},
{"package2_key_04", {S128KeyType::Package2, 4, 0}},
{"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
{"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
{"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
{"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
{"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
{"key_area_key_application_00",
{S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_01",
{S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_02",
{S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_03",
{S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_04",
{S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
};
const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key", {S256KeyType::SDSave, 0, 0}},
{"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}},
};
} // namespace Core::Crypto

View File

@@ -1,120 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
namespace Core::Crypto {
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
enum class S256KeyType : u64 {
Header, //
SDSave, //
SDNCA, //
};
enum class S128KeyType : u64 {
Master, // f1=crypto revision
Package1, // f1=crypto revision
Package2, // f1=crypto revision
Titlekek, // f1=crypto revision
ETicketRSAKek, //
KeyArea, // f1=crypto revision f2=type {app, ocean, system}
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
};
enum class KeyAreaKeyType : u8 {
Application,
Ocean,
System,
};
template <typename KeyType>
struct KeyIndex {
KeyType type;
u64 field1;
u64 field2;
std::string DebugInfo() const {
u8 key_size = 16;
if constexpr (std::is_same_v<KeyType, S256KeyType>)
key_size = 32;
return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size,
static_cast<u8>(type), field1, field2);
}
};
// The following two (== and hash) are so KeyIndex can be a key in unordered_map
template <typename KeyType>
bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2);
}
template <typename KeyType>
bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
return !operator==(lhs, rhs);
}
} // namespace Core::Crypto
namespace std {
template <typename KeyType>
struct hash<Core::Crypto::KeyIndex<KeyType>> {
size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const {
using std::hash;
return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^
(hash<u64>()(k.field2) << 1);
}
};
} // namespace std
namespace Core::Crypto {
std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);
class KeyManager {
public:
KeyManager();
bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
static bool KeyFileExists(bool title);
private:
std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
} // namespace Core::Crypto

View File

@@ -1,5 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace Crypto {} // namespace Crypto

View File

@@ -1,20 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/assert.h"
#include "core/file_sys/vfs.h"
#include "key_manager.h"
#include "mbedtls/cipher.h"
namespace Crypto {
typedef std::array<u8, 0x20> SHA256Hash;
inline SHA256Hash operator"" _HASH(const char* data, size_t len) {
if (len != 0x40)
return {};
}
} // namespace Crypto

View File

@@ -1,150 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <string>
#include <core/loader/loader.h>
#include "common/logging/log.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"
namespace FileSys {
XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
PartitionFilesystem main_hfs(
std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset));
if (main_hfs.GetStatus() != Loader::ResultStatus::Success) {
status = main_hfs.GetStatus();
return;
}
static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure",
"logo"};
for (XCIPartition partition :
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
if (raw != nullptr)
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
}
auto result = AddNCAFromPartition(XCIPartition::Secure);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
result = AddNCAFromPartition(XCIPartition::Update);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
result = AddNCAFromPartition(XCIPartition::Normal);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
if (GetFormatVersion() >= 0x2) {
result = AddNCAFromPartition(XCIPartition::Logo);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
}
status = Loader::ResultStatus::Success;
}
Loader::ResultStatus XCI::GetStatus() const {
return status;
}
VirtualDir XCI::GetPartition(XCIPartition partition) const {
return partitions[static_cast<size_t>(partition)];
}
VirtualDir XCI::GetSecurePartition() const {
return GetPartition(XCIPartition::Secure);
}
VirtualDir XCI::GetNormalPartition() const {
return GetPartition(XCIPartition::Normal);
}
VirtualDir XCI::GetUpdatePartition() const {
return GetPartition(XCIPartition::Update);
}
VirtualDir XCI::GetLogoPartition() const {
return GetPartition(XCIPartition::Logo);
}
std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
const auto iter =
std::find_if(ncas.begin(), ncas.end(),
[type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; });
return iter == ncas.end() ? nullptr : *iter;
}
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
auto nca = GetNCAByType(type);
if (nca != nullptr)
return nca->GetBaseFile();
return nullptr;
}
std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const {
return {};
}
std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const {
return std::vector<std::shared_ptr<VfsDirectory>>();
}
std::string XCI::GetName() const {
return file->GetName();
}
std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (partitions[static_cast<size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorInvalidFormat;
}
for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
continue;
auto nca = std::make_shared<NCA>(file);
if (nca->GetStatus() == Loader::ResultStatus::Success)
ncas.push_back(std::move(nca));
}
return Loader::ResultStatus::Success;
}
u8 XCI::GetFormatVersion() const {
return GetLogoPartition() == nullptr ? 0x1 : 0x2;
}
} // namespace FileSys

View File

@@ -1,96 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
enum class GamecardSize : u8 {
S_1GB = 0xFA,
S_2GB = 0xF8,
S_4GB = 0xF0,
S_8GB = 0xE0,
S_16GB = 0xE1,
S_32GB = 0xE2,
};
struct GamecardInfo {
std::array<u8, 0x70> data;
};
static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
struct GamecardHeader {
std::array<u8, 0x100> signature;
u32_le magic;
u32_le secure_area_start;
u32_le backup_area_start;
u8 kek_index;
GamecardSize size;
u8 header_version;
u8 flags;
u64_le package_id;
u64_le valid_data_end;
u128 info_iv;
u64_le hfs_offset;
u64_le hfs_size;
std::array<u8, 0x20> hfs_header_hash;
std::array<u8, 0x20> initial_data_hash;
u32_le secure_mode_flag;
u32_le title_key_flag;
u32_le key_flag;
u32_le normal_area_end;
GamecardInfo info;
};
static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size.");
enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
class XCI : public ReadOnlyVfsDirectory {
public:
explicit XCI(VirtualFile file);
Loader::ResultStatus GetStatus() const;
u8 GetFormatVersion() const;
VirtualDir GetPartition(XCIPartition partition) const;
VirtualDir GetSecurePartition() const;
VirtualDir GetNormalPartition() const;
VirtualDir GetUpdatePartition() const;
VirtualDir GetLogoPartition() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
VirtualFile GetNCAFileByType(NCAContentType type) const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
VirtualFile file;
GamecardHeader header{};
Loader::ResultStatus status;
std::vector<VirtualDir> partitions;
std::vector<std::shared_ptr<NCA>> ncas;
};
} // namespace FileSys

View File

@@ -4,14 +4,12 @@
#include <algorithm>
#include <utility>
#include <boost/optional.hpp>
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
#include "romfs.h"
namespace FileSys {
@@ -31,19 +29,11 @@ enum class NCASectionFilesystemType : u8 {
struct NCASectionHeaderBlock {
INSERT_PADDING_BYTES(3);
NCASectionFilesystemType filesystem_type;
NCASectionCryptoType crypto_type;
u8 crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
struct NCASectionRaw {
NCASectionHeaderBlock header;
std::array<u8, 0x138> block_data;
std::array<u8, 0x8> section_ctr;
INSERT_PADDING_BYTES(0xB8);
};
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
struct PFS0Superblock {
NCASectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
@@ -53,204 +43,67 @@ struct PFS0Superblock {
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_PADDING_BYTES(0x1B0);
INSERT_PADDING_BYTES(432);
};
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
struct RomFSSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_PADDING_BYTES(0x118);
};
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
union NCASectionHeader {
NCASectionRaw raw;
PFS0Superblock pfs0;
RomFSSuperblock romfs;
};
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
bool IsValidNCA(const NCAHeader& header) {
// TODO(DarkLordZach): Add NCA2/NCA0 support.
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
u8 NCA::GetCryptoRevision() const {
u8 master_key_id = header.crypto_type;
if (header.crypto_type_2 > master_key_id)
master_key_id = header.crypto_type_2;
if (master_key_id > 0)
--master_key_id;
return master_key_id;
}
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
const auto master_key_id = GetCryptoRevision();
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
return boost::none;
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index),
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
Core::Crypto::Key128 out;
if (type == NCASectionCryptoType::XTS)
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
else if (type == NCASectionCryptoType::CTR)
std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
else
LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
static_cast<u8>(type));
u128 out_128{};
memcpy(out_128.data(), out.data(), 16);
LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
master_key_id, header.key_index, out_128[1], out_128[0]);
return out;
}
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const {
const auto master_key_id = GetCryptoRevision();
u128 rights_id{};
memcpy(rights_id.data(), header.rights_id.data(), 16);
if (rights_id == u128{})
return boost::none;
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
if (titlekey == Core::Crypto::Key128{})
return boost::none;
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
return titlekey;
}
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const {
if (!encrypted)
return in;
switch (s_header.raw.header.crypto_type) {
case NCASectionCryptoType::NONE:
LOG_DEBUG(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
boost::optional<Core::Crypto::Key128> key = boost::none;
if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
[](char c) { return c == 0; }) == header.rights_id.end()) {
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
} else {
key = GetTitlekey();
}
if (key == boost::none)
return nullptr;
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
std::move(in), key.value(), starting_offset);
std::vector<u8> iv(16);
for (u8 i = 0; i < 8; ++i)
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
out->SetIV(iv);
return std::static_pointer_cast<VfsFile>(out);
}
case NCASectionCryptoType::XTS:
// TODO(DarkLordZach): Implement XTSEncryptionLayer.
default:
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
static_cast<u8>(s_header.raw.header.crypto_type));
return nullptr;
}
}
static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size.");
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
if (file == nullptr) {
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (!IsValidNCA(header)) {
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_ERROR(Loader, "File reader errored out during header read.");
encrypted = false;
if (!IsValidNCA(header)) {
NCAHeader dec_header{};
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
Core::Crypto::Op::Decrypt);
if (IsValidNCA(dec_header)) {
header = dec_header;
encrypted = true;
} else {
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
status = Loader::ResultStatus::ErrorMissingKeys;
else
status = Loader::ResultStatus::ErrorDecrypting;
return;
}
}
const std::ptrdiff_t number_sections =
std::ptrdiff_t number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
std::vector<NCASectionHeader> sections(number_sections);
const auto length_sections = SECTION_HEADER_SIZE * number_sections;
if (encrypted) {
auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
Core::Crypto::Op::Decrypt);
} else {
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
}
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
auto section = sections[i];
// Seek to beginning of this section.
NCASectionHeaderBlock block{};
if (sizeof(NCASectionHeaderBlock) !=
file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
RomFSSuperblock sb{};
if (sizeof(RomFSSuperblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset),
romfs_offset);
if (dec != nullptr) {
files.push_back(std::move(dec));
romfs = files.back();
} else {
status = Loader::ResultStatus::ErrorMissingKeys;
return;
}
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
sb.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = sb.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset));
romfs = files.back();
} else if (block.filesystem_type == NCASectionFilesystemType::PFS0) {
PFS0Superblock sb{};
// Seek back to beginning of this section.
if (sizeof(PFS0Superblock) !=
file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
LOG_CRITICAL(Loader, "File reader errored out during header read.");
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
section.pfs0.pfs0_header_offset;
sb.pfs0_header_offset;
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
if (dec != nullptr) {
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
auto npfs = std::make_shared<PartitionFilesystem>(
std::make_shared<OffsetVfsFile>(file, size, offset));
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
}
} else {
status = Loader::ResultStatus::ErrorMissingKeys;
return;
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.emplace_back(npfs);
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
}
}
}
@@ -300,10 +153,6 @@ VirtualDir NCA::GetExeFS() const {
return exefs;
}
VirtualFile NCA::GetBaseFile() const {
return file;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}

View File

@@ -8,19 +8,14 @@
#include <memory>
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "control_metadata.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
union NCASectionHeader;
enum class NCAContentType : u8 {
Program = 0,
Meta = 1,
@@ -29,13 +24,6 @@ enum class NCAContentType : u8 {
Data = 4,
};
enum class NCASectionCryptoType : u8 {
NONE = 1,
XTS = 2,
CTR = 3,
BKTR = 4,
};
struct NCASectionTableEntry {
u32_le media_offset;
u32_le media_end_offset;
@@ -60,7 +48,7 @@ struct NCAHeader {
std::array<u8, 0x10> rights_id;
std::array<NCASectionTableEntry, 0x4> section_tables;
std::array<std::array<u8, 0x20>, 0x4> hash_tables;
std::array<u8, 0x40> key_area;
std::array<std::array<u8, 0x10>, 0x4> key_area;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
@@ -70,7 +58,10 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
bool IsValidNCA(const NCAHeader& header);
inline bool IsValidNCA(const NCAHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
@@ -90,17 +81,10 @@ public:
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
VirtualFile GetBaseFile() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
u8 GetCryptoRevision() const;
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
boost::optional<Core::Crypto::Key128> GetTitlekey() const;
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const;
std::vector<VirtualDir> dirs;
std::vector<VirtualFile> files;
@@ -111,10 +95,6 @@ private:
NCAHeader header{};
Loader::ResultStatus status{};
bool encrypted;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -62,13 +62,6 @@ enum class Language : u8 {
Chinese = 14,
};
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
"AmericanEnglish", "BritishEnglish", "Japanese",
"French", "German", "LatinAmericanSpanish",
"Spanish", "Italian", "Dutch",
"CanadianFrench", "Portugese", "Russian",
"Korean", "Taiwanese", "Chinese"};
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {

View File

@@ -4,9 +4,8 @@
#pragma once
#include <array>
#include <cstddef>
#include <iterator>
#include <string_view>
#include "common/common_funcs.h"
#include "common/common_types.h"
@@ -22,14 +21,9 @@ enum EntryType : u8 {
// Structure of a directory entry, from
// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry
const size_t FILENAME_LENGTH = 0x300;
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);
filename[copy_size] = '\0';
}
char filename[0x300];
char filename[FILENAME_LENGTH];
INSERT_PADDING_BYTES(4);
EntryType type;
INSERT_PADDING_BYTES(3);

View File

@@ -7,7 +7,6 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
namespace FileSys {

View File

@@ -4,160 +4,12 @@
#include <algorithm>
#include <numeric>
#include <string>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
VfsFilesystem::~VfsFilesystem() = default;
std::string VfsFilesystem::GetName() const {
return root->GetName();
}
bool VfsFilesystem::IsReadable() const {
return root->IsReadable();
}
bool VfsFilesystem::IsWritable() const {
return root->IsWritable();
}
VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
const auto path = FileUtil::SanitizePath(path_);
if (root->GetFileRelative(path) != nullptr)
return VfsEntryType::File;
if (root->GetDirectoryRelative(path) != nullptr)
return VfsEntryType::Directory;
return VfsEntryType::None;
}
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->GetFileRelative(path);
}
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->CreateFileRelative(path);
}
VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// VfsDirectory impls are only required to implement copy across the current directory.
if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) {
if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path)))
return nullptr;
return OpenFile(new_path, Mode::ReadWrite);
}
// Do it using RawCopy. Non-default impls are encouraged to optimize this.
const auto old_file = OpenFile(old_path, Mode::Read);
if (old_file == nullptr)
return nullptr;
auto new_file = OpenFile(new_path, Mode::Read);
if (new_file != nullptr)
return nullptr;
new_file = CreateFile(new_path, Mode::Write);
if (new_file == nullptr)
return nullptr;
if (!VfsRawCopy(old_file, new_file))
return nullptr;
return new_file;
}
VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Again, non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyFile(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteFile(old_path))
return out;
return nullptr;
}
bool VfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_);
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
return parent->DeleteFile(FileUtil::GetFilename(path));
}
VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->GetDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_);
return root->CreateDirectoryRelative(path);
}
VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto old_dir = OpenDirectory(old_path, Mode::Read);
if (old_dir == nullptr)
return nullptr;
auto new_dir = OpenDirectory(new_path, Mode::Read);
if (new_dir != nullptr)
return nullptr;
new_dir = CreateDirectory(new_path, Mode::Write);
if (new_dir == nullptr)
return nullptr;
for (const auto& file : old_dir->GetFiles()) {
const auto x =
CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName());
if (x == nullptr)
return nullptr;
}
for (const auto& dir : old_dir->GetSubdirectories()) {
const auto x =
CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName());
if (x == nullptr)
return nullptr;
}
return new_dir;
}
VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) {
const auto old_path = FileUtil::SanitizePath(old_path_);
const auto new_path = FileUtil::SanitizePath(new_path_);
// Non-default impls are highly encouraged to provide a more optimized version of this.
auto out = CopyDirectory(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteDirectory(old_path))
return out;
return nullptr;
}
bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_);
auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write);
if (parent == nullptr)
return false;
return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path));
}
VfsFile::~VfsFile() = default;
std::string VfsFile::GetExtension() const {
@@ -433,26 +285,6 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
return false;
}
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, 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) {
auto f1_vs = file1->Read(f1_v.data(), block_size, i);
auto f2_vs = file2->Read(f2_v.data(), block_size, i);
if (f1_vs != f2_vs)
return false;
auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end());
if (iters.first != f1_v.end() && iters.second != f2_v.end())
return false;
}
return true;
}
bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
if (src == nullptr || dest == nullptr)
return false;

View File

@@ -11,74 +11,14 @@
#include <vector>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "core/file_sys/mode.h"
namespace FileSys {
struct VfsFilesystem;
struct VfsFile;
struct VfsDirectory;
// Convenience typedefs to use Vfs* interfaces
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
None,
File,
Directory,
};
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
// is provided for convenience, but if the Vfs implementation has any additional state or
// functionality, they will need to override.
struct VfsFilesystem : NonCopyable {
VfsFilesystem(VirtualDir root);
virtual ~VfsFilesystem();
// Gets the friendly name for the filesystem.
virtual std::string GetName() const;
// Return whether or not the user has read permissions on this filesystem.
virtual bool IsReadable() const;
// Return whether or not the user has write permission on this filesystem.
virtual bool IsWritable() const;
// Determine if the entry at path is non-existant, a file, or a directory.
virtual VfsEntryType GetEntryType(std::string_view path) const;
// Opens the file with path relative to root. If it doesn't exist, returns nullptr.
virtual VirtualFile OpenFile(std::string_view path, Mode perms);
// Creates a new, empty file at path
virtual VirtualFile CreateFile(std::string_view path, Mode perms);
// Copies the file from old_path to new_path, returning the new file on success and nullptr on
// failure.
virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
// Moves the file from old_path to new_path, returning the moved file on success and nullptr on
// failure.
virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
// Deletes the file with path relative to root, returing true on success.
virtual bool DeleteFile(std::string_view path);
// Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
virtual VirtualDir OpenDirectory(std::string_view path, Mode perms);
// Creates a new, empty directory at path
virtual VirtualDir CreateDirectory(std::string_view path, Mode perms);
// Copies the directory from old_path to new_path, returning the new directory on success and
// nullptr on failure.
virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
// Moves the directory from old_path to new_path, returning the moved directory on success and
// nullptr on failure.
virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
// Deletes the directory with path relative to root, returing true on success.
virtual bool DeleteDirectory(std::string_view path);
protected:
// Root directory in default implementation.
VirtualDir root;
};
// Convenience typedefs to use VfsDirectory and VfsFile
using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>;
using VirtualFile = std::shared_ptr<FileSys::VfsFile>;
// A class representing a file in an abstract filesystem.
struct VfsFile : NonCopyable {
@@ -305,9 +245,6 @@ struct ReadOnlyVfsDirectory : public VfsDirectory {
bool Rename(std::string_view name) override;
};
// 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);
// 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
// directory of src/dest.

View File

@@ -6,7 +6,7 @@
#include <cstddef>
#include <iterator>
#include <utility>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -29,8 +29,6 @@ static std::string ModeFlagsToString(Mode mode) {
mode_str = "a";
else if (mode & Mode::Write)
mode_str = "w";
else
UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
}
mode_str += "b";
@@ -38,174 +36,8 @@ static std::string ModeFlagsToString(Mode mode) {
return mode_str;
}
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
std::string RealVfsFilesystem::GetName() const {
return "Real";
}
bool RealVfsFilesystem::IsReadable() const {
return true;
}
bool RealVfsFilesystem::IsWritable() const {
return true;
}
VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path))
return VfsEntryType::None;
if (FileUtil::IsDirectory(path))
return VfsEntryType::Directory;
return VfsEntryType::File;
}
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (cache.find(path) != cache.end()) {
auto weak = cache[path];
if (!weak.expired()) {
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms));
}
}
if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
FileUtil::CreateEmptyFile(path);
auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
cache[path] = backing;
// Cannot use make_shared as RealVfsFile constructor is private
return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms));
}
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
return nullptr;
return OpenFile(path, perms);
}
VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path))
return nullptr;
return OpenFile(new_path, Mode::ReadWrite);
}
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
return nullptr;
if (cache.find(old_path) != cache.end()) {
auto cached = cache[old_path];
if (!cached.expired()) {
auto file = cached.lock();
file->Open(new_path, "r+b");
cache.erase(old_path);
cache[new_path] = file;
}
}
return OpenFile(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (cache.find(path) != cache.end()) {
if (!cache[path].expired())
cache[path].lock()->Close();
cache.erase(path);
}
return FileUtil::Delete(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path))
return nullptr;
// Cannot use make_shared as RealVfsDirectory constructor is private
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
}
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
!FileUtil::IsDirectory(old_path))
return nullptr;
FileUtil::CopyDir(old_path, new_path);
return OpenDirectory(new_path, Mode::ReadWrite);
}
VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
std::string_view new_path_) {
const auto old_path =
FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
return nullptr;
for (auto& kv : cache) {
// Path in cache starts with old_path
if (kv.first.rfind(old_path, 0) == 0) {
const auto file_old_path =
FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault);
const auto file_new_path =
FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()),
FileUtil::DirectorySeparator::PlatformDefault);
auto cached = cache[file_old_path];
if (!cached.expired()) {
auto file = cached.lock();
file->Open(file_new_path, "r+b");
cache.erase(file_old_path);
cache[file_new_path] = file;
}
}
}
return OpenDirectory(new_path, Mode::ReadWrite);
}
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
for (auto& kv : cache) {
// Path in cache starts with old_path
if (kv.first.rfind(path, 0) == 0) {
if (!cache[kv.first].expired())
cache[kv.first].lock()->Close();
cache.erase(kv.first);
}
}
return FileUtil::DeleteDirRecursively(path);
}
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_,
const std::string& path_, Mode perms_)
: base(base_), backing(std::move(backing_)), path(path_),
RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_)
: backing(path_, ModeFlagsToString(perms_).c_str()), path(path_),
parent_path(FileUtil::GetParentPath(path_)),
path_components(FileUtil::SplitPathComponents(path_)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
@@ -216,15 +48,15 @@ std::string RealVfsFile::GetName() const {
}
size_t RealVfsFile::GetSize() const {
return backing->GetSize();
return backing.GetSize();
}
bool RealVfsFile::Resize(size_t new_size) {
return backing->Resize(new_size);
return backing.Resize(new_size);
}
std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
return base.OpenDirectory(parent_path, perms);
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
bool RealVfsFile::IsWritable() const {
@@ -236,118 +68,62 @@ bool RealVfsFile::IsReadable() const {
}
size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
if (!backing->Seek(offset, SEEK_SET))
if (!backing.Seek(offset, SEEK_SET))
return 0;
return backing->ReadBytes(data, length);
return backing.ReadBytes(data, length);
}
size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
if (!backing->Seek(offset, SEEK_SET))
if (!backing.Seek(offset, SEEK_SET))
return 0;
return backing->WriteBytes(data, length);
return backing.WriteBytes(data, length);
}
bool RealVfsFile::Rename(std::string_view name) {
return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr;
std::string name_str(name.begin(), name.end());
const auto out = FileUtil::Rename(GetName(), name_str);
path = (parent_path + DIR_SEP).append(name);
path_components = parent_components;
path_components.push_back(std::move(name_str));
backing = FileUtil::IOFile(path, ModeFlagsToString(perms).c_str());
return out;
}
bool RealVfsFile::Close() {
return backing->Close();
return backing.Close();
}
// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
// constexpr' because there is a compile error in the branch not used.
template <>
std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
if (perms == Mode::Append)
return {};
std::vector<VirtualFile> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (!FileUtil::IsDirectory(full_path))
out.emplace_back(base.OpenFile(full_path, perms));
return true;
});
return out;
}
template <>
std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
if (perms == Mode::Append)
return {};
std::vector<VirtualDir> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out, this](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
out.emplace_back(base.OpenDirectory(full_path, perms));
return true;
});
return out;
}
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
: base(base_), path(FileUtil::RemoveTrailingSlash(path_)),
parent_path(FileUtil::GetParentPath(path)),
RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
: path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)),
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
FileUtil::CreateDir(path);
}
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenFile(full_path, perms);
}
if (perms == Mode::Append)
return;
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenDirectory(full_path, perms);
}
std::shared_ptr<VfsFile> RealVfsDirectory::GetFile(std::string_view name) const {
return GetFileRelative(name);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view name) const {
return GetDirectoryRelative(name);
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
return base.CreateFile(full_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
auto parent = std::string(FileUtil::GetParentPath(full_path));
return base.CreateDirectory(full_path, perms);
}
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name));
return base.DeleteDirectory(full_path);
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[this](u64* entries_out, const std::string& directory, const std::string& filename) {
std::string full_path = directory + DIR_SEP + filename;
if (FileUtil::IsDirectory(full_path))
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms));
else
files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms));
return true;
});
}
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
return IterateEntries<RealVfsFile, VfsFile>();
return files;
}
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
return IterateEntries<RealVfsDirectory, VfsDirectory>();
return subdirectories;
}
bool RealVfsDirectory::IsWritable() const {
@@ -366,32 +142,57 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const {
if (path_components.size() <= 1)
return nullptr;
return base.OpenDirectory(parent_path, perms);
return std::make_shared<RealVfsDirectory>(parent_path, perms);
}
std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
return base.CreateDirectory(subdir_path, perms);
if (!FileUtil::CreateDir(subdir_path)) {
return nullptr;
}
subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(subdir_path, perms));
return subdirectories.back();
}
std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(std::string_view name) {
const std::string file_path = (path + DIR_SEP).append(name);
return base.CreateFile(file_path, perms);
if (!FileUtil::CreateEmptyFile(file_path)) {
return nullptr;
}
files.emplace_back(std::make_shared<RealVfsFile>(file_path, perms));
return files.back();
}
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + DIR_SEP).append(name);
return base.DeleteDirectory(subdir_path);
return FileUtil::DeleteDirRecursively(subdir_path);
}
bool RealVfsDirectory::DeleteFile(std::string_view name) {
const auto file = GetFile(name);
if (file == nullptr) {
return false;
}
files.erase(std::find(files.begin(), files.end(), file));
auto real_file = std::static_pointer_cast<RealVfsFile>(file);
real_file->Close();
const std::string file_path = (path + DIR_SEP).append(name);
return base.DeleteFile(file_path);
return FileUtil::Delete(file_path);
}
bool RealVfsDirectory::Rename(std::string_view name) {
const std::string new_name = (parent_path + DIR_SEP).append(name);
return base.MoveFile(path, new_name) != nullptr;
return FileUtil::Rename(path, new_name);
}
std::string RealVfsDirectory::GetFullPath() const {
@@ -401,6 +202,16 @@ std::string RealVfsDirectory::GetFullPath() const {
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
const auto iter = std::find(files.begin(), files.end(), file);
if (iter == files.end())
return false;
const std::ptrdiff_t offset = std::distance(files.begin(), iter);
files[offset] = files.back();
files.pop_back();
subdirectories.emplace_back(std::move(dir));
return true;
}
} // namespace FileSys

View File

@@ -6,45 +6,18 @@
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
std::string GetName() const override;
bool IsReadable() const override;
bool IsWritable() const override;
VfsEntryType GetEntryType(std::string_view path) const override;
VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override;
VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override;
VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
bool DeleteFile(std::string_view path) override;
VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override;
VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override;
VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
bool DeleteDirectory(std::string_view path) override;
private:
boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache;
};
// An implmentation of VfsFile that represents a file on the user's computer.
class RealVfsFile : public VfsFile {
friend class RealVfsDirectory;
friend class RealVfsFilesystem;
struct RealVfsFile : public VfsFile {
friend struct RealVfsDirectory;
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
RealVfsFile(const std::string& name, Mode perms = Mode::Read);
public:
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
@@ -58,8 +31,7 @@ public:
private:
bool Close();
RealVfsFilesystem& base;
std::shared_ptr<FileUtil::IOFile> backing;
FileUtil::IOFile backing;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
@@ -68,19 +40,9 @@ private:
};
// An implementation of VfsDirectory that represents a directory on the user's computer.
class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem;
struct RealVfsDirectory : public VfsDirectory {
RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
public:
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
bool DeleteSubdirectoryRecursive(std::string_view name) override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
@@ -98,15 +60,13 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const;
RealVfsFilesystem& base;
std::string path;
std::string parent_path;
std::vector<std::string> path_components;
std::vector<std::string> parent_components;
Mode perms;
std::vector<std::shared_ptr<VfsFile>> files;
std::vector<std::shared_ptr<VfsDirectory>> subdirectories;
};
} // namespace FileSys

View File

@@ -41,42 +41,40 @@
#include "core/loader/loader.h"
#include "core/memory.h"
namespace GDBStub {
namespace {
constexpr int GDB_BUFFER_SIZE = 10000;
const int GDB_BUFFER_SIZE = 10000;
constexpr char GDB_STUB_START = '$';
constexpr char GDB_STUB_END = '#';
constexpr char GDB_STUB_ACK = '+';
constexpr char GDB_STUB_NACK = '-';
const char GDB_STUB_START = '$';
const char GDB_STUB_END = '#';
const char GDB_STUB_ACK = '+';
const char GDB_STUB_NACK = '-';
#ifndef SIGTRAP
constexpr u32 SIGTRAP = 5;
const u32 SIGTRAP = 5;
#endif
#ifndef SIGTERM
constexpr u32 SIGTERM = 15;
const u32 SIGTERM = 15;
#endif
#ifndef MSG_WAITALL
constexpr u32 MSG_WAITALL = 8;
const u32 MSG_WAITALL = 8;
#endif
constexpr u32 LR_REGISTER = 30;
constexpr u32 SP_REGISTER = 31;
constexpr u32 PC_REGISTER = 32;
constexpr u32 CPSR_REGISTER = 33;
constexpr u32 UC_ARM64_REG_Q0 = 34;
constexpr u32 FPSCR_REGISTER = 66;
const u32 LR_REGISTER = 30;
const u32 SP_REGISTER = 31;
const u32 PC_REGISTER = 32;
const u32 CPSR_REGISTER = 33;
const u32 UC_ARM64_REG_Q0 = 34;
const u32 FPSCR_REGISTER = 66;
// TODO/WiP - Used while working on support for FPU
constexpr u32 TODO_DUMMY_REG_997 = 997;
constexpr u32 TODO_DUMMY_REG_998 = 998;
const u32 TODO_DUMMY_REG_997 = 997;
const u32 TODO_DUMMY_REG_998 = 998;
// For sample XML files see the GDB source /gdb/features
// GDB also wants the l character at the start
// This XML defines what the registers are for this specific ARM device
constexpr char target_xml[] =
static const char* target_xml =
R"(l<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target version="1.0">
@@ -142,28 +140,30 @@ constexpr char target_xml[] =
</target>
)";
int gdbserver_socket = -1;
namespace GDBStub {
u8 command_buffer[GDB_BUFFER_SIZE];
u32 command_length;
static int gdbserver_socket = -1;
u32 latest_signal = 0;
bool memory_break = false;
static u8 command_buffer[GDB_BUFFER_SIZE];
static u32 command_length;
Kernel::Thread* current_thread = nullptr;
u32 current_core = 0;
static u32 latest_signal = 0;
static bool memory_break = false;
static Kernel::Thread* current_thread = nullptr;
static u32 current_core = 0;
// Binding to a port within the reserved ports range (0-1023) requires root permissions,
// so default to a port outside of that range.
u16 gdbstub_port = 24689;
static u16 gdbstub_port = 24689;
bool halt_loop = true;
bool step_loop = false;
bool send_trap = false;
static bool halt_loop = true;
static bool step_loop = false;
static bool send_trap = false;
// If set to false, the server will never be started and no
// gdbstub-related functions will be executed.
std::atomic<bool> server_enabled(false);
static std::atomic<bool> server_enabled(false);
#ifdef _WIN32
WSADATA InitData;
@@ -171,26 +171,23 @@ WSADATA InitData;
struct Breakpoint {
bool active;
VAddr addr;
PAddr addr;
u64 len;
std::array<u8, 4> inst;
};
using BreakpointMap = std::map<VAddr, Breakpoint>;
BreakpointMap breakpoints_execute;
BreakpointMap breakpoints_read;
BreakpointMap breakpoints_write;
static std::map<u64, Breakpoint> breakpoints_execute;
static std::map<u64, Breakpoint> breakpoints_read;
static std::map<u64, Breakpoint> breakpoints_write;
struct Module {
std::string name;
VAddr beg;
VAddr end;
PAddr beg;
PAddr end;
};
std::vector<Module> modules;
} // Anonymous namespace
static std::vector<Module> modules;
void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) {
Module module;
if (add_elf_ext) {
Common::SplitPath(name, nullptr, &module.name, nullptr);
@@ -421,11 +418,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
}
/**
* Get the map of breakpoints for a given breakpoint type.
* Get the list of breakpoints for a given breakpoint type.
*
* @param type Type of breakpoint map.
* @param type Type of breakpoint list.
*/
static BreakpointMap& GetBreakpointMap(BreakpointType type) {
static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
switch (type) {
case BreakpointType::Execute:
return breakpoints_execute;
@@ -444,24 +441,20 @@ static BreakpointMap& GetBreakpointMap(BreakpointType type) {
* @param type Type of breakpoint.
* @param addr Address of breakpoint.
*/
static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
BreakpointMap& p = GetBreakpointMap(type);
static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
const auto bp = p.find(addr);
if (bp == p.end()) {
return;
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
bp->second.len, bp->second.addr, static_cast<int>(type));
p.erase(static_cast<u64>(addr));
}
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
bp->second.len, bp->second.addr, static_cast<int>(type));
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.erase(addr);
}
BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
const BreakpointMap& p = GetBreakpointMap(type);
const auto next_breakpoint = p.lower_bound(addr);
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
auto next_breakpoint = p.lower_bound(static_cast<u64>(addr));
BreakpointAddress breakpoint;
if (next_breakpoint != p.end()) {
@@ -475,38 +468,36 @@ BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type)
return breakpoint;
}
bool CheckBreakpoint(VAddr addr, BreakpointType type) {
bool CheckBreakpoint(PAddr addr, BreakpointType type) {
if (!IsConnected()) {
return false;
}
const BreakpointMap& p = GetBreakpointMap(type);
const auto bp = p.find(addr);
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
if (bp == p.end()) {
return false;
}
auto bp = p.find(static_cast<u64>(addr));
if (bp != p.end()) {
u64 len = bp->second.len;
u64 len = bp->second.len;
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
// Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
// two instructions instead of the single instruction you placed the breakpoint
// on. So, as a way to make sure that execution breakpoints are only breaking
// on the instruction that was specified, set the length of an execution
// breakpoint to 1. This should be fine since the CPU should never begin executing
// an instruction anywhere except the beginning of the instruction.
if (type == BreakpointType::Execute) {
len = 1;
}
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
// Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
// two instructions instead of the single instruction you placed the breakpoint
// on. So, as a way to make sure that execution breakpoints are only breaking
// on the instruction that was specified, set the length of an execution
// breakpoint to 1. This should be fine since the CPU should never begin executing
// an instruction anywhere except the beginning of the instruction.
if (type == BreakpointType::Execute) {
len = 1;
}
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type {} @ {:016X}, range: {:016X}"
" - {:016X} ({:X} bytes)",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type {} @ {:016X}, range: {:016X}"
" - {:016X} ({:X} bytes)",
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true;
}
}
return false;
@@ -940,7 +931,6 @@ static void WriteMemory() {
GdbHexToMem(data.data(), len_pos + 1, len);
Memory::WriteBlock(addr, data.data(), len);
Core::System::GetInstance().InvalidateCpuInstructionCaches();
SendReply("OK");
}
@@ -960,7 +950,6 @@ static void Step() {
step_loop = true;
halt_loop = true;
send_trap = true;
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/// Tell the CPU if we hit a memory breakpoint.
@@ -977,7 +966,6 @@ static void Continue() {
memory_break = false;
step_loop = false;
halt_loop = false;
Core::System::GetInstance().InvalidateCpuInstructionCaches();
}
/**
@@ -987,17 +975,13 @@ static void Continue() {
* @param addr Address of breakpoint.
* @param len Length of breakpoint.
*/
static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
BreakpointMap& p = GetBreakpointMap(type);
static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
Breakpoint breakpoint;
breakpoint.active = true;
breakpoint.addr = addr;
breakpoint.len = len;
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
Memory::WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches();
p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
@@ -1031,7 +1015,7 @@ static void AddBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
u64 len =
@@ -1080,7 +1064,7 @@ static void RemoveBreakpoint() {
auto start_offset = command_buffer + 3;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints

View File

@@ -22,7 +22,7 @@ enum class BreakpointType {
};
struct BreakpointAddress {
VAddr address;
PAddr address;
BreakpointType type;
};
@@ -53,7 +53,7 @@ bool IsServerEnabled();
bool IsConnected();
/// Register module.
void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true);
/**
* Signal to the gdbstub server that it should halt CPU execution.
@@ -74,7 +74,7 @@ void HandlePacket();
* @param addr Address to search from.
* @param type Type of breakpoint.
*/
BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type);
/**
* Check if a breakpoint of the specified type exists at the given address.
@@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointTy
* @param addr Address of breakpoint.
* @param type Type of breakpoint.
*/
bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type);
/// If set to true, the CPU will halt at the beginning of the next CPU loop.
bool GetCpuHaltFlag();

View File

@@ -32,8 +32,9 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) {
}
// Gets the threads waiting on an address.
static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
const auto RetrieveWaitingThreads =
static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr address) {
auto RetrieveWaitingThreads =
[](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
auto& thread_list = scheduler->GetThreadList();
@@ -44,20 +45,16 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
}
};
// Retrieve all threads that are waiting for this address.
std::vector<SharedPtr<Thread>> threads;
RetrieveWaitingThreads(0, threads, address);
RetrieveWaitingThreads(1, threads, address);
RetrieveWaitingThreads(2, threads, address);
RetrieveWaitingThreads(3, threads, address);
// Retrieve a list of all threads that are waiting for this address.
RetrieveWaitingThreads(0, waiting_threads, address);
RetrieveWaitingThreads(1, waiting_threads, address);
RetrieveWaitingThreads(2, waiting_threads, address);
RetrieveWaitingThreads(3, waiting_threads, address);
// Sort them by priority, such that the highest priority ones come first.
std::sort(threads.begin(), threads.end(),
std::sort(waiting_threads.begin(), waiting_threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->current_priority < rhs->current_priority;
});
return threads;
}
// Wake up num_to_wake (or all) threads in a vector.
@@ -79,7 +76,9 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signals an address being waited on.
ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
@@ -111,11 +110,12 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
}
// Get threads waiting on the address.
std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
std::vector<SharedPtr<Thread>> waiting_threads;
GetThreadsWaitingOnAddress(waiting_threads, address);
// Determine the modified value depending on the waiting count.
s32 updated_value;
if (waiting_threads.empty()) {
if (waiting_threads.size() == 0) {
updated_value = value - 1;
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value + 1;

View File

@@ -14,8 +14,8 @@
namespace Kernel {
ClientPort::ClientPort() = default;
ClientPort::~ClientPort() = default;
ClientPort::ClientPort() {}
ClientPort::~ClientPort() {}
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Note: Threads do not wait for the server endpoint to call
@@ -40,12 +40,4 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
}
void ClientPort::ConnectionClosed() {
if (active_sessions == 0) {
return;
}
--active_sessions;
}
} // namespace Kernel

View File

@@ -37,20 +37,14 @@ public:
*/
ResultVal<SharedPtr<ClientSession>> Connect();
/**
* Signifies that a previously active connection has been closed,
* decreasing the total number of active connections to this port.
*/
void ConnectionClosed();
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have
u32 active_sessions; ///< Number of currently open sessions to this port
std::string name; ///< Name of client port (optional)
private:
ClientPort();
~ClientPort() override;
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
u32 active_sessions = 0; ///< Number of currently open sessions to this port
std::string name; ///< Name of client port (optional)
};
} // namespace Kernel

View File

@@ -31,9 +31,10 @@ public:
return HANDLE_TYPE;
}
ResetType GetResetType() const {
return reset_type;
}
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -46,11 +47,6 @@ public:
private:
Event();
~Event() override;
ResetType reset_type; ///< Current ResetType
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
};
} // namespace Kernel

View File

@@ -201,7 +201,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));

View File

@@ -132,7 +132,7 @@ public:
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
HandleTable& src_table);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
u32_le GetCommand() const {
return command;

View File

@@ -4,6 +4,7 @@
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
@@ -14,7 +15,9 @@ namespace Kernel {
unsigned int Object::next_object_id;
/// Initialize the kernel
void Init() {
void Init(u32 system_mode) {
Kernel::MemoryInit(system_mode);
Kernel::ResourceLimitsInit();
Kernel::ThreadingInit();
Kernel::TimersInit();
@@ -34,6 +37,7 @@ void Shutdown() {
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();
Kernel::MemoryShutdown();
}
} // namespace Kernel

View File

@@ -9,7 +9,7 @@
namespace Kernel {
/// Initialize the kernel with the specified system mode.
void Init();
void Init(u32 system_mode);
/// Shutdown the kernel
void Shutdown();

View File

@@ -0,0 +1,90 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cinttypes>
#include <memory>
#include <utility>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Kernel {
MemoryRegionInfo memory_regions[3];
/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
/// memory configuration type.
static const u32 memory_region_sizes[8][3] = {
// Old 3DS layouts
{0x04000000, 0x02C00000, 0x01400000}, // 0
{/* This appears to be unused. */}, // 1
{0x06000000, 0x00C00000, 0x01400000}, // 2
{0x05000000, 0x01C00000, 0x01400000}, // 3
{0x04800000, 0x02400000, 0x01400000}, // 4
{0x02000000, 0x04C00000, 0x01400000}, // 5
// New 3DS layouts
{0x07C00000, 0x06400000, 0x02000000}, // 6
{0x0B200000, 0x02E00000, 0x02000000}, // 7
};
void MemoryInit(u32 mem_type) {
// TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead.
ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!");
ASSERT(mem_type != 1);
// The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
// the sizes specified in the memory_region_sizes table.
VAddr base = 0;
for (int i = 0; i < 3; ++i) {
memory_regions[i].base = base;
memory_regions[i].size = memory_region_sizes[mem_type][i];
memory_regions[i].used = 0;
memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
// Reserve enough space for this region of FCRAM.
// We do not want this block of memory to be relocated when allocating from it.
memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size);
base += memory_regions[i].size;
}
// We must've allocated the entire FCRAM by the end
ASSERT(base == Memory::FCRAM_SIZE);
}
void MemoryShutdown() {
for (auto& region : memory_regions) {
region.base = 0;
region.size = 0;
region.used = 0;
region.linear_heap_memory = nullptr;
}
}
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
switch (region) {
case MemoryRegion::APPLICATION:
return &memory_regions[0];
case MemoryRegion::SYSTEM:
return &memory_regions[1];
case MemoryRegion::BASE:
return &memory_regions[2];
default:
UNREACHABLE();
}
}
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {}
void MapSharedPages(VMManager& address_space) {}
} // namespace Kernel

View File

@@ -0,0 +1,34 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <vector>
#include "common/common_types.h"
namespace Kernel {
class VMManager;
enum class MemoryRegion : u16;
struct AddressMapping;
struct MemoryRegionInfo {
u64 base; // Not an address, but offset from start of FCRAM
u64 size;
u64 used;
std::shared_ptr<std::vector<u8>> linear_heap_memory;
};
void MemoryInit(u32 mem_type);
void MemoryShutdown();
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
void MapSharedPages(VMManager& address_space);
extern MemoryRegionInfo memory_regions[3];
} // namespace Kernel

View File

@@ -8,6 +8,7 @@
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
@@ -124,6 +125,14 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.Unwrap();
misc_memory_used += stack_size;
memory_region->used += stack_size;
// Map special address mappings
MapSharedPages(vm_manager);
for (const auto& mapping : address_mappings) {
HandleSpecialMapping(vm_manager, mapping);
}
vm_manager.LogLayout();
status = ProcessStatus::Running;
@@ -132,19 +141,37 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
memory_region = GetMemoryRegion(flags.memory_region);
auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
misc_memory_used += segment.size;
memory_region->used += segment.size;
};
// Map CodeSet segments
MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
VAddr Process::GetLinearHeapAreaAddress() const {
// Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of
// the extra RAM in the n3DS.
return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR;
}
VAddr Process::GetLinearHeapBase() const {
return GetLinearHeapAreaAddress() + memory_region->base;
}
VAddr Process::GetLinearHeapLimit() const {
return GetLinearHeapBase() + memory_region->size;
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
@@ -179,6 +206,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
vm_manager.Reprotect(vma, perms);
heap_used = size;
memory_region->used += size;
return MakeResult<VAddr>(heap_end - size);
}
@@ -198,6 +226,52 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
return result;
heap_used -= size;
memory_region->used -= size;
return RESULT_SUCCESS;
}
ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) {
UNIMPLEMENTED();
return {};
}
ResultCode Process::LinearFree(VAddr target, u32 size) {
auto& linheap_memory = memory_region->linear_heap_memory;
if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}
if (size == 0) {
return RESULT_SUCCESS;
}
VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
if (target + size > heap_end) {
return ERR_INVALID_ADDRESS_STATE;
}
ResultCode result = vm_manager.UnmapRange(target, size);
if (result.IsError())
return result;
linear_heap_used -= size;
memory_region->used -= size;
if (target + size == heap_end) {
// End of linear heap has been freed, so check what's the last allocated block in it and
// reduce the size.
auto vma = vm_manager.FindVMA(target);
ASSERT(vma != vm_manager.vma_map.end());
ASSERT(vma->second.type == VMAType::Free);
VAddr new_end = vma->second.base;
if (new_end >= GetLinearHeapBase()) {
linheap_memory->resize(new_end - GetLinearHeapBase());
}
}
return RESULT_SUCCESS;
}

View File

@@ -4,7 +4,6 @@
#pragma once
#include <array>
#include <bitset>
#include <cstddef>
#include <memory>
@@ -54,14 +53,9 @@ union ProcessFlags {
enum class ProcessStatus { Created, Running, Exited };
class ResourceLimit;
struct MemoryRegionInfo;
struct CodeSet final : public Object {
struct Segment {
size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
static SharedPtr<CodeSet> Create(std::string name);
std::string GetTypeName() const override {
@@ -76,37 +70,23 @@ struct CodeSet final : public Object {
return HANDLE_TYPE;
}
Segment& CodeSegment() {
return segments[0];
}
const Segment& CodeSegment() const {
return segments[0];
}
Segment& RODataSegment() {
return segments[1];
}
const Segment& RODataSegment() const {
return segments[1];
}
Segment& DataSegment() {
return segments[2];
}
const Segment& DataSegment() const {
return segments[2];
}
/// Name of the process
std::string name;
std::shared_ptr<std::vector<u8>> memory;
std::array<Segment, 3> segments;
VAddr entrypoint;
struct Segment {
size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
/// Name of the process
std::string name;
Segment segments[3];
Segment& code = segments[0];
Segment& rodata = segments[1];
Segment& data = segments[2];
VAddr entrypoint;
private:
CodeSet();
@@ -183,11 +163,12 @@ public:
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0;
VAddr heap_end = 0;
u64 heap_used = 0;
VAddr heap_start = 0, heap_end = 0;
u64 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0;
MemoryRegionInfo* memory_region = nullptr;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
@@ -198,9 +179,16 @@ public:
std::string name;
VAddr GetLinearHeapAreaAddress() const;
VAddr GetLinearHeapBase() const;
VAddr GetLinearHeapLimit() const;
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms);
ResultCode LinearFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);

View File

@@ -27,7 +27,7 @@ ServerSession::~ServerSession() {
// Decrease the port's connection count.
if (parent->port)
parent->port->ConnectionClosed();
parent->port->active_sessions--;
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
// the SendSyncRequest result to 0xC920181A.

View File

@@ -8,6 +8,7 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
@@ -29,17 +30,35 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
shared_memory->other_permissions = other_permissions;
if (address == 0) {
shared_memory->backing_block = std::make_shared<std::vector<u8>>(size);
shared_memory->backing_block_offset = 0;
// We need to allocate a block from the Linear Heap ourselves.
// We'll manually allocate some memory from the linear heap in the specified region.
MemoryRegionInfo* memory_region = GetMemoryRegion(region);
auto& linheap_memory = memory_region->linear_heap_memory;
ASSERT_MSG(linheap_memory->size() + size <= memory_region->size,
"Not enough space in region to allocate shared memory!");
shared_memory->backing_block = linheap_memory;
shared_memory->backing_block_offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), size, 0);
memory_region->used += size;
shared_memory->linear_heap_phys_address =
Memory::FCRAM_PADDR + memory_region->base +
static_cast<PAddr>(shared_memory->backing_block_offset);
// Increase the amount of used linear heap memory for the owner process.
if (shared_memory->owner_process != nullptr) {
shared_memory->owner_process->linear_heap_used += size;
}
// Refresh the address mappings for the current process.
if (Core::CurrentProcess() != nullptr) {
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(
shared_memory->backing_block.get());
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
}
} else {
auto& vm_manager = shared_memory->owner_process->vm_manager;
// The memory is already available and mapped in the owner process.
auto vma = vm_manager.FindVMA(address);
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
@@ -55,7 +74,6 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
}
shared_memory->base_address = address;
return shared_memory;
}
@@ -106,6 +124,11 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
VAddr target_address = address;
if (base_address == 0 && target_address == 0) {
// Calculate the address at which to map the memory block.
target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address).value();
}
// Map the memory block into the target process
auto result = target_process->vm_manager.MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);

View File

@@ -111,6 +111,9 @@ public:
SharedPtr<Process> owner_process;
/// Address of shared memory block in the owner process if specified.
VAddr base_address;
/// Physical address of the shared memory block in the linear heap if no address was specified
/// during creation.
PAddr linear_heap_phys_address;
/// Backing memory for this shared memory block.
std::shared_ptr<std::vector<u8>> backing_block;
/// Offset into the backing block for this shared memory.

View File

@@ -20,6 +20,7 @@
#include "core/core_timing_util.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
@@ -80,8 +81,8 @@ void Thread::Stop() {
wait_objects.clear();
// Mark the TLS slot in the thread's page as free.
const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
const u64 tls_slot =
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
}
@@ -335,20 +336,37 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
auto& tls_slots = owner_process->tls_slots;
auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
// There are no already-allocated pages with free slots, lets allocate a new one.
// TLS pages are allocated from the BASE region in the linear heap.
MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE);
auto& linheap_memory = memory_region->linear_heap_memory;
if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
LOG_ERROR(Kernel_SVC,
"Not enough space in region to allocate a new TLS page for thread");
return ERR_OUT_OF_MEMORY;
}
size_t offset = linheap_memory->size();
// Allocate some memory from the end of the linear heap for this region.
linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
memory_region->used += Memory::PAGE_SIZE;
owner_process->linear_heap_used += Memory::PAGE_SIZE;
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
// Allocate some memory from the end of the linear heap for this region.
const size_t offset = thread->tls_memory->size();
thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
auto& vm_manager = owner_process->vm_manager;
vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
// Map the page to the current process' address space.
// TODO(Subv): Find the correct MemoryState for this region.
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
thread->tls_memory, 0, Memory::PAGE_SIZE,
linheap_memory, offset, Memory::PAGE_SIZE,
MemoryState::ThreadLocal);
}

View File

@@ -265,8 +265,6 @@ public:
private:
Thread();
~Thread() override;
std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
};
/**

102
src/core/hle/romfs.cpp Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/swap.h"
#include "core/hle/romfs.h"
namespace RomFS {
struct Header {
u32_le header_length;
u32_le dir_hash_table_offset;
u32_le dir_hash_table_length;
u32_le dir_table_offset;
u32_le dir_table_length;
u32_le file_hash_table_offset;
u32_le file_hash_table_length;
u32_le file_table_offset;
u32_le file_table_length;
u32_le data_offset;
};
static_assert(sizeof(Header) == 0x28, "Header has incorrect size");
struct DirectoryMetadata {
u32_le parent_dir_offset;
u32_le next_dir_offset;
u32_le first_child_dir_offset;
u32_le first_file_offset;
u32_le same_hash_next_dir_offset;
u32_le name_length; // in bytes
// followed by directory name
};
static_assert(sizeof(DirectoryMetadata) == 0x18, "DirectoryMetadata has incorrect size");
struct FileMetadata {
u32_le parent_dir_offset;
u32_le next_file_offset;
u64_le data_offset;
u64_le data_length;
u32_le same_hash_next_file_offset;
u32_le name_length; // in bytes
// followed by file name
};
static_assert(sizeof(FileMetadata) == 0x20, "FileMetadata has incorrect size");
static bool MatchName(const u8* buffer, u32 name_length, const std::u16string& name) {
std::vector<char16_t> name_buffer(name_length / sizeof(char16_t));
std::memcpy(name_buffer.data(), buffer, name_length);
return name == std::u16string(name_buffer.begin(), name_buffer.end());
}
const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path) {
constexpr u32 INVALID_FIELD = 0xFFFFFFFF;
// Split path into directory names and file name
std::vector<std::u16string> dir_names = path;
dir_names.pop_back();
const std::u16string& file_name = path.back();
Header header;
std::memcpy(&header, romfs, sizeof(header));
// Find directories of each level
DirectoryMetadata dir;
const u8* current_dir = romfs + header.dir_table_offset;
std::memcpy(&dir, current_dir, sizeof(dir));
for (const std::u16string& dir_name : dir_names) {
u32 child_dir_offset;
child_dir_offset = dir.first_child_dir_offset;
while (true) {
if (child_dir_offset == INVALID_FIELD) {
return nullptr;
}
const u8* current_child_dir = romfs + header.dir_table_offset + child_dir_offset;
std::memcpy(&dir, current_child_dir, sizeof(dir));
if (MatchName(current_child_dir + sizeof(dir), dir.name_length, dir_name)) {
current_dir = current_child_dir;
break;
}
child_dir_offset = dir.next_dir_offset;
}
}
// Find the file
FileMetadata file;
u32 file_offset = dir.first_file_offset;
while (file_offset != INVALID_FIELD) {
const u8* current_file = romfs + header.file_table_offset + file_offset;
std::memcpy(&file, current_file, sizeof(file));
if (MatchName(current_file + sizeof(file), file.name_length, file_name)) {
return romfs + header.data_offset + file.data_offset;
}
file_offset = file.next_file_offset;
}
return nullptr;
}
} // namespace RomFS

22
src/core/hle/romfs.h Normal file
View File

@@ -0,0 +1,22 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <vector>
#include "common/common_types.h"
namespace RomFS {
/**
* Gets the pointer to a file in a RomFS image.
* @param romfs The pointer to the RomFS image
* @param path A vector containing the directory names and file name of the path to the file
* @return the pointer to the file
* @todo reimplement this with a full RomFS manager
*/
const u8* GetFilePointer(const u8* romfs, const std::vector<std::u16string>& path);
} // namespace RomFS

View File

@@ -42,7 +42,7 @@ public:
{0, &IProfile::Get, "Get"},
{1, &IProfile::GetBase, "GetBase"},
{10, nullptr, "GetImageSize"},
{11, &IProfile::LoadImage, "LoadImage"},
{11, nullptr, "LoadImage"},
};
RegisterHandlers(functions);
}
@@ -83,27 +83,6 @@ private:
rb.PushRaw(profile_base);
}
void LoadImage(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
// TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
const u32 jpeg_size = 107;
static const std::array<u8, jpeg_size> jpeg{
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
};
ctx.WriteBuffer(jpeg.data(), jpeg_size);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(jpeg_size);
}
u128 user_id; ///< The user id this profile refers to.
};
@@ -140,13 +119,6 @@ private:
}
};
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
}
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};

View File

@@ -14,7 +14,6 @@ public:
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
void GetUserCount(Kernel::HLERequestContext& ctx);
void GetUserExistence(Kernel::HLERequestContext& ctx);
void ListAllUsers(Kernel::HLERequestContext& ctx);
void ListOpenUsers(Kernel::HLERequestContext& ctx);

View File

@@ -8,7 +8,7 @@ namespace Service::Account {
ACC_SU::ACC_SU(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:su") {
static const FunctionInfo functions[] = {
{0, &ACC_SU::GetUserCount, "GetUserCount"},
{0, nullptr, "GetUserCount"},
{1, &ACC_SU::GetUserExistence, "GetUserExistence"},
{2, &ACC_SU::ListAllUsers, "ListAllUsers"},
{3, &ACC_SU::ListOpenUsers, "ListOpenUsers"},

View File

@@ -8,7 +8,7 @@ namespace Service::Account {
ACC_U0::ACC_U0(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u0") {
static const FunctionInfo functions[] = {
{0, &ACC_U0::GetUserCount, "GetUserCount"},
{0, nullptr, "GetUserCount"},
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
{2, &ACC_U0::ListAllUsers, "ListAllUsers"},
{3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},

View File

@@ -8,7 +8,7 @@ namespace Service::Account {
ACC_U1::ACC_U1(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "acc:u1") {
static const FunctionInfo functions[] = {
{0, &ACC_U1::GetUserCount, "GetUserCount"},
{0, nullptr, "GetUserCount"},
{1, &ACC_U1::GetUserExistence, "GetUserExistence"},
{2, &ACC_U1::ListAllUsers, "ListAllUsers"},
{3, &ACC_U1::ListOpenUsers, "ListOpenUsers"},

View File

@@ -136,7 +136,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
{17, nullptr, "SetControllerFirmwareUpdateSection"},
{18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
{19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"},
{19, nullptr, "SetScreenShotImageOrientation"},
{20, nullptr, "SetDesirableKeyboardLayout"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
{41, nullptr, "IsSystemBufferSharingEnabled"},
@@ -254,13 +254,6 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext&
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
// in the Default display.

View File

@@ -83,7 +83,6 @@ private:
void LockExit(Kernel::HLERequestContext& ctx);
void UnlockExit(Kernel::HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);

View File

@@ -13,7 +13,6 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module_ = std::make_shared<Module>();
std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager);
std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager);
std::make_shared<APM_Sys>()->InstallAsService(service_manager);
}
} // namespace Service::APM

View File

@@ -74,31 +74,6 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
LOG_DEBUG(Service_APM, "called");
}
APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "RequestPerformanceMode"},
{1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"},
{2, nullptr, "GetThrottlingState"},
{3, nullptr, "GetLastThrottlingState"},
{4, nullptr, "ClearLastThrottlingState"},
{5, nullptr, "LoadAndApplySettings"},
};
// clang-format on
RegisterHandlers(functions);
}
void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
LOG_DEBUG(Service_APM, "called");
}
} // namespace Service::APM

View File

@@ -19,12 +19,4 @@ private:
std::shared_ptr<Module> apm;
};
class APM_Sys final : public ServiceFramework<APM_Sys> {
public:
explicit APM_Sys();
private:
void GetPerformanceEvent(Kernel::HLERequestContext& ctx);
};
} // namespace Service::APM

View File

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

View File

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

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audin_a.h"
namespace Service::Audio {

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audout_a.h"
namespace Service::Audio {

View File

@@ -4,8 +4,6 @@
#include <array>
#include <vector>
#include "audio_core/codec.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
@@ -50,7 +48,7 @@ public:
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
"IAudioOut", [=]() { buffer_event->Signal(); });
[=]() { buffer_event->Signal(); });
}
private:
@@ -113,10 +111,10 @@ private:
std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
const u64 tag{rp.Pop<u64>()};
std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
std::vector<u8> data(audio_buffer.buffer_size);
Memory::ReadBlock(audio_buffer.buffer, data.data(), data.size());
if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
if (!audio_core.QueueBuffer(stream, tag, std::move(data))) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
}
@@ -202,7 +200,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(DefaultSampleRate);
rb.Push<u32>(params.channel_count);
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
}

View File

@@ -38,6 +38,16 @@ private:
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
};
} // namespace Service::Audio

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/audio/audrec_a.h"
namespace Service::Audio {

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