Compare commits

..

4 Commits

Author SHA1 Message Date
greggameplayer
dbf5921c4b correct merging error 2018-08-11 14:23:01 +02:00
greggameplayer
16f73f652f Merge branch 'master' into R16-RenderTargetFormats-PixelFormats 2018-08-11 05:59:30 +02:00
greggameplayer
46ec32149b Merge branch 'master' into R16-RenderTargetFormats-PixelFormats 2018-08-05 23:22:00 +02:00
Unknown
0bf4313c82 Implement R16S & R16UI & R16I RenderTargetFormats & PixelFormats
Do a separate function in order to get Bytes Per Pixel of DepthFormat


Apply the new function in gpu.h


delete unneeded white space
2018-07-28 04:26:27 +02:00
214 changed files with 2208 additions and 6998 deletions

View File

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

View File

@@ -1,3 +1,3 @@
#!/bin/bash -ex
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.7)
# CMake 3.6 required for FindBoost to define IMPORTED libs properly on unknown Boost versions
cmake_minimum_required(VERSION 3.6)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
include(DownloadExternals)
@@ -66,12 +66,10 @@ if (NOT ENABLE_GENERIC)
detect_architecture("_M_AMD64" x86_64)
detect_architecture("_M_IX86" x86)
detect_architecture("_M_ARM" ARM)
detect_architecture("_M_ARM64" ARM64)
else()
detect_architecture("__x86_64__" x86_64)
detect_architecture("__i386__" x86)
detect_architecture("__arm__" ARM)
detect_architecture("__aarch64__" ARM64)
endif()
endif()
@@ -189,8 +187,8 @@ find_package(Threads REQUIRED)
if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.8")
if (MSVC14 AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.5")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
endif()
@@ -222,7 +220,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
if (MSVC)
message(STATUS "unicorn not found, falling back to bundled")
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
if (MSVC14 AND ARCHITECTURE_x86_64)
set(UNICORN_VER "unicorn-yuzu")
else()
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
@@ -281,7 +279,7 @@ endif()
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
if (MSVC14 AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.10.0-msvc2015_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
@@ -305,7 +303,7 @@ endif()
# ======================================
IF (APPLE)
find_library(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
FIND_LIBRARY(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)

2
externals/fmt vendored

View File

@@ -1,8 +1,4 @@
add_library(audio_core STATIC
algorithm/filter.cpp
algorithm/filter.h
algorithm/interpolate.cpp
algorithm/interpolate.h
audio_out.cpp
audio_out.h
audio_renderer.cpp
@@ -11,12 +7,12 @@ add_library(audio_core STATIC
codec.cpp
codec.h
null_sink.h
stream.cpp
stream.h
sink.h
sink_details.cpp
sink_details.h
sink_stream.h
stream.cpp
stream.h
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
)

View File

@@ -1,79 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#define _USE_MATH_DEFINES
#include <algorithm>
#include <array>
#include <cmath>
#include <vector>
#include "audio_core/algorithm/filter.h"
#include "common/common_types.h"
namespace AudioCore {
Filter Filter::LowPass(double cutoff, double Q) {
const double w0 = 2.0 * M_PI * cutoff;
const double sin_w0 = std::sin(w0);
const double cos_w0 = std::cos(w0);
const double alpha = sin_w0 / (2 * Q);
const double a0 = 1 + alpha;
const double a1 = -2.0 * cos_w0;
const double a2 = 1 - alpha;
const double b0 = 0.5 * (1 - cos_w0);
const double b1 = 1.0 * (1 - cos_w0);
const double b2 = 0.5 * (1 - cos_w0);
return {a0, a1, a2, b0, b1, b2};
}
Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2)
: a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {}
void Filter::Process(std::vector<s16>& signal) {
const size_t num_frames = signal.size() / 2;
for (size_t i = 0; i < num_frames; i++) {
std::rotate(in.begin(), in.end() - 1, in.end());
std::rotate(out.begin(), out.end() - 1, out.end());
for (size_t ch = 0; ch < channel_count; ch++) {
in[0][ch] = signal[i * channel_count + ch];
out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] -
a2 * out[2][ch];
signal[i * 2 + ch] = static_cast<s16>(std::clamp(out[0][ch], -32768.0, 32767.0));
}
}
}
/// Calculates the appropriate Q for each biquad in a cascading filter.
/// @param total_count The total number of biquads to be cascaded.
/// @param index 0-index of the biquad to calculate the Q value for.
static double CascadingBiquadQ(size_t total_count, size_t index) {
const double pole = M_PI * (2 * index + 1) / (4.0 * total_count);
return 1.0 / (2.0 * std::cos(pole));
}
CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) {
std::vector<Filter> cascade(cascade_size);
for (size_t i = 0; i < cascade_size; i++) {
cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i));
}
return CascadingFilter{std::move(cascade)};
}
CascadingFilter::CascadingFilter() = default;
CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {}
void CascadingFilter::Process(std::vector<s16>& signal) {
for (auto& filter : filters) {
filter.Process(signal);
}
}
} // namespace AudioCore

View File

@@ -1,62 +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 {
/// Digital biquad filter:
///
/// b0 + b1 z^-1 + b2 z^-2
/// H(z) = ------------------------
/// a0 + a1 z^-1 + b2 z^-2
class Filter {
public:
/// Creates a low-pass filter.
/// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
/// @param Q Determines the quality factor of this filter.
static Filter LowPass(double cutoff, double Q = 0.7071);
/// Passthrough filter.
Filter();
Filter(double a0, double a1, double a2, double b0, double b1, double b2);
void Process(std::vector<s16>& signal);
private:
static constexpr size_t channel_count = 2;
/// Coefficients are in normalized form (a0 = 1.0).
double a1, a2, b0, b1, b2;
/// Input History
std::array<std::array<double, channel_count>, 3> in;
/// Output History
std::array<std::array<double, channel_count>, 3> out;
};
/// Cascade filters to build up higher-order filters from lower-order ones.
class CascadingFilter {
public:
/// Creates a cascading low-pass filter.
/// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
/// @param cascade_size Number of biquads in cascade.
static CascadingFilter LowPass(double cutoff, size_t cascade_size);
/// Passthrough.
CascadingFilter();
explicit CascadingFilter(std::vector<Filter> filters);
void Process(std::vector<s16>& signal);
private:
std::vector<Filter> filters;
};
} // namespace AudioCore

View File

@@ -1,71 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#define _USE_MATH_DEFINES
#include <algorithm>
#include <cmath>
#include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "common/common_types.h"
#include "common/logging/log.h"
namespace AudioCore {
/// The Lanczos kernel
static double Lanczos(size_t a, double x) {
if (x == 0.0)
return 1.0;
const double px = M_PI * x;
return a * std::sin(px) * std::sin(px / a) / (px * px);
}
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) {
if (input.size() < 2)
return {};
if (ratio <= 0) {
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
ratio = 1.0;
}
if (ratio != state.current_ratio) {
const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio);
state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3);
state.current_ratio = ratio;
}
state.nyquist.Process(input);
constexpr size_t taps = InterpolationState::lanczos_taps;
const size_t num_frames = input.size() / 2;
std::vector<s16> output;
output.reserve(static_cast<size_t>(input.size() / ratio + 4));
double& pos = state.position;
auto& h = state.history;
for (size_t i = 0; i < num_frames; ++i) {
std::rotate(h.begin(), h.end() - 1, h.end());
h[0][0] = input[i * 2 + 0];
h[0][1] = input[i * 2 + 1];
while (pos <= 1.0) {
double l = 0.0;
double r = 0.0;
for (size_t j = 0; j < h.size(); j++) {
l += Lanczos(taps, pos + j - taps + 1) * h[j][0];
r += Lanczos(taps, pos + j - taps + 1) * h[j][1];
}
output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0)));
output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0)));
pos += ratio;
}
pos -= 1.0;
}
return output;
}
} // namespace AudioCore

View File

@@ -1,43 +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 "audio_core/algorithm/filter.h"
#include "common/common_types.h"
namespace AudioCore {
struct InterpolationState {
static constexpr size_t lanczos_taps = 4;
static constexpr size_t history_size = lanczos_taps * 2 - 1;
double current_ratio = 0.0;
CascadingFilter nyquist;
std::array<std::array<s16, 2>, history_size> history = {};
double position = 0;
};
/// Interpolates input signal to produce output signal.
/// @param input The signal to interpolate.
/// @param ratio Interpolation ratio.
/// ratio > 1.0 results in fewer output samples.
/// ratio < 1.0 results in more output samples.
/// @returns Output signal.
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio);
/// Interpolates input signal to produce output signal.
/// @param input The signal to interpolate.
/// @param input_rate The sample rate of input.
/// @param output_rate The desired sample rate of the output.
/// @returns Output signal.
inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
u32 input_rate, u32 output_rate) {
const double ratio = static_cast<double>(input_rate) / static_cast<double>(output_rate);
return Interpolate(state, std::move(input), ratio);
}
} // namespace AudioCore

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_renderer.h"
#include "common/assert.h"
#include "common/logging/log.h"
@@ -27,18 +26,6 @@ AudioRenderer::AudioRenderer(AudioRendererParameter params,
QueueMixedBuffer(2);
}
u32 AudioRenderer::GetSampleRate() const {
return worker_params.sample_rate;
}
u32 AudioRenderer::GetSampleCount() const {
return worker_params.sample_count;
}
u32 AudioRenderer::GetMixBufferCount() const {
return worker_params.mix_buffer_count;
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
@@ -200,8 +187,6 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
break;
}
samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE);
is_refresh_pending = false;
}
@@ -227,7 +212,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
break;
}
samples_remaining -= samples.size() / stream->GetNumChannels();
samples_remaining -= samples.size();
for (const auto& sample : samples) {
const s32 buffer_sample{buffer[offset]};

View File

@@ -8,7 +8,6 @@
#include <memory>
#include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_out.h"
#include "audio_core/codec.h"
#include "audio_core/stream.h"
@@ -27,7 +26,7 @@ enum class PlayState : u8 {
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
u32_le mix_buffer_count;
u32_le unknown_8;
u32_le unknown_c;
u32_le voice_count;
u32_le sink_count;
@@ -161,9 +160,6 @@ public:
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
u32 GetSampleRate() const;
u32 GetSampleCount() const;
u32 GetMixBufferCount() const;
private:
class VoiceState {
@@ -195,7 +191,6 @@ private:
size_t wave_index{};
size_t offset{};
Codec::ADPCMState adpcm_state{};
InterpolationState interp_state{};
std::vector<s16> samples;
VoiceOutStatus out_status{};
VoiceInfo info{};

View File

@@ -4,7 +4,6 @@
#include <algorithm>
#include <cstring>
#include <mutex>
#include "audio_core/cubeb_sink.h"
#include "audio_core/stream.h"
@@ -67,8 +66,6 @@ public:
return;
}
std::lock_guard lock{queue_mutex};
queue.reserve(queue.size() + samples.size() * GetNumChannels());
if (is_6_channel) {
@@ -97,7 +94,6 @@ private:
u32 num_channels{};
bool is_6_channel{};
std::mutex queue_mutex;
std::vector<s16> queue;
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
@@ -157,8 +153,6 @@ long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const v
return {};
}
std::lock_guard lock{impl->queue_mutex};
const size_t frames_to_write{
std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))};

View File

@@ -29,6 +29,8 @@ add_library(common STATIC
assert.h
bit_field.h
bit_set.h
break_points.cpp
break_points.h
cityhash.cpp
cityhash.h
color.h
@@ -38,8 +40,6 @@ add_library(common STATIC
file_util.cpp
file_util.h
hash.h
hex_util.cpp
hex_util.h
logging/backend.cpp
logging/backend.h
logging/filter.cpp

View File

@@ -178,7 +178,8 @@ public:
return ExtractValue(storage);
}
constexpr explicit operator bool() const {
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
constexpr FORCE_INLINE bool ToBool() const {
return Value() != 0;
}

View File

@@ -0,0 +1,90 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <sstream>
#include "common/break_points.h"
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end();
}
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const {
auto cond = [&iAddress](const TBreakPoint& bp) {
return bp.iAddress == iAddress && bp.bTemporary;
};
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
return it != m_BreakPoints.end();
}
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const {
TBreakPointsStr bps;
for (auto breakpoint : m_BreakPoints) {
if (!breakpoint.bTemporary) {
std::stringstream bp;
bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
bps.push_back(bp.str());
}
}
return bps;
}
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) {
for (auto bps_item : bps) {
TBreakPoint bp;
std::stringstream bpstr;
bpstr << std::hex << bps_item;
bpstr >> bp.iAddress;
bp.bOn = bps_item.find("n") != bps_item.npos;
bp.bTemporary = false;
Add(bp);
}
}
void BreakPoints::Add(const TBreakPoint& bp) {
if (!IsAddressBreakPoint(bp.iAddress)) {
m_BreakPoints.push_back(bp);
// if (jit)
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
}
}
void BreakPoints::Add(u32 em_address, bool temp) {
if (!IsAddressBreakPoint(em_address)) // only add new addresses
{
TBreakPoint pt; // breakpoint settings
pt.bOn = true;
pt.bTemporary = temp;
pt.iAddress = em_address;
m_BreakPoints.push_back(pt);
// if (jit)
// jit->GetBlockCache()->InvalidateICache(em_address, 4);
}
}
void BreakPoints::Remove(u32 em_address) {
auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; };
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
if (it != m_BreakPoints.end())
m_BreakPoints.erase(it);
}
void BreakPoints::Clear() {
// if (jit)
//{
// std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
// [](const TBreakPoint& bp)
// {
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
// }
// );
//}
m_BreakPoints.clear();
}

49
src/common/break_points.h Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2013 Dolphin Emulator Project / 2014 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"
class DebugInterface;
struct TBreakPoint {
u32 iAddress;
bool bOn;
bool bTemporary;
};
// Code breakpoints.
class BreakPoints {
public:
typedef std::vector<TBreakPoint> TBreakPoints;
typedef std::vector<std::string> TBreakPointsStr;
const TBreakPoints& GetBreakPoints() {
return m_BreakPoints;
}
TBreakPointsStr GetStrings() const;
void AddFromStrings(const TBreakPointsStr& bps);
// is address breakpoint
bool IsAddressBreakPoint(u32 iAddress) const;
bool IsTempBreakPoint(u32 iAddress) const;
// Add BreakPoint
void Add(u32 em_address, bool temp = false);
void Add(const TBreakPoint& bp);
// Remove Breakpoint
void Remove(u32 iAddress);
void Clear();
void DeleteByAddress(u32 Address);
private:
TBreakPoints m_BreakPoints;
u32 m_iBreakOnCount;
};

View File

@@ -750,12 +750,6 @@ std::string GetHactoolConfigurationPath() {
#endif
}
std::string GetNANDRegistrationDir(bool system) {
if (system)
return GetUserPath(UserPath::NANDDir) + "system/Contents/registered/";
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
}
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());
}
@@ -764,7 +758,7 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str)
IOFile file(filename, text_file ? "r" : "rb");
if (!file.IsOpen())
return 0;
return false;
str.resize(static_cast<u32>(file.GetSize()));
return file.ReadArray(&str[0], str.size());

View File

@@ -129,8 +129,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
std::string GetHactoolConfigurationPath();
std::string GetNANDRegistrationDir(bool system = false);
// Returns the path to where the sys file are
std::string GetSysDirectory();

View File

@@ -1,43 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/hex_util.h"
#include "common/logging/log.h"
namespace Common {
u8 ToHexNibble(char c1) {
if (c1 >= 65 && c1 <= 70)
return c1 - 55;
if (c1 >= 97 && c1 <= 102)
return c1 - 87;
if (c1 >= 48 && c1 <= 57)
return c1 - 48;
LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
return 0;
}
std::array<u8, 16> operator""_array16(const char* str, size_t len) {
if (len != 32) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=32, "
"actual={}).",
len);
return {};
}
return HexStringToArray<16>(str);
}
std::array<u8, 32> operator""_array32(const char* str, size_t len) {
if (len != 64) {
LOG_ERROR(Common,
"Attempting to parse string to array that is not of correct size (expected=64, "
"actual={}).",
len);
return {};
}
return HexStringToArray<32>(str);
}
} // namespace Common

View File

@@ -1,41 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstddef>
#include <string>
#include <fmt/format.h>
#include "common/common_types.h"
namespace Common {
u8 ToHexNibble(char c1);
template <size_t Size, bool le = false>
std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
if constexpr (le) {
for (size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
} else {
for (size_t i = 0; i < 2 * Size; i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
return out;
}
template <size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;
for (u8 c : array)
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
return out;
}
std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);
} // namespace Common

View File

@@ -302,14 +302,13 @@ Backend* GetBackend(std::string_view backend_name) {
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) {
auto& instance = Impl::Instance();
const auto& filter = instance.GetGlobalFilter();
auto filter = Impl::Instance().GetGlobalFilter();
if (!filter.CheckMessage(log_class, log_level))
return;
Entry entry =
CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
instance.PushEntry(std::move(entry));
Impl::Instance().PushEntry(std::move(entry));
}
} // namespace Log

View File

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

View File

@@ -4,7 +4,7 @@
#include <cstddef>
#ifdef _WIN32
#include <windows.h>
#include <Windows.h>
#else
#include <cerrno>
#include <cstring>

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ struct ThreadQueueList {
// (dynamically resizable) circular buffers to remove their overhead when
// inserting and popping.
using Priority = unsigned int;
typedef unsigned int Priority;
// Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
static const Priority NUM_QUEUES = N;
@@ -26,9 +26,9 @@ struct ThreadQueueList {
}
// Only for debugging, returns priority level.
Priority contains(const T& uid) const {
Priority contains(const T& uid) {
for (Priority i = 0; i < NUM_QUEUES; ++i) {
const Queue& cur = queues[i];
Queue& cur = queues[i];
if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
return i;
}
@@ -37,8 +37,8 @@ struct ThreadQueueList {
return -1;
}
T get_first() const {
const Queue* cur = first;
T get_first() {
Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
return cur->data.front();

View File

@@ -9,9 +9,10 @@
#include "common/assert.h"
#include "common/bit_set.h"
namespace Common::X64 {
namespace Common {
namespace X64 {
inline int RegToIndex(const Xbyak::Reg& reg) {
int RegToIndex(const Xbyak::Reg& reg) {
using Kind = Xbyak::Reg::Kind;
ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
"RegSet only support GPRs and XMM registers.");
@@ -151,8 +152,8 @@ constexpr size_t ABI_SHADOW_SPACE = 0;
#endif
inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size,
s32* out_subtraction, s32* out_xmm_offset) {
void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size,
s32* out_subtraction, s32* out_xmm_offset) {
int count = (regs & ABI_ALL_GPRS).Count();
rsp_alignment -= count * 8;
size_t subtraction = 0;
@@ -173,8 +174,8 @@ inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t n
*out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
}
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
@@ -194,8 +195,8 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet
return ABI_SHADOW_SPACE;
}
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
size_t rsp_alignment, size_t needed_frame_size = 0) {
void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, size_t rsp_alignment,
size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
@@ -216,4 +217,5 @@ inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32
}
}
} // namespace Common::X64
} // namespace X64
} // namespace Common

View File

@@ -8,7 +8,8 @@
#include <xbyak.h>
#include "common/x64/xbyak_abi.h"
namespace Common::X64 {
namespace Common {
namespace X64 {
// Constants for use with cmpps/cmpss
enum {
@@ -44,4 +45,5 @@ inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
}
}
} // namespace Common::X64
} // namespace X64
} // namespace Common

View File

@@ -20,10 +20,6 @@ add_library(core STATIC
crypto/key_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
crypto/xts_encryption_layer.h
file_sys/bis_factory.cpp
file_sys/bis_factory.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/content_archive.cpp
@@ -33,14 +29,10 @@ add_library(core STATIC
file_sys/directory.h
file_sys/errors.h
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
file_sys/partition_filesystem.cpp
file_sys/partition_filesystem.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/registered_cache.cpp
file_sys/registered_cache.h
file_sys/romfs.cpp
file_sys/romfs.h
file_sys/romfs_factory.cpp
@@ -51,16 +43,12 @@ add_library(core STATIC
file_sys/sdmc_factory.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
file_sys/vfs_concat.h
file_sys/vfs_offset.cpp
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
file_sys/vfs_real.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
@@ -126,8 +114,6 @@ add_library(core STATIC
hle/service/acc/acc_u0.h
hle/service/acc/acc_u1.cpp
hle/service/acc/acc_u1.h
hle/service/acc/profile_manager.cpp
hle/service/acc/profile_manager.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/applet_ae.cpp
@@ -263,10 +249,6 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvhost_gpu.h
hle/service/nvdrv/devices/nvhost_nvdec.cpp
hle/service/nvdrv/devices/nvhost_nvdec.h
hle/service/nvdrv/devices/nvhost_nvjpg.cpp
hle/service/nvdrv/devices/nvhost_nvjpg.h
hle/service/nvdrv/devices/nvhost_vic.cpp
hle/service/nvdrv/devices/nvhost_vic.h
hle/service/nvdrv/devices/nvmap.cpp
hle/service/nvdrv/devices/nvmap.h
hle/service/nvdrv/interface.cpp
@@ -351,8 +333,6 @@ add_library(core STATIC
loader/linker.h
loader/loader.cpp
loader/loader.h
loader/nax.cpp
loader/nax.h
loader/nca.cpp
loader/nca.h
loader/nro.cpp

View File

@@ -8,8 +8,6 @@
#include "common/common_types.h"
#include "core/hle/kernel/vm_manager.h"
namespace Core {
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
public:
@@ -124,5 +122,3 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
};
} // namespace Core

View File

@@ -14,8 +14,6 @@
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {
using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
@@ -88,16 +86,7 @@ public:
}
void AddTicks(u64 ticks) override {
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
// device a way so that timing is consistent across all cores without increasing the ticks 4
// times.
u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES;
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
CoreTiming::AddTicks(amortized_ticks);
CoreTiming::AddTicks(ticks - num_interpreted_instructions);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
@@ -136,9 +125,6 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
// Unpredictable instructions
config.define_unpredictable_behaviour = true;
return std::make_unique<Dynarmic::A64::Jit>(config);
}
@@ -248,7 +234,9 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
}
void ARM_Dynarmic::PrepareReschedule() {
jit->HaltExecution();
if (jit->IsExecuting()) {
jit->HaltExecution();
}
}
void ARM_Dynarmic::ClearInstructionCache() {
@@ -302,5 +290,3 @@ bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr,
Memory::Write64(vaddr, value[1]);
});
}
} // namespace Core

View File

@@ -12,8 +12,6 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
@@ -83,5 +81,3 @@ private:
friend class ARM_Dynarmic;
Dynarmic::A64::ExclusiveMonitor monitor;
};
} // namespace Core

View File

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

View File

@@ -6,8 +6,6 @@
#include "common/common_types.h"
namespace Core {
class ExclusiveMonitor {
public:
virtual ~ExclusiveMonitor();
@@ -21,5 +19,3 @@ public:
virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0;
virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0;
};
} // namespace Core

View File

@@ -11,8 +11,6 @@
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
namespace Core {
// Load Unicorn DLL once on Windows using RAII
#ifdef _MSC_VER
#include <unicorn_dynload.h>
@@ -213,7 +211,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
}
}
void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
int uregs[32];
void* tregs[32];
@@ -240,7 +238,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
}
void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
void ARM_Unicorn::LoadContext(const ARM_Interface::ThreadContext& ctx) {
int uregs[32];
void* tregs[32];
@@ -279,5 +277,3 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt = bkpt;
last_bkpt_hit = true;
}
} // namespace Core

View File

@@ -9,8 +9,6 @@
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
namespace Core {
class ARM_Unicorn final : public ARM_Interface {
public:
ARM_Unicorn();
@@ -48,5 +46,3 @@ private:
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit;
};
} // namespace Core

View File

@@ -5,7 +5,6 @@
#include <memory>
#include <utility>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/gdbstub/gdbstub.h"
@@ -18,7 +17,6 @@
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/settings.h"
#include "file_sys/vfs_concat.h"
#include "file_sys/vfs_real.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -90,39 +88,8 @@ System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files.
std::string dir_name;
std::string filename;
Common::SplitPath(path, &dir_name, &filename, nullptr);
if (filename == "00") {
const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read);
std::vector<FileSys::VirtualFile> concat;
for (u8 i = 0; i < 0x10; ++i) {
auto next = dir->GetFile(fmt::format("{:02X}", i));
if (next != nullptr)
concat.push_back(std::move(next));
else {
next = dir->GetFile(fmt::format("{:02x}", i));
if (next != nullptr)
concat.push_back(std::move(next));
else
break;
}
}
if (concat.empty())
return nullptr;
return FileSys::ConcatenateFiles(concat, dir->GetName());
}
return vfs->OpenFile(path, FileSys::Mode::Read);
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -135,7 +102,18 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second));
return ResultStatus::ErrorSystemMode;
switch (system_mode.second) {
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
return ResultStatus::ErrorUnsupportedArch;
default:
return ResultStatus::ErrorSystemMode;
}
}
ResultStatus init_result{Init(emu_window)};
@@ -147,12 +125,22 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
}
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (load_result != Loader::ResultStatus::Success) {
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
System::Shutdown();
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
switch (load_result) {
case Loader::ResultStatus::ErrorMissingKeys:
return ResultStatus::ErrorLoader_ErrorMissingKeys;
case Loader::ResultStatus::ErrorDecrypting:
return ResultStatus::ErrorLoader_ErrorDecrypting;
case Loader::ResultStatus::ErrorInvalidFormat:
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
case Loader::ResultStatus::ErrorUnsupportedArch:
return ResultStatus::ErrorUnsupportedArch;
default:
return ResultStatus::ErrorLoader;
}
}
status = ResultStatus::Success;
return status;
@@ -181,7 +169,7 @@ Cpu& System::CpuCore(size_t core_index) {
return *cpu_cores[core_index];
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
System::ResultStatus System::Init(EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();

View File

@@ -5,7 +5,6 @@
#pragma once
#include <array>
#include <map>
#include <memory>
#include <string>
#include <thread>
@@ -23,9 +22,8 @@
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
namespace Core::Frontend {
class EmuWindow;
}
class ARM_Interface;
namespace Service::SM {
class ServiceManager;
@@ -37,16 +35,8 @@ class RendererBase;
namespace Core {
class ARM_Interface;
class System {
public:
System(const System&) = delete;
System& operator=(const System&) = delete;
System(System&&) = delete;
System& operator=(System&&) = delete;
~System();
/**
@@ -59,15 +49,21 @@ 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
ErrorSystemFiles, ///< Error in finding system files
ErrorSharedFont, ///< Error in finding shared font
ErrorVideoCore, ///< Error in the video core
ErrorUnknown, ///< Any other error
ErrorLoader, ///< The base for loader errors (too many to repeat)
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
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
/// invalid format
ErrorSystemFiles, ///< Error in finding system files
ErrorSharedFont, ///< Error in finding shared font
ErrorVideoCore, ///< Error in the video core
ErrorUnsupportedArch, ///< Unsupported Architecture (32-Bit ROMs)
ErrorUnknown ///< Any other error
};
/**
@@ -109,7 +105,7 @@ public:
* @param filepath String path to the executable application to load on the host file system.
* @returns ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath);
ResultStatus Load(EmuWindow& emu_window, const std::string& filepath);
/**
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@@ -188,13 +184,6 @@ public:
return current_process;
}
/// Gets the name of the current game
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
return app_loader->ReadTitle(out);
}
PerfStats perf_stats;
FrameLimiter frame_limiter;
@@ -244,7 +233,7 @@ private:
* input.
* @return ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Init(Frontend::EmuWindow& emu_window);
ResultStatus Init(EmuWindow& emu_window);
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;

View File

@@ -14,7 +14,6 @@
#include "core/core_timing.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/settings.h"
namespace Core {
@@ -91,7 +90,6 @@ void Cpu::RunLoop(bool tight_loop) {
LOG_TRACE(Core, "Core-{} idling", core_index);
if (IsMainCore()) {
// TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
CoreTiming::Idle();
CoreTiming::Advance();
}
@@ -127,8 +125,6 @@ void Cpu::Reschedule() {
}
reschedule_pending = false;
// Lock the global kernel mutex when we manipulate the HLE state
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
scheduler->Reschedule();
}

View File

@@ -12,14 +12,14 @@
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
class ARM_Interface;
namespace Kernel {
class Scheduler;
}
namespace Core {
class ARM_Interface;
constexpr unsigned NUM_CPU_CORES{4};
class CpuBarrier {
@@ -79,7 +79,7 @@ private:
std::shared_ptr<CpuBarrier> cpu_barrier;
std::shared_ptr<Kernel::Scheduler> scheduler;
std::atomic<bool> reschedule_pending = false;
bool reschedule_pending{};
size_t core_index;
};

View File

@@ -56,9 +56,6 @@ static u64 event_fifo_id;
// to the event_queue by the emu thread
static Common::MPSCQueue<Event, false> ts_queue;
// the queue for unscheduling the events from other threads threadsafe
static Common::MPSCQueue<std::pair<const EventType*, u64>, false> unschedule_queue;
constexpr int MAX_SLICE_LENGTH = 20000;
static s64 idled_cycles;
@@ -138,9 +135,11 @@ void ClearPendingEvents() {
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
ASSERT(event_type != nullptr);
s64 timeout = GetTicks() + cycles_into_future;
// If this event needs to be scheduled before the next advance(), force one early
if (!is_global_timer_sane)
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<>());
}
@@ -161,10 +160,6 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
}
}
void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
unschedule_queue.Push(std::make_pair(event_type, userdata));
}
void RemoveEvent(const EventType* event_type) {
auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
@@ -201,9 +196,6 @@ void MoveEvents() {
void Advance() {
MoveEvents();
for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
UnscheduleEvent(ev.first, ev.second);
}
int cycles_executed = slice_length - downcount;
global_timer += cycles_executed;

View File

@@ -65,7 +65,6 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata);
void UnscheduleEvent(const EventType* event_type, u64 userdata);
void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
/// We only permit one event of each type in the queue at a time.
void RemoveEvent(const EventType* event_type);

View File

@@ -99,7 +99,10 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
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) {
ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size.");
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++));
@@ -109,4 +112,4 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest,
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // namespace Core::Crypto
} // namespace Core::Crypto

View File

@@ -20,8 +20,10 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (sector_offset == 0) {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt);
return raw.size();
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)
@@ -32,7 +34,7 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length + sector_offset < 0x10) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return std::min<u64>(length, read);
return read;
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);

View File

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

View File

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

View File

@@ -1,58 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "core/crypto/xts_encryption_layer.h"
namespace Core::Crypto {
constexpr u64 XTS_SECTOR_SIZE = 0x4000;
XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_)
: EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {}
size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
if (length == 0)
return 0;
const auto sector_offset = offset & 0x3FFF;
if (sector_offset == 0) {
if (length % XTS_SECTOR_SIZE == 0) {
std::vector<u8> raw = base->ReadBytes(length, offset);
cipher.XTSTranscode(raw.data(), raw.size(), data, offset / XTS_SECTOR_SIZE,
XTS_SECTOR_SIZE, Op::Decrypt);
return raw.size();
}
if (length > XTS_SECTOR_SIZE) {
const auto rem = length % XTS_SECTOR_SIZE;
const auto read = length - rem;
return Read(data, read, offset) + Read(data + read, rem, offset + read);
}
std::vector<u8> buffer = base->ReadBytes(XTS_SECTOR_SIZE, offset);
if (buffer.size() < XTS_SECTOR_SIZE)
buffer.resize(XTS_SECTOR_SIZE);
cipher.XTSTranscode(buffer.data(), buffer.size(), buffer.data(), offset / XTS_SECTOR_SIZE,
XTS_SECTOR_SIZE, Op::Decrypt);
std::memcpy(data, buffer.data(), std::min(buffer.size(), length));
return std::min(buffer.size(), length);
}
// offset does not fall on block boundary (0x4000)
std::vector<u8> block = base->ReadBytes(0x4000, offset - sector_offset);
if (block.size() < XTS_SECTOR_SIZE)
block.resize(XTS_SECTOR_SIZE);
cipher.XTSTranscode(block.data(), block.size(), block.data(),
(offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
const size_t read = XTS_SECTOR_SIZE - sector_offset;
if (length + sector_offset < XTS_SECTOR_SIZE) {
std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
return std::min<u64>(length, read);
}
std::memcpy(data, block.data() + sector_offset, read);
return read + Read(data + read, length - read, offset + read);
}
} // namespace Core::Crypto

View File

@@ -1,25 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#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 XTS-mode AES decription.
class XTSEncryptionLayer : public EncryptionLayer {
public:
XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key);
size_t Read(u8* data, size_t length, size_t offset) const override;
private:
// Must be mutable as operations modify cipher contexts.
mutable AESCipher<Key256> cipher;
};
} // namespace Core::Crypto

View File

@@ -1,24 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/file_sys/bis_factory.h"
namespace FileSys {
BISFactory::BISFactory(VirtualDir nand_root_)
: nand_root(std::move(nand_root_)),
sysnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
return sysnand_cache;
}
std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
return usrnand_cache;
}
} // namespace FileSys

View File

@@ -1,30 +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 "core/loader/loader.h"
#include "registered_cache.h"
namespace FileSys {
/// File system interface to the Built-In Storage
/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
/// registered caches.
class BISFactory {
public:
explicit BISFactory(VirtualDir nand_root);
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
private:
VirtualDir nand_root;
std::shared_ptr<RegisteredCache> sysnand_cache;
std::shared_ptr<RegisteredCache> usrnand_cache;
};
} // namespace FileSys

View File

@@ -4,27 +4,22 @@
#include <array>
#include <string>
#include <fmt/ostream.h>
#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"
#include "core/loader/loader.h"
namespace FileSys {
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorBadXCIHeader;
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
status = Loader::ResultStatus::ErrorBadXCIHeader;
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
@@ -36,6 +31,9 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
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)]);
@@ -43,8 +41,6 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
}
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
auto result = AddNCAFromPartition(XCIPartition::Secure);
if (result != Loader::ResultStatus::Success) {
status = result;
@@ -78,10 +74,6 @@ Loader::ResultStatus XCI::GetStatus() const {
return status;
}
Loader::ResultStatus XCI::GetProgramNCAStatus() const {
return program_nca_status;
}
VirtualDir XCI::GetPartition(XCIPartition partition) const {
return partitions[static_cast<size_t>(partition)];
}
@@ -102,10 +94,6 @@ VirtualDir XCI::GetLogoPartition() const {
return GetPartition(XCIPartition::Logo);
}
const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
return ncas;
}
std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
const auto iter =
std::find_if(ncas.begin(), ncas.end(),
@@ -120,19 +108,19 @@ VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
return nullptr;
}
std::vector<VirtualFile> XCI::GetFiles() const {
std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const {
return {};
}
std::vector<VirtualDir> XCI::GetSubdirectories() 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();
}
VirtualDir XCI::GetParentDirectory() const {
std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
@@ -142,27 +130,15 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (partitions[static_cast<size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorXCIMissingPartition;
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);
// TODO(DarkLordZach): Add proper Rev1+ Support
if (nca->IsUpdate())
continue;
if (nca->GetType() == NCAContentType::Program) {
program_nca_status = nca->GetStatus();
}
if (nca->GetStatus() == Loader::ResultStatus::Success) {
if (nca->GetStatus() == Loader::ResultStatus::Success)
ncas.push_back(std::move(nca));
} else {
const u16 error_id = static_cast<u16>(nca->GetStatus());
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
partition_names[static_cast<size_t>(part)], nca->GetName(), error_id,
nca->GetStatus());
}
}
return Loader::ResultStatus::Success;

View File

@@ -59,7 +59,6 @@ public:
explicit XCI(VirtualFile file);
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramNCAStatus() const;
u8 GetFormatVersion() const;
@@ -69,17 +68,16 @@ public:
VirtualDir GetUpdatePartition() const;
VirtualDir GetLogoPartition() const;
const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
VirtualFile GetNCAFileByType(NCAContentType type) const;
std::vector<VirtualFile> GetFiles() const override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<VirtualDir> GetSubdirectories() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
std::string GetName() const override;
VirtualDir GetParentDirectory() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
@@ -91,7 +89,6 @@ private:
GamecardHeader header{};
Loader::ResultStatus status;
Loader::ResultStatus program_nca_status;
std::vector<VirtualDir> partitions;
std::vector<std::shared_ptr<NCA>> ncas;

View File

@@ -113,27 +113,17 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
return out;
}
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
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{}) {
status = Loader::ResultStatus::ErrorInvalidRightsID;
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{}) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
if (titlekey == Core::Crypto::Key128{})
return boost::none;
}
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
status = Loader::ResultStatus::ErrorMissingTitlekek;
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);
@@ -141,7 +131,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
return titlekey;
}
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) {
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const {
if (!encrypted)
return in;
@@ -153,22 +143,15 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
boost::optional<Core::Crypto::Key128> key = boost::none;
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (key == boost::none) {
if (status == Loader::ResultStatus::Success)
status = Loader::ResultStatus::ErrorMissingTitlekey;
return nullptr;
}
} else {
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);
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return nullptr;
}
} 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);
@@ -178,7 +161,7 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
return std::static_pointer_cast<VfsFile>(out);
}
case NCASectionCryptoType::XTS:
// TODO(DarkLordZach): Find a test case for XTS-encrypted NCAs
// TODO(DarkLordZach): Implement XTSEncryptionLayer.
default:
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
static_cast<u8>(s_header.raw.header.crypto_type));
@@ -187,31 +170,16 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
}
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
status = Loader::ResultStatus::Success;
if (file == nullptr) {
status = Loader::ResultStatus::ErrorNullFile;
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
if (sizeof(NCAHeader) != file->ReadObject(&header)) {
if (sizeof(NCAHeader) != file->ReadObject(&header))
LOG_ERROR(Loader, "File reader errored out during header read.");
status = Loader::ResultStatus::ErrorBadNCAHeader;
return;
}
encrypted = false;
if (!IsValidNCA(header)) {
if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
status = Loader::ResultStatus::ErrorNCA2;
return;
}
if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
status = Loader::ResultStatus::ErrorNCA0;
return;
}
NCAHeader dec_header{};
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
@@ -221,26 +189,14 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
header = dec_header;
encrypted = true;
} else {
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
status = Loader::ResultStatus::ErrorNCA2;
return;
}
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
status = Loader::ResultStatus::ErrorNCA0;
return;
}
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
status = Loader::ResultStatus::ErrorMissingHeaderKey;
status = Loader::ResultStatus::ErrorMissingKeys;
else
status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
status = Loader::ResultStatus::ErrorDecrypting;
return;
}
}
has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
[](char c) { return c == '\0'; }) != header.rights_id.end();
const 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; });
@@ -258,10 +214,6 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
}
is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
}) != sections.end();
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
auto section = sections[i];
@@ -277,12 +229,7 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
files.push_back(std::move(dec));
romfs = files.back();
} else {
if (status != Loader::ResultStatus::Success)
return;
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
status = Loader::ResultStatus::ErrorMissingKeys;
return;
}
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
@@ -302,12 +249,7 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
exefs = dirs.back();
}
} else {
if (status != Loader::ResultStatus::Success)
return;
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
status = Loader::ResultStatus::ErrorMissingKeys;
return;
}
}
@@ -362,10 +304,6 @@ VirtualFile NCA::GetBaseFile() const {
return file;
}
bool NCA::IsUpdate() const {
return is_update;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}

View File

@@ -27,7 +27,6 @@ enum class NCAContentType : u8 {
Control = 2,
Manual = 3,
Data = 4,
Data_Unknown5 = 5, ///< Seems to be used on some system archives
};
enum class NCASectionCryptoType : u8 {
@@ -93,16 +92,14 @@ public:
VirtualFile GetBaseFile() const;
bool IsUpdate() 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();
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset);
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;
@@ -112,8 +109,6 @@ private:
VirtualFile file;
NCAHeader header{};
bool has_rights_id{};
bool is_update{};
Loader::ResultStatus status{};

View File

@@ -16,7 +16,7 @@ std::string LanguageEntry::GetDeveloperName() const {
return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100);
}
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
NACP::NACP(VirtualFile file_) : file(std::move(file_)), raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
}

View File

@@ -81,6 +81,7 @@ public:
std::string GetVersionString() const;
private:
VirtualFile file;
std::unique_ptr<RawNACP> raw;
};

View File

@@ -1,131 +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/common_funcs.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "content_archive.h"
#include "core/file_sys/nca_metadata.h"
namespace FileSys {
bool operator>=(TitleType lhs, TitleType rhs) {
return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs);
}
bool operator<=(TitleType lhs, TitleType rhs) {
return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs);
}
CNMT::CNMT(VirtualFile file) {
if (file->ReadObject(&header) != sizeof(CNMTHeader))
return;
// If type is {Application, Update, AOC} has opt-header.
if (header.type >= TitleType::Application && header.type <= TitleType::AOC) {
if (file->ReadObject(&opt_header, sizeof(CNMTHeader)) != sizeof(OptionalHeader)) {
LOG_WARNING(Loader, "Failed to read optional header.");
}
}
for (u16 i = 0; i < header.number_content_entries; ++i) {
auto& next = content_records.emplace_back(ContentRecord{});
if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(ContentRecord) +
header.table_offset) != sizeof(ContentRecord)) {
content_records.erase(content_records.end() - 1);
}
}
for (u16 i = 0; i < header.number_meta_entries; ++i) {
auto& next = meta_records.emplace_back(MetaRecord{});
if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(MetaRecord) +
header.table_offset) != sizeof(MetaRecord)) {
meta_records.erase(meta_records.end() - 1);
}
}
}
CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
std::vector<MetaRecord> meta_records)
: header(std::move(header)), opt_header(std::move(opt_header)),
content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
u64 CNMT::GetTitleID() const {
return header.title_id;
}
u32 CNMT::GetTitleVersion() const {
return header.title_version;
}
TitleType CNMT::GetType() const {
return header.type;
}
const std::vector<ContentRecord>& CNMT::GetContentRecords() const {
return content_records;
}
const std::vector<MetaRecord>& CNMT::GetMetaRecords() const {
return meta_records;
}
bool CNMT::UnionRecords(const CNMT& other) {
bool change = false;
for (const auto& rec : other.content_records) {
const auto iter = std::find_if(content_records.begin(), content_records.end(),
[&rec](const ContentRecord& r) {
return r.nca_id == rec.nca_id && r.type == rec.type;
});
if (iter == content_records.end()) {
content_records.emplace_back(rec);
++header.number_content_entries;
change = true;
}
}
for (const auto& rec : other.meta_records) {
const auto iter =
std::find_if(meta_records.begin(), meta_records.end(), [&rec](const MetaRecord& r) {
return r.title_id == rec.title_id && r.title_version == rec.title_version &&
r.type == rec.type;
});
if (iter == meta_records.end()) {
meta_records.emplace_back(rec);
++header.number_meta_entries;
change = true;
}
}
return change;
}
std::vector<u8> CNMT::Serialize() const {
const bool has_opt_header =
header.type >= TitleType::Application && header.type <= TitleType::AOC;
const auto dead_zone = header.table_offset + sizeof(CNMTHeader);
std::vector<u8> out(
std::max(sizeof(CNMTHeader) + (has_opt_header ? sizeof(OptionalHeader) : 0), dead_zone) +
content_records.size() * sizeof(ContentRecord) + meta_records.size() * sizeof(MetaRecord));
memcpy(out.data(), &header, sizeof(CNMTHeader));
// Optional Header
if (has_opt_header) {
memcpy(out.data() + sizeof(CNMTHeader), &opt_header, sizeof(OptionalHeader));
}
auto offset = header.table_offset;
for (const auto& rec : content_records) {
memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
offset += sizeof(ContentRecord);
}
for (const auto& rec : meta_records) {
memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord));
offset += sizeof(MetaRecord);
}
return out;
}
} // namespace FileSys

View File

@@ -1,112 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstring>
#include <memory>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class CNMT;
struct CNMTHeader;
struct OptionalHeader;
enum class TitleType : u8 {
SystemProgram = 0x01,
SystemDataArchive = 0x02,
SystemUpdate = 0x03,
FirmwarePackageA = 0x04,
FirmwarePackageB = 0x05,
Application = 0x80,
Update = 0x81,
AOC = 0x82,
DeltaTitle = 0x83,
};
bool operator>=(TitleType lhs, TitleType rhs);
bool operator<=(TitleType lhs, TitleType rhs);
enum class ContentRecordType : u8 {
Meta = 0,
Program = 1,
Data = 2,
Control = 3,
Manual = 4,
Legal = 5,
Patch = 6,
};
struct ContentRecord {
std::array<u8, 0x20> hash;
std::array<u8, 0x10> nca_id;
std::array<u8, 0x6> size;
ContentRecordType type;
INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}};
struct MetaRecord {
u64_le title_id;
u32_le title_version;
TitleType type;
u8 install_byte;
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(MetaRecord) == 0x10, "MetaRecord has incorrect size.");
struct OptionalHeader {
u64_le title_id;
u64_le minimum_version;
};
static_assert(sizeof(OptionalHeader) == 0x10, "OptionalHeader has incorrect size.");
struct CNMTHeader {
u64_le title_id;
u32_le title_version;
TitleType type;
INSERT_PADDING_BYTES(1);
u16_le table_offset;
u16_le number_content_entries;
u16_le number_meta_entries;
INSERT_PADDING_BYTES(12);
};
static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");
// A class representing the format used by NCA metadata files, typically named {}.cnmt.nca or
// meta0.ncd. These describe which NCA's belong with which titles in the registered cache.
class CNMT {
public:
explicit CNMT(VirtualFile file);
CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
std::vector<MetaRecord> meta_records);
u64 GetTitleID() const;
u32 GetTitleVersion() const;
TitleType GetType() const;
const std::vector<ContentRecord>& GetContentRecords() const;
const std::vector<MetaRecord>& GetMetaRecords() const;
bool UnionRecords(const CNMT& other);
std::vector<u8> Serialize() const;
private:
CNMTHeader header;
OptionalHeader opt_header;
std::vector<ContentRecord> content_records;
std::vector<MetaRecord> meta_records;
// TODO(DarkLordZach): According to switchbrew, for Patch-type there is additional data
// after the table. This is not documented, unfortunately.
};
} // namespace FileSys

View File

@@ -24,19 +24,19 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const {
PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
// At least be as large as the header
if (file->GetSize() < sizeof(Header)) {
status = Loader::ResultStatus::ErrorBadPFSHeader;
status = Loader::ResultStatus::Error;
return;
}
// For cartridges, HFSs can get very large, so we need to calculate the size up to
// the actual content itself instead of just blindly reading in the entire file.
if (sizeof(Header) != file->ReadObject(&pfs_header)) {
status = Loader::ResultStatus::ErrorBadPFSHeader;
status = Loader::ResultStatus::Error;
return;
}
if (!pfs_header.HasValidMagicValue()) {
status = Loader::ResultStatus::ErrorBadPFSHeader;
status = Loader::ResultStatus::ErrorInvalidFormat;
return;
}
@@ -51,7 +51,7 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
const size_t total_size = file_data.size();
if (total_size != metadata_size) {
status = Loader::ResultStatus::ErrorIncorrectPFSFileSize;
status = Loader::ResultStatus::Error;
return;
}

View File

@@ -13,7 +13,7 @@
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
enum class ResultStatus;
}
namespace FileSys {

View File

@@ -12,26 +12,26 @@ namespace FileSys {
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
size_t total_size = static_cast<size_t>(file->GetSize());
if (total_size < sizeof(Header))
return Loader::ResultStatus::ErrorBadNPDMHeader;
return Loader::ResultStatus::Error;
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
if (sizeof(Header) != npdm_header_data.size())
return Loader::ResultStatus::ErrorBadNPDMHeader;
return Loader::ResultStatus::Error;
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
if (sizeof(AcidHeader) != acid_header_data.size())
return Loader::ResultStatus::ErrorBadACIDHeader;
return Loader::ResultStatus::Error;
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
return Loader::ResultStatus::ErrorBadACIHeader;
return Loader::ResultStatus::Error;
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
return Loader::ResultStatus::ErrorBadFileAccessControl;
return Loader::ResultStatus::Error;
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
return Loader::ResultStatus::ErrorBadFileAccessHeader;
return Loader::ResultStatus::Error;
return Loader::ResultStatus::Success;
}

View File

@@ -13,7 +13,7 @@
#include "partition_filesystem.h"
namespace Loader {
enum class ResultStatus : u16;
enum class ResultStatus;
}
namespace FileSys {

View File

@@ -1,493 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/encryption_layer.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_concat.h"
namespace FileSys {
std::string RegisteredCacheEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
}
static bool FollowsTwoDigitDirFormat(std::string_view name) {
static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
std::regex_constants::icase);
return std::regex_match(name.begin(), name.end(), two_digit_regex);
}
static bool FollowsNcaIdFormat(std::string_view name) {
static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
std::regex_constants::icase);
return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex);
}
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
bool within_two_digit) {
if (!within_two_digit)
return fmt::format("/{}.nca", Common::HexArrayToString(nca_id, second_hex_upper));
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
return fmt::format("/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, second_hex_upper));
}
static std::string GetCNMTName(TitleType type, u64 title_id) {
constexpr std::array<const char*, 9> TITLE_TYPE_NAMES{
"SystemProgram",
"SystemData",
"SystemUpdate",
"BootImagePackage",
"BootImagePackageSafe",
"Application",
"Patch",
"AddOnContent",
"" ///< Currently unknown 'DeltaTitle'
};
auto index = static_cast<size_t>(type);
// If the index is after the jump in TitleType, subtract it out.
if (index >= static_cast<size_t>(TitleType::Application)) {
index -= static_cast<size_t>(TitleType::Application) -
static_cast<size_t>(TitleType::FirmwarePackageB);
}
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
}
static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
switch (type) {
case NCAContentType::Program:
// TODO(DarkLordZach): Differentiate between Program and Patch
return ContentRecordType::Program;
case NCAContentType::Meta:
return ContentRecordType::Meta;
case NCAContentType::Control:
return ContentRecordType::Control;
case NCAContentType::Data:
case NCAContentType::Data_Unknown5:
return ContentRecordType::Data;
case NCAContentType::Manual:
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
return ContentRecordType::Manual;
default:
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
}
}
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
if (dir->GetFileRelative(path) != nullptr)
return dir->GetFileRelative(path);
if (dir->GetDirectoryRelative(path) != nullptr) {
const auto nca_dir = dir->GetDirectoryRelative(path);
VirtualFile file = nullptr;
const auto files = nca_dir->GetFiles();
if (files.size() == 1 && files[0]->GetName() == "00") {
file = files[0];
} else {
std::vector<VirtualFile> concat;
// Since the files are a two-digit hex number, max is FF.
for (size_t i = 0; i < 0x100; ++i) {
auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
if (next != nullptr) {
concat.push_back(std::move(next));
} else {
next = nca_dir->GetFile(fmt::format("{:02x}", i));
if (next != nullptr)
concat.push_back(std::move(next));
else
break;
}
}
if (concat.empty())
return nullptr;
file = FileSys::ConcatenateFiles(concat);
}
return file;
}
return nullptr;
}
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
VirtualFile file;
// Try all four modes of file storage:
// (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
// 00: /000000**/{:032X}.nca
// 01: /{:032X}.nca
// 10: /000000**/{:032x}.nca
// 11: /{:032x}.nca
for (u8 i = 0; i < 4; ++i) {
const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
file = OpenFileOrDirectoryConcat(dir, path);
if (file != nullptr)
return file;
}
return file;
}
static boost::optional<NcaID> CheckMapForContentRecord(
const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
if (map.find(title_id) == map.end())
return boost::none;
const auto& cnmt = map.at(title_id);
const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
[type](const ContentRecord& rec) { return rec.type == type; });
if (iter == cnmt.GetContentRecords().end())
return boost::none;
return boost::make_optional(iter->nca_id);
}
boost::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
ContentRecordType type) const {
if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end())
return meta_id.at(title_id);
const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type);
if (res1 != boost::none)
return res1;
return CheckMapForContentRecord(meta, title_id, type);
}
std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
std::vector<NcaID> ids;
for (const auto& d2_dir : dir->GetSubdirectories()) {
if (FollowsNcaIdFormat(d2_dir->GetName())) {
ids.push_back(Common::HexStringToArray<0x10, true>(d2_dir->GetName().substr(0, 0x20)));
continue;
}
if (!FollowsTwoDigitDirFormat(d2_dir->GetName()))
continue;
for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
if (!FollowsNcaIdFormat(nca_dir->GetName()))
continue;
ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
}
for (const auto& nca_file : d2_dir->GetFiles()) {
if (!FollowsNcaIdFormat(nca_file->GetName()))
continue;
ids.push_back(
Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));
}
}
for (const auto& d2_file : dir->GetFiles()) {
if (FollowsNcaIdFormat(d2_file->GetName()))
ids.push_back(Common::HexStringToArray<0x10, true>(d2_file->GetName().substr(0, 0x20)));
}
return ids;
}
void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
for (const auto& id : ids) {
const auto file = GetFileAtID(id);
if (file == nullptr)
continue;
const auto nca = std::make_shared<NCA>(parser(file, id));
if (nca->GetStatus() != Loader::ResultStatus::Success ||
nca->GetType() != NCAContentType::Meta) {
continue;
}
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& section0_file : section0->GetFiles()) {
if (section0_file->GetExtension() != "cnmt")
continue;
meta.insert_or_assign(nca->GetTitleId(), CNMT(section0_file));
meta_id.insert_or_assign(nca->GetTitleId(), id);
break;
}
}
}
void RegisteredCache::AccumulateYuzuMeta() {
const auto dir = this->dir->GetSubdirectory("yuzu_meta");
if (dir == nullptr)
return;
for (const auto& file : dir->GetFiles()) {
if (file->GetExtension() != "cnmt")
continue;
CNMT cnmt(file);
yuzu_meta.insert_or_assign(cnmt.GetTitleID(), std::move(cnmt));
}
}
void RegisteredCache::Refresh() {
if (dir == nullptr)
return;
const auto ids = AccumulateFiles();
ProcessFiles(ids);
AccumulateYuzuMeta();
}
RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function)
: dir(std::move(dir_)), parser(std::move(parsing_function)) {
Refresh();
}
RegisteredCache::~RegisteredCache() = default;
bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type) != nullptr;
}
bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry) != nullptr;
}
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)
return nullptr;
return GetFileAtID(id.get());
}
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)
return nullptr;
return parser(GetFileAtID(id.get()), id.get());
}
VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_shared<NCA>(raw);
}
std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
template <typename T>
void RegisteredCache::IterateAllMetadata(
std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc,
std::function<bool(const CNMT&, const ContentRecord&)> filter) const {
for (const auto& kv : meta) {
const auto& cnmt = kv.second;
if (filter(cnmt, EMPTY_META_CONTENT_RECORD))
out.push_back(proc(cnmt, EMPTY_META_CONTENT_RECORD));
for (const auto& rec : cnmt.GetContentRecords()) {
if (GetFileAtID(rec.nca_id) != nullptr && filter(cnmt, rec)) {
out.push_back(proc(cnmt, rec));
}
}
}
for (const auto& kv : yuzu_meta) {
const auto& cnmt = kv.second;
for (const auto& rec : cnmt.GetContentRecords()) {
if (GetFileAtID(rec.nca_id) != nullptr && filter(cnmt, rec)) {
out.push_back(proc(cnmt, rec));
}
}
}
}
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[](const CNMT& c, const ContentRecord& r) { return true; });
return out;
}
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
boost::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type != boost::none && title_type.get() != c.GetType())
return false;
if (record_type != boost::none && record_type.get() != r.type)
return false;
if (title_id != boost::none && title_id.get() != c.GetTitleID())
return false;
return true;
});
return out;
}
static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) {
const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false));
const auto iter =
std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(),
[&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; });
return iter == xci->GetNCAs().end() ? nullptr : *iter;
}
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto& ncas = xci->GetNCAs();
const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
return nca->GetType() == NCAContentType::Meta;
});
if (meta_iter == ncas.end()) {
LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
"is therefore malformed. Double check your encryption keys.");
return InstallResult::ErrorMetaFailed;
}
// Install Metadata File
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
const auto meta_id = Common::HexStringToArray<16>(meta_id_raw);
const auto res = RawInstallNCA(*meta_iter, copy, overwrite_if_exists, meta_id);
if (res != InstallResult::Success)
return res;
// Install all the other NCAs
const auto section0 = (*meta_iter)->GetSubdirectories()[0];
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
for (const auto& record : cnmt.GetContentRecords()) {
const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
if (res2 != InstallResult::Success)
return res2;
}
Refresh();
return InstallResult::Success;
}
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
bool overwrite_if_exists, const VfsCopyFunction& copy) {
CNMTHeader header{
nca->GetTitleId(), ///< Title ID
0, ///< Ignore/Default title version
type, ///< Type
{}, ///< Padding
0x10, ///< Default table offset
1, ///< 1 Content Entry
0, ///< No Meta Entries
{}, ///< Padding
};
OptionalHeader opt_header{0, 0};
ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca->GetType()), {}};
const auto& data = nca->GetBaseFile()->ReadBytes(0x100000);
mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0);
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
if (!RawInstallYuzuMeta(new_cnmt))
return InstallResult::ErrorMetaFailed;
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
}
InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
boost::optional<NcaID> override_id) {
const auto in = nca->GetBaseFile();
Core::Crypto::SHA256Hash hash{};
// Calculate NcaID
// NOTE: Because computing the SHA256 of an entire NCA is quite expensive (especially if the
// game is massive), we're going to cheat and only hash the first MB of the NCA.
// Also, for XCIs the NcaID matters, so if the override id isn't none, use that.
NcaID id{};
if (override_id == boost::none) {
const auto& data = in->ReadBytes(0x100000);
mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
memcpy(id.data(), hash.data(), 16);
} else {
id = override_id.get();
}
std::string path = GetRelativePathFromNcaID(id, false, true);
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
return InstallResult::ErrorAlreadyExists;
}
if (GetFileAtID(id) != nullptr) {
LOG_WARNING(Loader, "Overwriting existing NCA...");
VirtualDir c_dir;
{ c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); }
c_dir->DeleteFile(FileUtil::GetFilename(path));
}
auto out = dir->CreateFileRelative(path);
if (out == nullptr)
return InstallResult::ErrorCopyFailed;
return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
}
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
// Reasoning behind this method can be found in the comment for InstallEntry, NCA overload.
const auto dir = this->dir->CreateDirectoryRelative("yuzu_meta");
const auto filename = GetCNMTName(cnmt.GetType(), cnmt.GetTitleID());
if (dir->GetFile(filename) == nullptr) {
auto out = dir->CreateFile(filename);
const auto buffer = cnmt.Serialize();
out->Resize(buffer.size());
out->WriteBytes(buffer);
} else {
auto out = dir->GetFile(filename);
CNMT old_cnmt(out);
// Returns true on change
if (old_cnmt.UnionRecords(cnmt)) {
out->Resize(0);
const auto buffer = old_cnmt.Serialize();
out->Resize(buffer.size());
out->WriteBytes(buffer);
}
}
Refresh();
return std::find_if(yuzu_meta.begin(), yuzu_meta.end(),
[&cnmt](const std::pair<u64, CNMT>& kv) {
return kv.second.GetType() == cnmt.GetType() &&
kv.second.GetTitleID() == cnmt.GetTitleID();
}) != yuzu_meta.end();
}
} // namespace FileSys

View File

@@ -1,128 +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 <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <boost/container/flat_map.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class XCI;
class CNMT;
using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
enum class InstallResult {
Success,
ErrorAlreadyExists,
ErrorCopyFailed,
ErrorMetaFailed,
};
struct RegisteredCacheEntry {
u64 title_id;
ContentRecordType type;
std::string DebugInfo() const;
};
// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
/*
* A class that catalogues NCAs in the registered directory structure.
* Nintendo's registered format follows this structure:
*
* Root
* | 000000XX <- XX is the ____ two digits of the NcaID
* | <hash>.nca <- hash is the NcaID (first half of SHA256 over entire file) (folder)
* | 00
* | 01 <- Actual content split along 4GB boundaries. (optional)
*
* (This impl also supports substituting the nca dir for an nca file, as that's more convenient when
* 4GB splitting can be ignored.)
*/
class RegisteredCache {
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
// parsing function.
explicit RegisteredCache(VirtualDir dir,
RegisteredCacheParsingFunction parsing_function =
[](const VirtualFile& file, const NcaID& id) { return file; });
~RegisteredCache();
void Refresh();
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not boost::none, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
boost::optional<TitleType> title_type = boost::none,
boost::optional<ContentRecordType> record_type = boost::none,
boost::optional<u64> title_id = boost::none) const;
// Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
// is a meta NCA and all of them are accessible.
InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
// Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
// poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
// dir inside the NAND called 'yuzu_meta' and store the raw CNMT there.
// TODO(DarkLordZach): Author real meta-type NCAs and install those.
InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
private:
template <typename T>
void IterateAllMetadata(std::vector<T>& out,
std::function<T(const CNMT&, const ContentRecord&)> proc,
std::function<bool(const CNMT&, const ContentRecord&)> filter) const;
std::vector<NcaID> AccumulateFiles() const;
void ProcessFiles(const std::vector<NcaID>& ids);
void AccumulateYuzuMeta();
boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
VirtualFile GetFileAtID(NcaID id) const;
VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
boost::optional<NcaID> override_id = boost::none);
bool RawInstallYuzuMeta(const CNMT& cnmt);
VirtualDir dir;
RegisteredCacheParsingFunction parser;
// maps tid -> NcaID of meta
boost::container::flat_map<u64, NcaID> meta_id;
// maps tid -> meta
boost::container::flat_map<u64, CNMT> meta;
// maps tid -> meta for CNMT in yuzu_meta
boost::container::flat_map<u64, CNMT> yuzu_meta;
};
} // namespace FileSys

View File

@@ -65,7 +65,7 @@ void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 t
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
parent->AddFile(std::make_shared<OffsetVfsFile>(
file, entry.first.size, entry.first.offset + data_offset, entry.second));
file, entry.first.size, entry.first.offset + data_offset, entry.second, parent));
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
break;
@@ -79,7 +79,7 @@ void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, s
while (true) {
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
auto current = std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second);
if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
@@ -108,9 +108,9 @@ VirtualDir ExtractRomFS(VirtualFile file) {
const u64 file_offset = header.file_meta.offset;
const u64 dir_offset = header.directory_meta.offset + 4;
auto root =
const auto root =
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
file->GetName(), file->GetContainingDirectory());
file->GetContainingDirectory(), file->GetName());
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);

View File

@@ -6,57 +6,20 @@
#include <memory>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(file)) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id) {
// TODO(DarkLordZach): Use title id.
return MakeResult<VirtualFile>(file);
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
switch (storage) {
case StorageId::NandSystem: {
const auto res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
const auto romfs = res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
return MakeResult<VirtualFile>(romfs);
}
case StorageId::NandUser: {
const auto res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
const auto romfs = res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
return MakeResult<VirtualFile>(romfs);
}
default:
UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
}
}
} // namespace FileSys

View File

@@ -6,33 +6,17 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace Loader {
class AppLoader;
} // namespace Loader
#include "core/loader/loader.h"
namespace FileSys {
enum class ContentRecordType : u8;
enum class StorageId : u8 {
None = 0,
Host = 1,
GameCard = 2,
NandSystem = 3,
NandUser = 4,
SdCard = 5,
};
/// File system interface to the RomFS archive
class RomFSFactory {
public:
explicit RomFSFactory(Loader::AppLoader& app_loader);
ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
ResultVal<VirtualFile> Open(u64 title_id);
private:
VirtualFile file;

View File

@@ -73,7 +73,7 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
}
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) {
u128 user_id, u64 save_id) const {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0)

View File

@@ -49,11 +49,11 @@ public:
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id);
private:
VirtualDir dir;
std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id,
u64 save_id) const;
};
} // namespace FileSys

View File

@@ -3,27 +3,15 @@
// Refer to the license.txt file included.
#include <memory>
#include "core/file_sys/registered_cache.h"
#include "core/core.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/xts_archive.h"
namespace FileSys {
SDMCFactory::SDMCFactory(VirtualDir dir_)
: dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
[](const VirtualFile& file, const NcaID& id) {
return std::make_shared<NAX>(file, id)->GetDecrypted();
})) {}
SDMCFactory::~SDMCFactory() = default;
SDMCFactory::SDMCFactory(VirtualDir dir) : dir(std::move(dir)) {}
ResultVal<VirtualDir> SDMCFactory::Open() {
return MakeResult<VirtualDir>(dir);
}
std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const {
return contents;
}
} // namespace FileSys

View File

@@ -4,27 +4,19 @@
#pragma once
#include <memory>
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
class RegisteredCache;
/// File system interface to the SDCard archive
class SDMCFactory {
public:
explicit SDMCFactory(VirtualDir dir);
~SDMCFactory();
ResultVal<VirtualDir> Open();
std::shared_ptr<RegisteredCache> GetSDMCContents() const;
private:
VirtualDir dir;
std::shared_ptr<RegisteredCache> contents;
};
} // namespace FileSys

View File

@@ -8,7 +8,6 @@
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/backend.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -75,15 +74,15 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view
return new_file;
}
VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
const auto sanitized_old_path = FileUtil::SanitizePath(old_path);
const auto sanitized_new_path = FileUtil::SanitizePath(new_path);
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(sanitized_old_path, sanitized_new_path);
auto out = CopyFile(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteFile(sanitized_old_path))
if (DeleteFile(old_path))
return out;
return nullptr;
}
@@ -138,15 +137,15 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_
return new_dir;
}
VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
const auto sanitized_old_path = FileUtil::SanitizePath(old_path);
const auto sanitized_new_path = FileUtil::SanitizePath(new_path);
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(sanitized_old_path, sanitized_new_path);
auto out = CopyDirectory(old_path_, new_path_);
if (out == nullptr)
return nullptr;
if (DeleteDirectory(sanitized_old_path))
if (DeleteDirectory(old_path))
return out;
return nullptr;
}
@@ -462,11 +461,4 @@ bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
std::vector<u8> data = src->ReadAllBytes();
return dest->WriteBytes(data, 0) == data.size();
}
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
const auto res = rel->GetDirectoryRelative(path);
if (res == nullptr)
return rel->CreateDirectoryRelative(path);
return res;
}
} // namespace FileSys

View File

@@ -9,16 +9,15 @@
#include <string_view>
#include <type_traits>
#include <vector>
#include <boost/optional.hpp>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "core/file_sys/mode.h"
namespace FileSys {
class VfsDirectory;
class VfsFile;
class VfsFilesystem;
enum class Mode : u32;
struct VfsFilesystem;
struct VfsFile;
struct VfsDirectory;
// Convenience typedefs to use Vfs* interfaces
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
@@ -35,9 +34,8 @@ enum class VfsEntryType {
// 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.
class VfsFilesystem : NonCopyable {
public:
explicit VfsFilesystem(VirtualDir root);
struct VfsFilesystem : NonCopyable {
VfsFilesystem(VirtualDir root);
virtual ~VfsFilesystem();
// Gets the friendly name for the filesystem.
@@ -83,8 +81,7 @@ protected:
};
// A class representing a file in an abstract filesystem.
class VfsFile : NonCopyable {
public:
struct VfsFile : NonCopyable {
virtual ~VfsFile();
// Retrieves the file name.
@@ -182,8 +179,7 @@ public:
};
// A class representing a directory in an abstract filesystem.
class VfsDirectory : NonCopyable {
public:
struct VfsDirectory : NonCopyable {
virtual ~VfsDirectory();
// Retrives the file located at path as if the current directory was root. Returns nullptr if
@@ -299,8 +295,7 @@ protected:
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
// if writable. This is to avoid redundant empty methods everywhere.
class ReadOnlyVfsDirectory : public VfsDirectory {
public:
struct ReadOnlyVfsDirectory : public VfsDirectory {
bool IsWritable() const override;
bool IsReadable() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
@@ -318,8 +313,4 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block
// directory of src/dest.
bool VfsRawCopy(VirtualFile src, VirtualFile dest);
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
// it attempts to create it and returns the new dir or nullptr on failure.
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path);
} // namespace FileSys

View File

@@ -1,94 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_concat.h"
namespace FileSys {
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files[0];
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
: name(std::move(name)) {
size_t next_offset = 0;
for (const auto& file : files_) {
files[next_offset] = file;
next_offset += file->GetSize();
}
}
std::string ConcatenatedVfsFile::GetName() const {
if (files.empty())
return "";
if (!name.empty())
return name;
return files.begin()->second->GetName();
}
size_t ConcatenatedVfsFile::GetSize() const {
if (files.empty())
return 0;
return files.rbegin()->first + files.rbegin()->second->GetSize();
}
bool ConcatenatedVfsFile::Resize(size_t new_size) {
return false;
}
std::shared_ptr<VfsDirectory> ConcatenatedVfsFile::GetContainingDirectory() const {
if (files.empty())
return nullptr;
return files.begin()->second->GetContainingDirectory();
}
bool ConcatenatedVfsFile::IsWritable() const {
return false;
}
bool ConcatenatedVfsFile::IsReadable() const {
return true;
}
size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const {
auto entry = files.end();
for (auto iter = files.begin(); iter != files.end(); ++iter) {
if (iter->first > offset) {
entry = --iter;
break;
}
}
// Check if the entry should be the last one. The loop above will make it end().
if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize())
--entry;
if (entry == files.end())
return 0;
const auto remaining = entry->second->GetSize() + offset - entry->first;
if (length > remaining) {
return entry->second->Read(data, remaining, offset - entry->first) +
Read(data + remaining, length - remaining, offset + remaining);
}
return entry->second->Read(data, length, offset - entry->first);
}
size_t ConcatenatedVfsFile::Write(const u8* data, size_t length, size_t offset) {
return 0;
}
bool ConcatenatedVfsFile::Rename(std::string_view name) {
return false;
}
} // namespace FileSys

View File

@@ -1,41 +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 <string_view>
#include <boost/container/flat_map.hpp>
#include "core/file_sys/vfs.h"
namespace FileSys {
// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name = "");
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
// read-only.
class ConcatenatedVfsFile : public VfsFile {
friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
public:
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Read(u8* data, size_t length, size_t offset) const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
private:
// Maps starting offset to file -- more efficient.
boost::container::flat_map<u64, VirtualFile> files;
std::string name;
};
} // namespace FileSys

View File

@@ -15,8 +15,7 @@ namespace FileSys {
// Similar to seeking to an offset.
// If the file is writable, operations that would write past the end of the offset file will expand
// the size of this wrapper.
class OffsetVfsFile : public VfsFile {
public:
struct OffsetVfsFile : public VfsFile {
OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
std::string new_name = "", VirtualDir new_parent = nullptr);

View File

@@ -83,12 +83,8 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash);
if (!FileUtil::Exists(path)) {
FileUtil::CreateFullPath(path_fwd);
if (!FileUtil::CreateEmptyFile(path))
return nullptr;
}
if (!FileUtil::Exists(path) && !FileUtil::CreateEmptyFile(path))
return nullptr;
return OpenFile(path, perms);
}
@@ -144,12 +140,8 @@ VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms)
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash);
if (!FileUtil::Exists(path)) {
FileUtil::CreateFullPath(path_fwd);
if (!FileUtil::CreateDir(path))
return nullptr;
}
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));
}
@@ -314,14 +306,14 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
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) || FileUtil::IsDirectory(full_path))
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenFile(full_path, perms);
}
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) || !FileUtil::IsDirectory(full_path))
if (!FileUtil::Exists(full_path))
return nullptr;
return base.OpenDirectory(full_path, perms);
}
@@ -341,6 +333,7 @@ std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view p
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);
}

View File

@@ -5,6 +5,7 @@
#pragma once
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/mode.h"

View File

@@ -8,8 +8,8 @@
namespace FileSys {
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
std::vector<VirtualDir> dirs_, std::string name_,
VirtualDir parent_)
std::vector<VirtualDir> dirs_, VirtualDir parent_,
std::string name_)
: files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
name(std::move(name_)) {}

View File

@@ -10,11 +10,10 @@ namespace FileSys {
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
// Vector data is supplied upon construction.
class VectorVfsDirectory : public VfsDirectory {
public:
struct VectorVfsDirectory : public VfsDirectory {
explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
std::vector<VirtualDir> dirs = {}, std::string name = "",
VirtualDir parent = nullptr);
std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr,
std::string name = "");
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;

View File

@@ -1,169 +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 <cstring>
#include <regex>
#include <string>
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/xts_encryption_layer.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/xts_archive.h"
#include "core/loader/loader.h"
namespace FileSys {
constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000;
template <typename SourceData, typename SourceKey, typename Destination>
static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_length,
const SourceData* data, size_t data_length) {
mbedtls_md_context_t context;
mbedtls_md_init(&context);
const auto key_f = reinterpret_cast<const u8*>(key);
const std::vector<u8> key_v(key_f, key_f + key_length);
if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) ||
mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) ||
mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) ||
mbedtls_md_hmac_finish(&context, reinterpret_cast<u8*>(out))) {
mbedtls_md_free(&context);
return false;
}
mbedtls_md_free(&context);
return true;
}
NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
std::string path = FileUtil::SanitizePath(file->GetFullPath());
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
std::regex_constants::ECMAScript |
std::regex_constants::icase);
std::smatch match;
if (!std::regex_search(path, match, nax_path_regex)) {
status = Loader::ResultStatus::ErrorBadNAXFilePath;
return;
}
std::string two_dir = match[1];
std::string nca_id = match[2];
std::transform(two_dir.begin(), two_dir.end(), two_dir.begin(), ::toupper);
std::transform(nca_id.begin(), nca_id.end(), nca_id.begin(), ::tolower);
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
}
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
: file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, false)));
}
Loader::ResultStatus NAX::Parse(std::string_view path) {
if (file->ReadObject(header.get()) != sizeof(NAXHeader))
return Loader::ResultStatus::ErrorBadNAXHeader;
if (header->magic != Common::MakeMagic('N', 'A', 'X', '0'))
return Loader::ResultStatus::ErrorBadNAXHeader;
if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size)
return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
keys.DeriveSDSeedLazy();
std::array<Core::Crypto::Key256, 2> sd_keys{};
const auto sd_keys_res = Core::Crypto::DeriveSDKeys(sd_keys, keys);
if (sd_keys_res != Loader::ResultStatus::Success) {
return sd_keys_res;
}
const auto enc_keys = header->key_area;
size_t i = 0;
for (; i < sd_keys.size(); ++i) {
std::array<Core::Crypto::Key128, 2> nax_keys{};
if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(),
path.size())) {
return Loader::ResultStatus::ErrorNAXKeyHMACFailed;
}
for (size_t j = 0; j < nax_keys.size(); ++j) {
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j],
Core::Crypto::Mode::ECB);
cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(),
Core::Crypto::Op::Decrypt);
}
Core::Crypto::SHA256Hash validation{};
if (!CalculateHMAC256(validation.data(), &header->magic, 0x60, sd_keys[i].data() + 0x10,
0x10)) {
return Loader::ResultStatus::ErrorNAXValidationHMACFailed;
}
if (header->hmac == validation)
break;
}
if (i == 2) {
return Loader::ResultStatus::ErrorNAXKeyDerivationFailed;
}
type = static_cast<NAXContentType>(i);
Core::Crypto::Key256 final_key{};
std::memcpy(final_key.data(), &header->key_area, final_key.size());
const auto enc_file =
std::make_shared<OffsetVfsFile>(file, header->file_size, NAX_HEADER_PADDING_SIZE);
dec_file = std::make_shared<Core::Crypto::XTSEncryptionLayer>(enc_file, final_key);
return Loader::ResultStatus::Success;
}
Loader::ResultStatus NAX::GetStatus() const {
return status;
}
VirtualFile NAX::GetDecrypted() const {
return dec_file;
}
std::shared_ptr<NCA> NAX::AsNCA() const {
if (type == NAXContentType::NCA)
return std::make_shared<NCA>(GetDecrypted());
return nullptr;
}
NAXContentType NAX::GetContentType() const {
return type;
}
std::vector<std::shared_ptr<VfsFile>> NAX::GetFiles() const {
return {dec_file};
}
std::vector<std::shared_ptr<VfsDirectory>> NAX::GetSubdirectories() const {
return {};
}
std::string NAX::GetName() const {
return file->GetName();
}
std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -1,69 +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/crypto/key_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
struct NAXHeader {
std::array<u8, 0x20> hmac;
u64_le magic;
std::array<Core::Crypto::Key128, 2> key_area;
u64_le file_size;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(NAXHeader) == 0x80, "NAXHeader has incorrect size.");
enum class NAXContentType : u8 {
Save = 0,
NCA = 1,
};
class NAX : public ReadOnlyVfsDirectory {
public:
explicit NAX(VirtualFile file);
explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id);
Loader::ResultStatus GetStatus() const;
VirtualFile GetDecrypted() const;
std::shared_ptr<NCA> AsNCA() const;
NAXContentType GetContentType() 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 Parse(std::string_view path);
std::unique_ptr<NAXHeader> header;
VirtualFile file;
Loader::ResultStatus status;
NAXContentType type;
VirtualFile dec_file;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -8,8 +8,6 @@
#include "core/frontend/input.h"
#include "core/settings.h"
namespace Core::Frontend {
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
public std::enable_shared_from_this<TouchState> {
public:
@@ -110,5 +108,3 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
NotifyFramebufferLayoutChanged(Layout::DefaultFrameLayout(width, height));
}
} // namespace Core::Frontend

View File

@@ -10,8 +10,6 @@
#include "common/common_types.h"
#include "core/frontend/framebuffer_layout.h"
namespace Core::Frontend {
/**
* Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...).
@@ -34,9 +32,9 @@ class EmuWindow {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
bool fullscreen = false;
int res_width = 0;
int res_height = 0;
bool fullscreen;
int res_width;
int res_height;
std::pair<unsigned, unsigned> min_client_area_size;
};
@@ -168,5 +166,3 @@ private:
*/
std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
};
} // namespace Core::Frontend

View File

@@ -11,16 +11,17 @@ namespace Kernel {
namespace ErrCodes {
enum {
// TODO(Subv): Remove these 3DS OS error codes.
OutOfHandles = 19,
SessionClosedByRemote = 26,
PortNameTooLong = 30,
NoPendingSessions = 35,
WrongPermission = 46,
InvalidBufferDescriptor = 48,
MaxConnectionsReached = 52,
// Confirmed Switch OS error codes
MaxConnectionsReached = 7,
InvalidAddress = 102,
HandleTableFull = 105,
InvalidMemoryState = 106,
InvalidMemoryPermissions = 108,
InvalidProcessorId = 113,
InvalidHandle = 114,
InvalidCombination = 116,
@@ -29,7 +30,6 @@ enum {
TooLarge = 119,
InvalidEnumValue = 120,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
}
@@ -37,21 +37,18 @@ enum {
// double check that the code matches before re-using the constant.
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
constexpr ResultCode ERR_OUT_OF_HANDLES(-1);
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
ErrCodes::MaxConnectionsReached);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(-1);
constexpr ResultCode ERR_WRONG_PERMISSION(-1);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
constexpr ResultCode ERR_INVALID_COMBINATION(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
ErrCodes::InvalidCombination);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(-1);
constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
ErrCodes::InvalidMemoryPermissions);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_POINTER(-1);

View File

@@ -26,7 +26,7 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
u16 slot = next_free_slot;
if (slot >= generations.size()) {
LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
return ERR_HANDLE_TABLE_FULL;
return ERR_OUT_OF_HANDLES;
}
next_free_slot = generations[slot];

View File

@@ -47,7 +47,7 @@ public:
/**
* Allocates a handle for the given object.
* @return The created Handle or one of the following errors:
* - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded.
* - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
*/
ResultVal<Handle> Create(SharedPtr<Object> obj);

View File

@@ -11,7 +11,7 @@
namespace Kernel {
std::atomic<u32> Object::next_object_id{0};
unsigned int Object::next_object_id;
/// Initialize the kernel
void Init() {

View File

@@ -4,7 +4,6 @@
#pragma once
#include <atomic>
#include <string>
#include <utility>
@@ -43,8 +42,8 @@ public:
virtual ~Object();
/// Returns a unique identifier for the object. For debugging purposes only.
u32 GetObjectId() const {
return object_id.load(std::memory_order_relaxed);
unsigned int GetObjectId() const {
return object_id;
}
virtual std::string GetTypeName() const {
@@ -62,23 +61,23 @@ public:
bool IsWaitable() const;
public:
static std::atomic<u32> next_object_id;
static unsigned int next_object_id;
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
std::atomic<u32> ref_count{0};
std::atomic<u32> object_id{next_object_id++};
unsigned int ref_count = 0;
unsigned int object_id = next_object_id++;
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
object->ref_count.fetch_add(1, std::memory_order_relaxed);
++object->ref_count;
}
inline void intrusive_ptr_release(Object* object) {
if (object->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
if (--object->ref_count == 0) {
delete object;
}
}

View File

@@ -17,7 +17,7 @@ namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
@@ -25,7 +25,7 @@ Scheduler::~Scheduler() {
}
}
bool Scheduler::HaveReadyThreads() const {
bool Scheduler::HaveReadyThreads() {
std::lock_guard<std::mutex> lock(scheduler_mutex);
return ready_queue.get_first() != nullptr;
}

View File

@@ -11,19 +11,17 @@
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
namespace Core {
class ARM_Interface;
}
namespace Kernel {
class Scheduler final {
public:
explicit Scheduler(Core::ARM_Interface* cpu_core);
explicit Scheduler(ARM_Interface* cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
bool HaveReadyThreads() const;
bool HaveReadyThreads();
/// Reschedules to the next available thread (call after current thread is suspended)
void Reschedule();
@@ -72,7 +70,7 @@ private:
SharedPtr<Thread> current_thread = nullptr;
Core::ARM_Interface* cpu_core;
ARM_Interface* cpu_core;
static std::mutex scheduler_mutex;
};

View File

@@ -71,14 +71,6 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
const u32 object_id{context.GetDomainMessageHeader()->object_id};
switch (domain_message_header->command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
if (object_id > domain_request_handlers.size()) {
LOG_CRITICAL(IPC,
"object_id {} is too big! This probably means a recent service call "
"to {} needed to return a new interface!",
object_id, name);
UNREACHABLE();
return RESULT_SUCCESS; // Ignore error if asserts are off
}
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
@@ -152,7 +144,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
// end of the command such that only commands following this one are handled as domains
if (convert_to_domain) {
ASSERT_MSG(IsSession(), "ServerSession is already a domain instance.");
ASSERT_MSG(domain_request_handlers.empty(), "already a domain");
domain_request_handlers = {hle_handler};
convert_to_domain = false;
}

View File

@@ -97,12 +97,7 @@ public:
/// Returns true if the session has been converted to a domain, otherwise False
bool IsDomain() const {
return !IsSession();
}
/// Returns true if this session has not been converted to a domain, otherwise false.
bool IsSession() const {
return domain_request_handlers.empty();
return !domain_request_handlers.empty();
}
/// Converts the session to a domain at the end of the current command

View File

@@ -101,7 +101,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
GetObjectId(), address, name);
return ERR_INVALID_MEMORY_PERMISSIONS;
return ERR_WRONG_PERMISSION;
}
VAddr target_address = address;

View File

@@ -250,11 +250,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
}
/// Break program execution
static void Break(u64 reason, u64 info1, u64 info2) {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
static void Break(u64 unk_0, u64 unk_1, u64 unk_2) {
LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!");
ASSERT(false);
}
@@ -319,7 +316,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
break;
case GetInfoType::TitleId:
*result = Core::CurrentProcess()->program_id;
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0");
*result = 0;
break;
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
@@ -534,6 +532,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread));
*out_handle = thread->guest_handle;
Core::System::GetInstance().PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
LOG_TRACE(Kernel_SVC,
@@ -707,7 +706,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = g_handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->status == ThreadStatus::WaitMutex);
ASSERT(thread->status != ThreadStatus::Running);
thread->status = ThreadStatus::WaitMutex;
thread->wakeup_callback = nullptr;
owner->AddMutexWaiter(thread);

View File

@@ -23,7 +23,6 @@
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -105,10 +104,6 @@ void ExitCurrentThread() {
*/
static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle);
if (thread == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
@@ -160,14 +155,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
if (nanoseconds == -1)
return;
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType,
callback_handle);
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType,
callback_handle);
}
void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEventThreadsafe(ThreadWakeupEventType, callback_handle);
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
}
static boost::optional<s32> GetNextProcessorId(u64 mask) {
@@ -283,9 +276,9 @@ static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
* @param entry_point Address of entry point for execution
* @param arg User argument for thread
*/
static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext));
memset(&context, 0, sizeof(ARM_Interface::ThreadContext));
context.cpu_registers[0] = arg;
context.pc = entry_point;
@@ -426,33 +419,12 @@ VAddr Thread::GetCommandBufferAddress() const {
}
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
// waiters list is consistent and return without doing anything.
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr != wait_mutex_threads.end());
return;
}
// A thread can't wait on two different mutexes at the same time.
ASSERT(thread->lock_owner == nullptr);
// Ensure that the thread is not already in the list of mutex waiters
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr == wait_mutex_threads.end());
thread->lock_owner = this;
wait_mutex_threads.emplace_back(std::move(thread));
UpdatePriority();
}
void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == this);
// Ensure that the thread is in the list of mutex waiters
auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
ASSERT(itr != wait_mutex_threads.end());
boost::remove_erase(wait_mutex_threads, thread);
thread->lock_owner = nullptr;
UpdatePriority();

View File

@@ -204,7 +204,7 @@ public:
return status == ThreadStatus::WaitSynchAll;
}
Core::ARM_Interface::ThreadContext context;
ARM_Interface::ThreadContext context;
u32 thread_id;

View File

@@ -227,7 +227,7 @@ public:
}
}
ResultVal(ResultVal&& o) noexcept : result_code(o.result_code) {
ResultVal(ResultVal&& o) : result_code(o.result_code) {
if (!o.empty()) {
new (&object) T(std::move(o.object));
}

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