Compare commits

..

3 Commits

Author SHA1 Message Date
FernandoS27
26c4cb1fdc Implement dumping the decompiled shader as well 2018-10-19 22:21:40 -04:00
FernandoS27
f72b9e2c32 Implemented Shader Dumper 2018-10-19 19:12:41 -04:00
ReinUsesLisp
03feb29bce glsl_decompiler: Implement geometry shaders 2018-10-19 18:03:06 -04:00
415 changed files with 11301 additions and 26658 deletions

3
.gitmodules vendored
View File

@@ -13,6 +13,9 @@
[submodule "dynarmic"]
path = externals/dynarmic
url = https://github.com/MerryMage/dynarmic.git
[submodule "xbyak"]
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
[submodule "fmt"]
path = externals/fmt
url = https://github.com/fmtlib/fmt.git

View File

@@ -1,3 +1,3 @@
#!/bin/bash -ex
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache citraemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh

View File

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

View File

@@ -1,5 +1,8 @@
#!/bin/bash -ex
apt-get update
apt-get install -y clang-format-6.0
# Run clang-format
cd /yuzu
./.travis/clang-format/script.sh

View File

@@ -57,4 +57,3 @@ done
pip3 install pefile
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
python3 .travis/linux-mingw/scan_dll.py package/imageformats/*.dll "package/"

View File

@@ -23,21 +23,21 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
message(STATUS "Copying pre-commit hook")
file(COPY hooks/pre-commit
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks)
endif()
# Sanity check : Check that all submodules are present
# =======================================================================
function(check_submodules_present)
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules)
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
foreach(module ${gitmodules})
string(REGEX REPLACE "path *= *" "" module ${module})
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git")
message(FATAL_ERROR "Git submodule ${module} not found. "
"Please run: git submodule update --init --recursive")
endif()
@@ -45,17 +45,17 @@ function(check_submodules_present)
endfunction()
check_submodules_present()
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY)
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif()
if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif()
# Detect current compilation architecture and create standard definitions
@@ -170,7 +170,7 @@ endif()
# On modern Unixes, this is typically already the case. The lone exception is
# glibc, which may default to 32 bits. glibc allows this to be configured
# by setting _FILE_OFFSET_BITS.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
@@ -178,6 +178,10 @@ endif()
set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
# System imported libraries
# ======================
@@ -185,13 +189,13 @@ find_package(Boost 1.63.0 QUIET)
if (NOT Boost_FOUND)
message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost")
set(Boost_NO_SYSTEM_PATHS OFF)
find_package(Boost QUIET REQUIRED)
endif()
# Output binaries to bin/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -260,7 +264,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
endif()
set(UNICORN_FOUND YES)
set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn)
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
@@ -352,12 +356,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
find_program(CLANG_FORMAT
NAMES clang-format${CLANG_FORMAT_POSTFIX}
clang-format
PATHS ${PROJECT_BINARY_DIR}/externals)
PATHS ${CMAKE_BINARY_DIR}/externals)
# if find_program doesn't find it, try to download from externals
if (NOT CLANG_FORMAT)
if (WIN32)
message(STATUS "Clang format not found! Downloading...")
set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
file(DOWNLOAD
https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
"${CLANG_FORMAT}" SHOW_PROGRESS
@@ -373,11 +377,11 @@ if (NOT CLANG_FORMAT)
endif()
if (CLANG_FORMAT)
set(SRCS ${PROJECT_SOURCE_DIR}/src)
set(SRCS ${CMAKE_SOURCE_DIR}/src)
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
if (WIN32)
add_custom_target(clang-format
COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}"
COMMAND powershell.exe -Command "${CLANG_FORMAT} -i @(Get-ChildItem -Recurse ${SRCS}/* -Include \'*.h\', \'*.cpp\')"
COMMENT ${CCOMMENT})
elseif(MINGW)
add_custom_target(clang-format
@@ -446,10 +450,10 @@ endif()
# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
endif()

View File

@@ -125,6 +125,17 @@ after_build:
Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
# copy all the dll dependencies to the release folder
. "./.appveyor/UtilityFunctions.ps1"
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
Write-Host "Detected the following dependencies:"
Write-Host $MingwDLLs
foreach ($file in $MingwDLLs) {
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
}
# copy the qt windows plugin dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
@@ -134,18 +145,6 @@ after_build:
# copy the qt jpeg imageformat dll to platforms
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
# copy all the dll dependencies to the release folder
. "./.appveyor/UtilityFunctions.ps1"
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\imageformats\qjpeg.dll"
Write-Host "Detected the following dependencies:"
Write-Host $MingwDLLs
foreach ($file in $MingwDLLs) {
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
}
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
7z a $MINGW_SEVENZIP $RELEASE_DIST
}

View File

@@ -9,6 +9,7 @@ target_include_directories(catch-single-include INTERFACE catch/single_include)
# Dynarmic
if (ARCHITECTURE_x86_64)
add_library(xbyak INTERFACE)
set(DYNARMIC_TESTS OFF)
set(DYNARMIC_NO_BUNDLED_FMT ON)
add_subdirectory(dynarmic)
@@ -52,6 +53,14 @@ target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# SoundTouch
add_subdirectory(soundtouch)
# Xbyak
if (ARCHITECTURE_x86_64)
# Defined before "dynarmic" above
# add_library(xbyak INTERFACE)
target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif()
# Opus
add_subdirectory(opus)
target_include_directories(opus INTERFACE ./opus/include)

View File

@@ -33,10 +33,6 @@ else()
endif()
if(NOT HEAD_HASH)
if(EXISTS "@GIT_DATA@/head-ref")
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
else()
set(HEAD_HASH "Unknown")
endif()
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
externals/xbyak vendored Submodule

Submodule externals/xbyak added at 1de435ed04

0
git Normal file
View File

View File

@@ -54,9 +54,8 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
double l = 0.0;
double r = 0.0;
for (std::size_t j = 0; j < h.size(); j++) {
const double lanczos_calc = Lanczos(taps, pos + j - taps + 1);
l += lanczos_calc * h[j][0];
r += lanczos_calc * h[j][1];
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)));

View File

@@ -22,14 +22,16 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
return Stream::Format::Multi51Channel16;
}
UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels);
LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
UNREACHABLE();
return {};
}
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
Stream::ReleaseCallback&& release_callback) {
if (!sink) {
sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
sink = sink_details.factory(Settings::values.audio_device_id);
}
return std::make_shared<Stream>(

View File

@@ -8,7 +8,7 @@
#include "audio_core/codec.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/kernel/event.h"
#include "core/memory.h"
namespace AudioCore {
@@ -72,7 +72,7 @@ private:
EffectInStatus info{};
};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event)
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
effects(params.effect_count) {
@@ -260,7 +260,8 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
break;
}
default:
UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
UNREACHABLE();
break;
}
@@ -279,15 +280,13 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
break;
}
default:
UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
UNREACHABLE();
break;
}
// Only interpolate when necessary, expensive.
if (GetInfo().sample_rate != STREAM_SAMPLE_RATE) {
samples = Interpolate(interp_state, std::move(samples), GetInfo().sample_rate,
STREAM_SAMPLE_RATE);
}
samples =
Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE);
is_refresh_pending = false;
}

View File

@@ -15,7 +15,7 @@
#include "core/hle/kernel/object.h"
namespace Kernel {
class WritableEvent;
class Event;
}
namespace AudioCore {
@@ -143,7 +143,7 @@ struct AuxInfo {
std::array<u8, 24> output_mix_buffers;
u32_le mix_buffer_count;
u32_le sample_rate; // Stored in the aux buffer currently
u32_le sample_count;
u32_le sampe_count;
u64_le send_buffer_info;
u64_le send_buffer_base;
@@ -208,8 +208,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
class AudioRenderer {
public:
AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event);
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
~AudioRenderer();
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
@@ -225,7 +224,7 @@ private:
class VoiceState;
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::vector<EffectState> effects;
std::unique_ptr<AudioOut> audio_out;

View File

@@ -107,7 +107,7 @@ private:
static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state);
};
CubebSink::CubebSink(std::string_view target_device_name) {
CubebSink::CubebSink(std::string target_device_name) {
if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
return;
@@ -121,8 +121,7 @@ CubebSink::CubebSink(std::string_view target_device_name) {
const auto collection_end{collection.device + collection.count};
const auto device{
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
return info.friendly_name != nullptr &&
target_device_name == info.friendly_name;
return target_device_name == info.friendly_name;
})};
if (device != collection_end) {
output_device = device->devid;

View File

@@ -15,7 +15,7 @@ namespace AudioCore {
class CubebSink final : public Sink {
public:
explicit CubebSink(std::string_view device_id);
explicit CubebSink(std::string device_id);
~CubebSink() override;
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,

View File

@@ -10,7 +10,7 @@ namespace AudioCore {
class NullSink final : public Sink {
public:
explicit NullSink(std::string_view) {}
explicit NullSink(std::string){};
~NullSink() override = default;
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,

View File

@@ -14,68 +14,31 @@
#include "common/logging/log.h"
namespace AudioCore {
namespace {
struct SinkDetails {
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
using ListDevicesFn = std::vector<std::string> (*)();
/// Name for this sink.
const char* id;
/// A method to call to construct an instance of this type of sink.
FactoryFn factory;
/// A method to call to list available devices.
ListDevicesFn list_devices;
};
// sink_details is ordered in terms of desirability, with the best choice at the top.
constexpr SinkDetails sink_details[] = {
// g_sink_details is ordered in terms of desirability, with the best choice at the top.
const std::vector<SinkDetails> g_sink_details = {
#ifdef HAVE_CUBEB
SinkDetails{"cubeb",
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<CubebSink>(device_id);
},
&ListCubebSinkDevices},
SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices},
#endif
SinkDetails{"null",
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<NullSink>(device_id);
},
SinkDetails{"null", &std::make_unique<NullSink, std::string>,
[] { return std::vector<std::string>{"null"}; }},
};
const SinkDetails& GetSinkDetails(std::string_view sink_id) {
auto iter =
std::find_if(std::begin(sink_details), std::end(sink_details),
std::find_if(g_sink_details.begin(), g_sink_details.end(),
[sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
if (sink_id == "auto" || iter == std::end(sink_details)) {
if (sink_id == "auto" || iter == g_sink_details.end()) {
if (sink_id != "auto") {
LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id);
}
// Auto-select.
// sink_details is ordered in terms of desirability, with the best choice at the front.
iter = std::begin(sink_details);
// g_sink_details is ordered in terms of desirability, with the best choice at the front.
iter = g_sink_details.begin();
}
return *iter;
}
} // Anonymous namespace
std::vector<const char*> GetSinkIDs() {
std::vector<const char*> sink_ids(std::size(sink_details));
std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
[](const auto& sink) { return sink.id; });
return sink_ids;
}
std::vector<std::string> GetDeviceListForSink(std::string_view sink_id) {
return GetSinkDetails(sink_id).list_devices();
}
std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) {
return GetSinkDetails(sink_id).factory(device_id);
}
} // namespace AudioCore

View File

@@ -4,21 +4,34 @@
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace AudioCore {
class Sink;
/// Retrieves the IDs for all available audio sinks.
std::vector<const char*> GetSinkIDs();
struct SinkDetails {
using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>;
using ListDevicesFn = std::function<std::vector<std::string>()>;
/// Gets the list of devices for a particular sink identified by the given ID.
std::vector<std::string> GetDeviceListForSink(std::string_view sink_id);
SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_)
: id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {}
/// Creates an audio sink identified by the given device ID.
std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id);
/// Name for this sink.
const char* id;
/// A method to call to construct an instance of this type of sink.
FactoryFn factory;
/// A method to call to list available devices.
ListDevicesFn list_devices;
};
extern const std::vector<SinkDetails> g_sink_details;
const SinkDetails& GetSinkDetails(std::string_view sink_id);
} // namespace AudioCore

View File

@@ -11,6 +11,7 @@
#include "audio_core/stream.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/settings.h"
@@ -28,7 +29,8 @@ u32 Stream::GetNumChannels() const {
case Format::Multi51Channel16:
return 6;
}
UNIMPLEMENTED_MSG("Unimplemented format={}", static_cast<u32>(format));
LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
return {};
}
@@ -48,7 +50,7 @@ void Stream::Play() {
void Stream::Stop() {
state = State::Stopped;
UNIMPLEMENTED();
ASSERT_MSG(false, "Unimplemented");
}
Stream::State Stream::GetState() const {
@@ -102,7 +104,10 @@ void Stream::PlayNextBuffer() {
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
void Stream::ReleaseActiveBuffer() {
MICROPROFILE_SCOPE(AudioOutput);
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
@@ -119,7 +124,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
}
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
UNIMPLEMENTED();
ASSERT_MSG(false, "Unimplemented");
return {};
}

View File

@@ -10,7 +10,8 @@
namespace AudioCore {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count)
: m_sample_rate(sample_rate), m_channel_count(channel_count) {
m_sound_touch.setChannels(channel_count);
m_sound_touch.setSampleRate(sample_rate);
m_sound_touch.setPitch(1.0);
@@ -32,10 +33,10 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
// We were given actual_samples number of samples, and num_samples were requested from us.
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
const double max_latency = 0.25; // seconds
const double max_latency = 1.0; // seconds
const double max_backlog = m_sample_rate * max_latency;
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
if (backlog_fullness > 4.0) {
if (backlog_fullness > 5.0) {
// Too many samples in backlog: Don't push anymore on
num_in = 0;
}
@@ -49,7 +50,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
// This low-pass filter smoothes out variance in the calculated stretch ratio.
// The time-scale determines how responsive this filter is.
constexpr double lpf_time_scale = 0.712; // seconds
constexpr double lpf_time_scale = 2.0; // seconds
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);

View File

@@ -27,6 +27,7 @@ public:
private:
u32 m_sample_rate;
u32 m_channel_count;
soundtouch::SoundTouch m_sound_touch;
double m_stretch_ratio = 1.0;
};

View File

@@ -44,7 +44,7 @@ add_library(common STATIC
detached_tasks.cpp
detached_tasks.h
bit_field.h
bit_util.h
bit_set.h
cityhash.cpp
cityhash.h
color.h
@@ -64,6 +64,8 @@ add_library(common STATIC
logging/text_formatter.cpp
logging/text_formatter.h
math_util.h
memory_util.cpp
memory_util.h
microprofile.cpp
microprofile.h
microprofileui.h
@@ -95,9 +97,14 @@ if(ARCHITECTURE_x86_64)
PRIVATE
x64/cpu_detect.cpp
x64/cpu_detect.h
x64/xbyak_abi.h
x64/xbyak_util.h
)
endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
if (ARCHITECTURE_x86_64)
target_link_libraries(common PRIVATE xbyak)
endif()

View File

@@ -52,8 +52,5 @@ __declspec(noinline, noreturn)
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
#endif
#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!")
#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)

View File

@@ -117,21 +117,21 @@ private:
// We don't delete it because we want BitField to be trivially copyable.
constexpr BitField& operator=(const BitField&) = default;
// UnderlyingType is T for non-enum types and the underlying type of T if
// StorageType is T for non-enum types and the underlying type of T if
// T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly.
using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
std::enable_if<true, T>>::type;
using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
std::enable_if<true, T>>::type;
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
using StorageType = std::make_unsigned_t<UnderlyingType>;
// Unsigned version of StorageType
using StorageTypeU = std::make_unsigned_t<StorageType>;
public:
/// Constants to allow limited introspection of fields if needed
static constexpr std::size_t position = Position;
static constexpr std::size_t bits = Bits;
static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
/**
* Formats a value by masking and shifting it according to the field parameters. A value
@@ -148,12 +148,11 @@ public:
* union in a constexpr context.
*/
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
if (std::numeric_limits<T>::is_signed) {
std::size_t shift = 8 * sizeof(T) - bits;
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
shift);
return (T)((storage << (shift - position)) >> shift);
} else {
return static_cast<T>((storage & mask) >> position);
return (T)((storage & mask) >> position);
}
}

244
src/common/bit_set.h Normal file
View File

@@ -0,0 +1,244 @@
// This file is under the public domain.
#pragma once
#include <cstddef>
#ifdef _WIN32
#include <intrin.h>
#endif
#include <initializer_list>
#include <new>
#include <type_traits>
#include "common/common_types.h"
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
namespace Common {
// Helper functions:
#ifdef _MSC_VER
template <typename T>
static inline int CountSetBits(T v) {
// from https://graphics.stanford.edu/~seander/bithacks.html
// GCC has this built in, but MSVC's intrinsic will only emit the actual
// POPCNT instruction, which we're not depending on
v = v - ((v >> 1) & (T) ~(T)0 / 3);
v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
}
static inline int LeastSignificantSetBit(u8 val) {
unsigned long index;
_BitScanForward(&index, val);
return (int)index;
}
static inline int LeastSignificantSetBit(u16 val) {
unsigned long index;
_BitScanForward(&index, val);
return (int)index;
}
static inline int LeastSignificantSetBit(u32 val) {
unsigned long index;
_BitScanForward(&index, val);
return (int)index;
}
static inline int LeastSignificantSetBit(u64 val) {
unsigned long index;
_BitScanForward64(&index, val);
return (int)index;
}
#else
static inline int CountSetBits(u8 val) {
return __builtin_popcount(val);
}
static inline int CountSetBits(u16 val) {
return __builtin_popcount(val);
}
static inline int CountSetBits(u32 val) {
return __builtin_popcount(val);
}
static inline int CountSetBits(u64 val) {
return __builtin_popcountll(val);
}
static inline int LeastSignificantSetBit(u8 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u16 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u32 val) {
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u64 val) {
return __builtin_ctzll(val);
}
#endif
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
// using the set bits of an integer to represent a set of integers. Like that
// class, it acts like an array of bools:
// BitSet32 bs;
// bs[1] = true;
// but also like the underlying integer ([0] = least significant bit):
// BitSet32 bs2 = ...;
// bs = (bs ^ bs2) & BitSet32(0xffff);
// The following additional functionality is provided:
// - Construction using an initializer list.
// BitSet bs { 1, 2, 4, 8 };
// - Efficiently iterating through the set bits:
// for (int i : bs)
// [i is the *index* of a set bit]
// (This uses the appropriate CPU instruction to find the next set bit in one
// operation.)
// - Counting set bits using .Count() - see comment on that method.
// TODO: use constexpr when MSVC gets out of the Dark Ages
template <typename IntTy>
class BitSet {
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
public:
// A reference to a particular bit, returned from operator[].
class Ref {
public:
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
operator bool() const {
return (m_bs->m_val & m_mask) != 0;
}
bool operator=(bool set) {
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
return set;
}
private:
BitSet* m_bs;
IntTy m_mask;
};
// A STL-like iterator is required to be able to use range-based for loops.
class Iterator {
public:
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
Iterator(IntTy val) : m_val(val), m_bit(0) {}
Iterator& operator=(Iterator other) {
new (this) Iterator(other);
return *this;
}
int operator*() {
return m_bit + ComputeLsb();
}
Iterator& operator++() {
int lsb = ComputeLsb();
m_val >>= lsb + 1;
m_bit += lsb + 1;
m_has_lsb = false;
return *this;
}
Iterator operator++(int _) {
Iterator other(*this);
++*this;
return other;
}
bool operator==(Iterator other) const {
return m_val == other.m_val;
}
bool operator!=(Iterator other) const {
return m_val != other.m_val;
}
private:
int ComputeLsb() {
if (!m_has_lsb) {
m_lsb = LeastSignificantSetBit(m_val);
m_has_lsb = true;
}
return m_lsb;
}
IntTy m_val;
int m_bit;
int m_lsb = -1;
bool m_has_lsb = false;
};
BitSet() : m_val(0) {}
explicit BitSet(IntTy val) : m_val(val) {}
BitSet(std::initializer_list<int> init) {
m_val = 0;
for (int bit : init)
m_val |= (IntTy)1 << bit;
}
static BitSet AllTrue(std::size_t count) {
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
}
Ref operator[](std::size_t bit) {
return Ref(this, (IntTy)1 << bit);
}
const Ref operator[](std::size_t bit) const {
return (*const_cast<BitSet*>(this))[bit];
}
bool operator==(BitSet other) const {
return m_val == other.m_val;
}
bool operator!=(BitSet other) const {
return m_val != other.m_val;
}
bool operator<(BitSet other) const {
return m_val < other.m_val;
}
bool operator>(BitSet other) const {
return m_val > other.m_val;
}
BitSet operator|(BitSet other) const {
return BitSet(m_val | other.m_val);
}
BitSet operator&(BitSet other) const {
return BitSet(m_val & other.m_val);
}
BitSet operator^(BitSet other) const {
return BitSet(m_val ^ other.m_val);
}
BitSet operator~() const {
return BitSet(~m_val);
}
BitSet& operator|=(BitSet other) {
return *this = *this | other;
}
BitSet& operator&=(BitSet other) {
return *this = *this & other;
}
BitSet& operator^=(BitSet other) {
return *this = *this ^ other;
}
operator u32() = delete;
operator bool() {
return m_val != 0;
}
// Warning: Even though on modern CPUs this is a single fast instruction,
// Dolphin's official builds do not currently assume POPCNT support on x86,
// so slower explicit bit twiddling is generated. Still should generally
// be faster than a loop.
unsigned int Count() const {
return CountSetBits(m_val);
}
Iterator begin() const {
return Iterator(m_val);
}
Iterator end() const {
return Iterator(0);
}
IntTy m_val;
};
} // namespace Common
typedef Common::BitSet<u8> BitSet8;
typedef Common::BitSet<u16> BitSet16;
typedef Common::BitSet<u32> BitSet32;
typedef Common::BitSet<u64> BitSet64;

View File

@@ -1,61 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <climits>
#include <cstddef>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/common_types.h"
namespace Common {
/// Gets the size of a specified type T in bits.
template <typename T>
constexpr std::size_t BitSize() {
return sizeof(T) * CHAR_BIT;
}
#ifdef _MSC_VER
inline u32 CountLeadingZeroes32(u32 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value) != 0) {
return 31 - leading_zero;
}
return 32;
}
inline u64 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
return 63 - leading_zero;
}
return 64;
}
#else
inline u32 CountLeadingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return __builtin_clz(value);
}
inline u64 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return __builtin_clzll(value);
}
#endif
} // namespace Common

View File

@@ -15,24 +15,21 @@
#ifdef _WIN32
#include <windows.h>
// windows.h needs to be included before other windows headers
#include <direct.h> // getcwd
#include <commdlg.h> // for GetSaveFileName
#include <direct.h> // getcwd
#include <io.h>
#include <shellapi.h>
#include <shlobj.h> // for SHGetFolderPath
#include <tchar.h>
#include "common/string_util.h"
#ifdef _MSC_VER
// 64 bit offsets for MSVC
// 64 bit offsets for windows
#define fseeko _fseeki64
#define ftello _ftelli64
#define fileno _fileno
#endif
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
#define atoll _atoi64
#define stat _stat64
#define fstat _fstat64
#define fileno _fileno
#else
#ifdef __APPLE__
#include <sys/param.h>

View File

@@ -12,8 +12,7 @@
#include <thread>
#include <vector>
#ifdef _WIN32
#include <share.h> // For _SH_DENYWR
#include <windows.h> // For OutputDebugStringW
#include <share.h> // For _SH_DENYWR
#else
#define _SH_DENYWR 0
#endif
@@ -140,18 +139,12 @@ void FileBackend::Write(const Entry& entry) {
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
return;
}
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
if (entry.log_level >= Level::Error) {
file.Flush();
}
}
void DebuggerBackend::Write(const Entry& entry) {
#ifdef _WIN32
::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
#endif
}
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \
CLS(Log) \
@@ -203,7 +196,6 @@ void DebuggerBackend::Write(const Entry& entry) {
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCIE) \
@@ -212,12 +204,10 @@ void DebuggerBackend::Write(const Entry& entry) {
SUB(Service, PM) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, PSM) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, TCAP) \
SUB(Service, Time) \
SUB(Service, USB) \
SUB(Service, VI) \

View File

@@ -103,20 +103,6 @@ private:
std::size_t bytes_written;
};
/**
* Backend that writes to Visual Studio's output window
*/
class DebuggerBackend : public Backend {
public:
static const char* Name() {
return "debugger";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(std::string_view backend_name);

View File

@@ -83,7 +83,6 @@ enum class Class : ClassType {
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCIE, ///< The PCIe service
@@ -92,12 +91,10 @@ enum class Class : ClassType {
Service_PM, ///< The PM service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_PSM, ///< The PSM service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_TCAP, ///< The TCAP service.
Service_Time, ///< The time service
Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service

View File

@@ -4,12 +4,18 @@
#pragma once
#include <algorithm>
#include <cstdlib>
#include <type_traits>
namespace MathUtil {
constexpr float PI = 3.14159265f;
static constexpr float PI = 3.14159265f;
inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1,
unsigned length1) {
return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1));
}
template <class T>
struct Rectangle {
@@ -18,16 +24,16 @@ struct Rectangle {
T right{};
T bottom{};
constexpr Rectangle() = default;
Rectangle() = default;
constexpr Rectangle(T left, T top, T right, T bottom)
Rectangle(T left, T top, T right, T bottom)
: left(left), top(top), right(right), bottom(bottom) {}
T GetWidth() const {
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
return std::abs(static_cast<typename std::make_signed<T>::type>(right - left));
}
T GetHeight() const {
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top));
}
Rectangle<T> TranslateX(const T x) const {
return Rectangle{left + x, top, right + x, bottom};

177
src/common/memory_util.cpp Normal file
View File

@@ -0,0 +1,177 @@
// 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/logging/log.h"
#include "common/memory_util.h"
#ifdef _WIN32
#include <windows.h>
// Windows.h needs to be included before psapi.h
#include <psapi.h>
#include "common/common_funcs.h"
#include "common/string_util.h"
#else
#include <cstdlib>
#include <sys/mman.h>
#endif
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
#include <unistd.h>
#define PAGE_MASK (getpagesize() - 1)
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
#endif
// This is purposely not a full wrapper for virtualalloc/mmap, but it
// provides exactly the primitive operations that Dolphin needs.
void* AllocateExecutableMemory(std::size_t size, bool low) {
#if defined(_WIN32)
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
static char* map_hint = nullptr;
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
// This OS has no flag to enforce allocation below the 4 GB boundary,
// but if we hint that we want a low address it is very likely we will
// get one.
// An older version of this code used MAP_FIXED, but that has the side
// effect of discarding already mapped pages that happen to be in the
// requested virtual memory range (such as the emulated RAM, sometimes).
if (low && (!map_hint))
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
#endif
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
| (low ? MAP_32BIT : 0)
#endif
,
-1, 0);
#endif /* defined(_WIN32) */
#ifdef _WIN32
if (ptr == nullptr) {
#else
if (ptr == MAP_FAILED) {
ptr = nullptr;
#endif
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
}
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
else {
if (low) {
map_hint += size;
map_hint = (char*)round_page(map_hint); /* round up to the next page */
}
}
#endif
#if EMU_ARCH_BITS == 64
if ((u64)ptr >= 0x80000000 && low == true)
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
#endif
return ptr;
}
void* AllocateMemoryPages(std::size_t size) {
#ifdef _WIN32
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
#else
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED)
ptr = nullptr;
#endif
if (ptr == nullptr)
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
return ptr;
}
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
#ifdef _WIN32
void* ptr = _aligned_malloc(size, alignment);
#else
void* ptr = nullptr;
#ifdef ANDROID
ptr = memalign(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size) != 0)
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
#endif
#endif
if (ptr == nullptr)
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
return ptr;
}
void FreeMemoryPages(void* ptr, std::size_t size) {
if (ptr) {
#ifdef _WIN32
if (!VirtualFree(ptr, 0, MEM_RELEASE))
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
#else
munmap(ptr, size);
#endif
}
}
void FreeAlignedMemory(void* ptr) {
if (ptr) {
#ifdef _WIN32
_aligned_free(ptr);
#else
free(ptr);
#endif
}
}
void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
#else
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
#endif
}
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
&oldValue))
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
#else
mprotect(ptr, size,
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
#endif
}
std::string MemUsage() {
#ifdef _WIN32
#pragma comment(lib, "psapi")
DWORD processID = GetCurrentProcessId();
HANDLE hProcess;
PROCESS_MEMORY_COUNTERS pmc;
std::string Ret;
// Print information about the memory usage of the process.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
if (nullptr == hProcess)
return "MemUsage Error";
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
CloseHandle(hProcess);
return Ret;
#else
return "";
#endif
}

21
src/common/memory_util.h Normal file
View File

@@ -0,0 +1,21 @@
// 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 <cstddef>
#include <string>
void* AllocateExecutableMemory(std::size_t size, bool low = true);
void* AllocateMemoryPages(std::size_t size);
void FreeMemoryPages(void* ptr, std::size_t size);
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
void FreeAlignedMemory(void* ptr);
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
std::string MemUsage();
inline int GetPageSize() {
return 4096;
}

View File

@@ -12,7 +12,7 @@ template <typename T>
class Quaternion {
public:
Math::Vec3<T> xyz;
T w{};
T w;
Quaternion<decltype(-T{})> Inverse() const {
return {-xyz, w};

View File

@@ -4,10 +4,11 @@
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <codecvt>
#include <cstdio>
#include <cstdlib>
#include <locale>
#include <sstream>
#include <cstring>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -32,6 +33,24 @@ std::string ToUpper(std::string str) {
return str;
}
// For Debugging. Read out an u8 array.
std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
std::ostringstream oss;
oss << std::setfill('0') << std::hex;
for (int line = 0; size; ++data, --size) {
oss << std::setw(2) << (int)*data;
if (line_len == ++line) {
oss << '\n';
line = 0;
} else if (spaces)
oss << ' ';
}
return oss.str();
}
std::string StringFromBuffer(const std::vector<u8>& data) {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
}
@@ -56,6 +75,40 @@ std::string StripQuotes(const std::string& s) {
return s;
}
bool TryParse(const std::string& str, u32* const output) {
char* endptr = nullptr;
// Reset errno to a value other than ERANGE
errno = 0;
unsigned long value = strtoul(str.c_str(), &endptr, 0);
if (!endptr || *endptr)
return false;
if (errno == ERANGE)
return false;
#if ULONG_MAX > UINT_MAX
if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
return false;
#endif
*output = static_cast<u32>(value);
return true;
}
bool TryParse(const std::string& str, bool* const output) {
if ("1" == str || "true" == ToLower(str))
*output = true;
else if ("0" == str || "false" == ToLower(str))
*output = false;
else
return false;
return true;
}
std::string StringFromBool(bool value) {
return value ? "True" : "False";
}
@@ -214,15 +267,6 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
return std::string(buffer, len);
}
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
std::size_t max_len) {
std::size_t len = 0;
while (len < max_len && buffer[len] != '\0')
++len;
return std::u16string(buffer.begin(), buffer.begin() + len);
}
const char* TrimSourcePath(const char* path, const char* root) {
const char* p = path;

View File

@@ -5,6 +5,8 @@
#pragma once
#include <cstddef>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include "common/common_types.h"
@@ -17,13 +19,44 @@ std::string ToLower(std::string str);
/// Make a string uppercase
std::string ToUpper(std::string str);
std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
std::string StringFromBuffer(const std::vector<u8>& data);
std::string StripSpaces(const std::string& s);
std::string StripQuotes(const std::string& s);
// Thousand separator. Turns 12345678 into 12,345,678
template <typename I>
std::string ThousandSeparate(I value, int spaces = 0) {
std::ostringstream oss;
// std::locale("") seems to be broken on many platforms
#if defined _WIN32 || (defined __linux__ && !defined __clang__)
oss.imbue(std::locale(""));
#endif
oss << std::setw(spaces) << value;
return oss.str();
}
std::string StringFromBool(bool value);
bool TryParse(const std::string& str, bool* output);
bool TryParse(const std::string& str, u32* output);
template <typename N>
static bool TryParse(const std::string& str, N* const output) {
std::istringstream iss(str);
N tmp = 0;
if (iss >> tmp) {
*output = tmp;
return true;
} else
return false;
}
std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
@@ -66,14 +99,6 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
*/
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
/**
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
* max_len_bytes.
*/
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
std::size_t max_len);
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,

View File

@@ -153,7 +153,6 @@ struct VisitorInterface : NonCopyable {
/// Completion method, called once all fields have been visited
virtual void Complete() = 0;
virtual bool SubmitTestcase() = 0;
};
/**
@@ -179,9 +178,6 @@ struct NullVisitor : public VisitorInterface {
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
void Complete() override {}
bool SubmitTestcase() override {
return false;
}
};
/// Appends build-specific information to the given FieldCollection,

View File

@@ -25,6 +25,23 @@
namespace Common {
int CurrentThreadId() {
#ifdef _MSC_VER
return GetCurrentThreadId();
#elif defined __APPLE__
return mach_thread_self();
#else
return 0;
#endif
}
#ifdef _WIN32
// Supporting functions
void SleepCurrentThread(int ms) {
Sleep(ms);
}
#endif
#ifdef _MSC_VER
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
@@ -45,7 +62,7 @@ void SwitchCurrentThread() {
// This is implemented much nicer in upcoming msvc++, see:
// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
void SetCurrentThreadName(const char* name) {
void SetCurrentThreadName(const char* szThreadName) {
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
@@ -58,7 +75,7 @@ void SetCurrentThreadName(const char* name) {
#pragma pack(pop)
info.dwType = 0x1000;
info.szName = name;
info.szName = szThreadName;
info.dwThreadID = -1; // dwThreadID;
info.dwFlags = 0;
@@ -90,6 +107,10 @@ void SetCurrentThreadAffinity(u32 mask) {
}
#ifndef _WIN32
void SleepCurrentThread(int ms) {
usleep(1000 * ms);
}
void SwitchCurrentThread() {
usleep(1000 * 1);
}
@@ -97,15 +118,15 @@ void SwitchCurrentThread() {
// MinGW with the POSIX threading model does not support pthread_setname_np
#if !defined(_WIN32) || defined(_MSC_VER)
void SetCurrentThreadName(const char* name) {
void SetCurrentThreadName(const char* szThreadName) {
#ifdef __APPLE__
pthread_setname_np(name);
pthread_setname_np(szThreadName);
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name);
pthread_set_name_np(pthread_self(), szThreadName);
#elif defined(__NetBSD__)
pthread_setname_np(pthread_self(), "%s", (void*)name);
pthread_setname_np(pthread_self(), "%s", (void*)szThreadName);
#else
pthread_setname_np(pthread_self(), name);
pthread_setname_np(pthread_self(), szThreadName);
#endif
}
#endif

View File

@@ -13,8 +13,15 @@
namespace Common {
int CurrentThreadId();
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
void SetCurrentThreadAffinity(u32 mask);
class Event {
public:
Event() : is_set(false) {}
void Set() {
std::lock_guard<std::mutex> lk(mutex);
if (!is_set) {
@@ -46,14 +53,14 @@ public:
}
private:
bool is_set = false;
bool is_set;
std::condition_variable condvar;
std::mutex mutex;
};
class Barrier {
public:
explicit Barrier(std::size_t count_) : count(count_) {}
explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {}
/// Blocks until all "count" threads have called Sync()
void Sync() {
@@ -73,13 +80,12 @@ public:
private:
std::condition_variable condvar;
std::mutex mutex;
std::size_t count;
std::size_t waiting = 0;
std::size_t generation = 0; // Incremented once each time the barrier is used
const std::size_t count;
std::size_t waiting;
std::size_t generation; // Incremented once each time the barrier is used
};
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
void SetCurrentThreadAffinity(u32 mask);
void SleepCurrentThread(int ms);
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
void SetCurrentThreadName(const char* name);

View File

@@ -49,22 +49,6 @@ struct ThreadQueueList {
return T();
}
template <typename UnaryPredicate>
T get_first_filter(UnaryPredicate filter) const {
const Queue* cur = first;
while (cur != nullptr) {
if (!cur->data.empty()) {
for (const auto& item : cur->data) {
if (filter(item))
return item;
}
}
cur = cur->next_nonempty;
}
return T();
}
T pop_first() {
Queue* cur = first;
while (cur != nullptr) {

222
src/common/x64/xbyak_abi.h Normal file
View File

@@ -0,0 +1,222 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <initializer_list>
#include <xbyak.h>
#include "common/assert.h"
#include "common/bit_set.h"
namespace Common::X64 {
inline 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.");
ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
}
inline Xbyak::Reg64 IndexToReg64(int reg_index) {
ASSERT(reg_index < 16);
return Xbyak::Reg64(reg_index);
}
inline Xbyak::Xmm IndexToXmm(int reg_index) {
ASSERT(reg_index >= 16 && reg_index < 32);
return Xbyak::Xmm(reg_index - 16);
}
inline Xbyak::Reg IndexToReg(int reg_index) {
if (reg_index < 16) {
return IndexToReg64(reg_index);
} else {
return IndexToXmm(reg_index);
}
}
inline BitSet32 BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
BitSet32 bits;
for (const Xbyak::Reg& reg : regs) {
bits[RegToIndex(reg)] = true;
}
return bits;
}
const BitSet32 ABI_ALL_GPRS(0x0000FFFF);
const BitSet32 ABI_ALL_XMMS(0xFFFF0000);
#ifdef _WIN32
// Microsoft x64 ABI
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
});
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rsi,
Xbyak::util::rdi,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
// XMMs
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
#else
// System V x86-64 ABI
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::rdi,
Xbyak::util::rsi,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
});
constexpr std::size_t ABI_SHADOW_SPACE = 0;
#endif
inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
std::size_t needed_frame_size, s32* out_subtraction,
s32* out_xmm_offset) {
int count = (regs & ABI_ALL_GPRS).Count();
rsp_alignment -= count * 8;
std::size_t subtraction = 0;
int xmm_count = (regs & ABI_ALL_XMMS).Count();
if (xmm_count) {
// If we have any XMMs to save, we must align the stack here.
subtraction = rsp_alignment & 0xF;
}
subtraction += 0x10 * xmm_count;
std::size_t xmm_base_subtraction = subtraction;
subtraction += needed_frame_size;
subtraction += ABI_SHADOW_SPACE;
// Final alignment.
rsp_alignment -= subtraction;
subtraction += rsp_alignment & 0xF;
*out_subtraction = (s32)subtraction;
*out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
}
inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
std::size_t rsp_alignment,
std::size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
for (int reg_index : (regs & ABI_ALL_GPRS)) {
code.push(IndexToReg64(reg_index));
}
if (subtraction != 0) {
code.sub(code.rsp, subtraction);
}
for (int reg_index : (regs & ABI_ALL_XMMS)) {
code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(reg_index));
xmm_offset += 0x10;
}
return ABI_SHADOW_SPACE;
}
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
std::size_t rsp_alignment,
std::size_t needed_frame_size = 0) {
s32 subtraction, xmm_offset;
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
for (int reg_index : (regs & ABI_ALL_XMMS)) {
code.movaps(IndexToXmm(reg_index), code.xword[code.rsp + xmm_offset]);
xmm_offset += 0x10;
}
if (subtraction != 0) {
code.add(code.rsp, subtraction);
}
// GPRs need to be popped in reverse order
for (int reg_index = 15; reg_index >= 0; reg_index--) {
if (regs[reg_index]) {
code.pop(IndexToReg64(reg_index));
}
}
}
} // namespace Common::X64

View File

@@ -0,0 +1,47 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <type_traits>
#include <xbyak.h>
#include "common/x64/xbyak_abi.h"
namespace Common::X64 {
// Constants for use with cmpps/cmpss
enum {
CMP_EQ = 0,
CMP_LT = 1,
CMP_LE = 2,
CMP_UNORD = 3,
CMP_NEQ = 4,
CMP_NLT = 5,
CMP_NLE = 6,
CMP_ORD = 7,
};
inline bool IsWithin2G(uintptr_t ref, uintptr_t target) {
u64 distance = target - (ref + 5);
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
}
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
}
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
std::size_t addr = reinterpret_cast<std::size_t>(f);
if (IsWithin2G(code, addr)) {
code.call(f);
} else {
// ABI_RETURN is a safe temp register to use before a call
code.mov(ABI_RETURN, addr);
code.call(ABI_RETURN);
}
}
} // namespace Common::X64

View File

@@ -12,8 +12,6 @@ add_library(core STATIC
core_timing.h
core_timing_util.cpp
core_timing_util.h
cpu_core_manager.cpp
cpu_core_manager.h
crypto/aes_util.cpp
crypto/aes_util.h
crypto/encryption_layer.cpp
@@ -63,10 +61,6 @@ add_library(core STATIC
file_sys/sdmc_factory.h
file_sys/submission_package.cpp
file_sys/submission_package.h
file_sys/system_archive/ng_word.cpp
file_sys/system_archive/ng_word.h
file_sys/system_archive/system_archive.cpp
file_sys/system_archive/system_archive.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
@@ -83,10 +77,6 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
frontend/applets/software_keyboard.h
frontend/emu_window.cpp
frontend/emu_window.h
frontend/framebuffer_layout.cpp
@@ -103,6 +93,8 @@ add_library(core STATIC
hle/kernel/client_session.cpp
hle/kernel/client_session.h
hle/kernel/errors.h
hle/kernel/event.cpp
hle/kernel/event.h
hle/kernel/handle_table.cpp
hle/kernel/handle_table.h
hle/kernel/hle_ipc.cpp
@@ -115,10 +107,6 @@ add_library(core STATIC
hle/kernel/object.h
hle/kernel/process.cpp
hle/kernel/process.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/readable_event.cpp
hle/kernel/readable_event.h
hle/kernel/resource_limit.cpp
hle/kernel/resource_limit.h
hle/kernel/scheduler.cpp
@@ -141,8 +129,6 @@ add_library(core STATIC
hle/kernel/vm_manager.h
hle/kernel/wait_object.cpp
hle/kernel/wait_object.h
hle/kernel/writable_event.cpp
hle/kernel/writable_event.h
hle/lock.cpp
hle/lock.h
hle/result.h
@@ -164,22 +150,12 @@ add_library(core STATIC
hle/service/am/applet_ae.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
hle/service/am/applets/profile_select.cpp
hle/service/am/applets/profile_select.h
hle/service/am/applets/software_keyboard.cpp
hle/service/am/applets/software_keyboard.h
hle/service/am/applets/stub_applet.cpp
hle/service/am/applets/stub_applet.h
hle/service/am/idle.cpp
hle/service/am/idle.h
hle/service/am/omm.cpp
hle/service/am/omm.h
hle/service/am/spsm.cpp
hle/service/am/spsm.h
hle/service/am/tcap.cpp
hle/service/am/tcap.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
@@ -304,8 +280,6 @@ add_library(core STATIC
hle/service/nifm/nifm.h
hle/service/nim/nim.cpp
hle/service/nim/nim.h
hle/service/npns/npns.cpp
hle/service/npns/npns.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pl_u.cpp
@@ -353,8 +327,6 @@ add_library(core STATIC
hle/service/prepo/prepo.h
hle/service/psc/psc.cpp
hle/service/psc/psc.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
hle/service/service.cpp
hle/service/service.h
hle/service/set/set.cpp

View File

@@ -151,7 +151,6 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
config.cntfrq_el0 = 19200000; // Value from fusee.
// Unpredictable instructions
config.define_unpredictable_behaviour = true;

View File

@@ -8,14 +8,12 @@
#include <thread>
#include <utility>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/cpu_core_manager.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
@@ -25,13 +23,12 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/software_keyboard.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "frontend/applets/software_keyboard.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
@@ -41,6 +38,7 @@ namespace Core {
/*static*/ System System::s_instance;
namespace {
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files.
@@ -69,26 +67,66 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
}
if (FileUtil::IsDirectory(path))
return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read);
return vfs->OpenFile(path, FileSys::Mode::Read);
}
struct System::Impl {
/// Runs a CPU core while the system is powered on
void RunCpuCore(Cpu& cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state.RunLoop(true);
}
}
} // Anonymous namespace
struct System::Impl {
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core];
}
ResultStatus RunLoop(bool tight_loop) {
status = ResultStatus::Success;
cpu_core_manager.RunLoop(tight_loop);
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return ResultStatus::Success;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
ResultStatus Init(Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
@@ -98,15 +136,15 @@ struct System::Impl {
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
/// Create default implementations of applets if one is not provided.
if (profile_selector == nullptr)
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
if (software_keyboard == nullptr)
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
auto main_process = Kernel::Process::Create(kernel, "main");
kernel.MakeCurrentProcess(main_process.get());
cpu_barrier = std::make_unique<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -120,8 +158,17 @@ struct System::Impl {
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
cpu_core_manager.Initialize(system);
is_powered_on = true;
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
if (Settings::values.use_multi_core) {
for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
}
}
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
@@ -131,15 +178,14 @@ struct System::Impl {
return ResultStatus::Success;
}
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
}
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
if (system_mode.second != Loader::ResultStatus::Success) {
@@ -149,7 +195,7 @@ struct System::Impl {
return ResultStatus::ErrorSystemMode;
}
ResultStatus init_result{Init(system, emu_window)};
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
@@ -179,8 +225,6 @@ struct System::Impl {
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
is_powered_on = false;
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
@@ -190,7 +234,19 @@ struct System::Impl {
gpu_core.reset();
// Close all CPU/threading state
cpu_core_manager.Shutdown();
cpu_barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : cpu_core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_exclusive_monitor.reset();
cpu_barrier.reset();
// Shutdown kernel and core timing
kernel.Shutdown();
@@ -227,12 +283,11 @@ struct System::Impl {
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
/// Frontend applets
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::unique_ptr<CpuBarrier> cpu_barrier;
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
std::size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -243,6 +298,9 @@ struct System::Impl {
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, Cpu*> thread_to_cpu;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
};
@@ -254,10 +312,6 @@ Cpu& System::CurrentCpuCore() {
return impl->CurrentCpuCore();
}
const Cpu& System::CurrentCpuCore() const {
return impl->CurrentCpuCore();
}
System::ResultStatus System::RunLoop(bool tight_loop) {
return impl->RunLoop(tight_loop);
}
@@ -267,15 +321,17 @@ System::ResultStatus System::SingleStep() {
}
void System::InvalidateCpuInstructionCaches() {
impl->cpu_core_manager.InvalidateAllInstructionCaches();
for (auto& cpu : impl->cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
return impl->Load(*this, emu_window, filepath);
return impl->Load(emu_window, filepath);
}
bool System::IsPoweredOn() const {
return impl->is_powered_on;
return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
}
void System::PrepareReschedule() {
@@ -286,11 +342,7 @@ PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}
TelemetrySession& System::TelemetrySession() {
return *impl->telemetry_session;
}
const TelemetrySession& System::TelemetrySession() const {
Core::TelemetrySession& System::TelemetrySession() const {
return *impl->telemetry_session;
}
@@ -298,11 +350,7 @@ ARM_Interface& System::CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
const ARM_Interface& System::CurrentArmInterface() const {
return CurrentCpuCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() const {
std::size_t System::CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
@@ -310,10 +358,6 @@ Kernel::Scheduler& System::CurrentScheduler() {
return CurrentCpuCore().Scheduler();
}
const Kernel::Scheduler& System::CurrentScheduler() const {
return CurrentCpuCore().Scheduler();
}
Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
return CpuCore(core_index).Scheduler();
}
@@ -334,25 +378,18 @@ ARM_Interface& System::ArmInterface(std::size_t core_index) {
return CpuCore(core_index).ArmInterface();
}
const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
return CpuCore(core_index).ArmInterface();
}
Cpu& System::CpuCore(std::size_t core_index) {
return impl->cpu_core_manager.GetCore(core_index);
ASSERT(core_index < NUM_CPU_CORES);
return *impl->cpu_cores[core_index];
}
const Cpu& System::CpuCore(std::size_t core_index) const {
ASSERT(core_index < NUM_CPU_CORES);
return impl->cpu_core_manager.GetCore(core_index);
return *impl->cpu_cores[core_index];
}
ExclusiveMonitor& System::Monitor() {
return impl->cpu_core_manager.GetExclusiveMonitor();
}
const ExclusiveMonitor& System::Monitor() const {
return impl->cpu_core_manager.GetExclusiveMonitor();
return *impl->cpu_exclusive_monitor;
}
Tegra::GPU& System::GPU() {
@@ -427,24 +464,8 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) {
impl->profile_selector = std::move(applet);
}
const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
return *impl->profile_selector;
}
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
impl->software_keyboard = std::move(applet);
}
const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
return *impl->software_keyboard;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
return impl->Init(emu_window);
}
void System::Shutdown() {

View File

@@ -9,13 +9,10 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/kernel/object.h"
#include "frontend/applets/profile_select.h"
namespace Core::Frontend {
class EmuWindow;
class SoftwareKeyboardApplet;
} // namespace Core::Frontend
namespace FileSys {
@@ -57,9 +54,6 @@ class TelemetrySession;
struct PerfStatsResults;
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path);
class System {
public:
System(const System&) = delete;
@@ -135,11 +129,11 @@ public:
*/
bool IsPoweredOn() const;
/// Gets a reference to the telemetry session for this emulation session.
Core::TelemetrySession& TelemetrySession();
/// Gets a reference to the telemetry session for this emulation session.
const Core::TelemetrySession& TelemetrySession() const;
/**
* Returns a reference to the telemetry session for this emulation session.
* @returns Reference to the telemetry session.
*/
Core::TelemetrySession& TelemetrySession() const;
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
@@ -150,36 +144,24 @@ public:
/// Gets an ARM interface to the CPU core that is currently running
ARM_Interface& CurrentArmInterface();
/// Gets an ARM interface to the CPU core that is currently running
const ARM_Interface& CurrentArmInterface() const;
/// Gets the index of the currently running CPU core
std::size_t CurrentCoreIndex() const;
std::size_t CurrentCoreIndex();
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler();
/// Gets the scheduler for the CPU core that is currently running
const Kernel::Scheduler& CurrentScheduler() const;
/// Gets a reference to an ARM interface for the CPU core with the specified index
/// Gets an ARM interface to the CPU core with the specified index
ARM_Interface& ArmInterface(std::size_t core_index);
/// Gets a const reference to an ARM interface from the CPU core with the specified index
const ARM_Interface& ArmInterface(std::size_t core_index) const;
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(std::size_t core_index);
/// Gets a CPU interface to the CPU core with the specified index
const Cpu& CpuCore(std::size_t core_index) const;
/// Gets a reference to the exclusive monitor
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor();
/// Gets a constant reference to the exclusive monitor
const ExclusiveMonitor& Monitor() const;
/// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU();
@@ -242,23 +224,12 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet);
const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const;
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
private:
System();
/// Returns the currently running CPU core
Cpu& CurrentCpuCore();
/// Returns the currently running CPU core
const Cpu& CurrentCpuCore() const;
/**
* Initialize the emulated system.
* @param emu_window Reference to the host-system window used for video output and keyboard

View File

@@ -1,142 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/cpu_core_manager.h"
#include "core/gdbstub/gdbstub.h"
#include "core/settings.h"
namespace Core {
namespace {
void RunCpuCore(const System& system, Cpu& cpu_state) {
while (system.IsPoweredOn()) {
cpu_state.RunLoop(true);
}
}
} // Anonymous namespace
CpuCoreManager::CpuCoreManager() = default;
CpuCoreManager::~CpuCoreManager() = default;
void CpuCoreManager::Initialize(System& system) {
barrier = std::make_unique<CpuBarrier>();
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
for (std::size_t index = 0; index < cores.size(); ++index) {
cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
}
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
if (!Settings::values.use_multi_core) {
return;
}
for (std::size_t index = 0; index < core_threads.size(); ++index) {
core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
std::ref(*cores[index + 1]));
thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
}
}
void CpuCoreManager::Shutdown() {
barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cores) {
cpu_core.reset();
}
exclusive_monitor.reset();
barrier.reset();
}
Cpu& CpuCoreManager::GetCore(std::size_t index) {
return *cores.at(index);
}
const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
return *cores.at(index);
}
ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
return *exclusive_monitor;
}
const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
return *exclusive_monitor;
}
Cpu& CpuCoreManager::GetCurrentCore() {
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cores[active_core];
}
const Cpu& CpuCoreManager::GetCurrentCore() const {
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cores[active_core];
}
void CpuCoreManager::RunLoop(bool tight_loop) {
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
}
void CpuCoreManager::InvalidateAllInstructionCaches() {
for (auto& cpu : cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
} // namespace Core

View File

@@ -1,59 +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 <map>
#include <memory>
#include <thread>
namespace Core {
class Cpu;
class CpuBarrier;
class ExclusiveMonitor;
class System;
class CpuCoreManager {
public:
CpuCoreManager();
CpuCoreManager(const CpuCoreManager&) = delete;
CpuCoreManager(CpuCoreManager&&) = delete;
~CpuCoreManager();
CpuCoreManager& operator=(const CpuCoreManager&) = delete;
CpuCoreManager& operator=(CpuCoreManager&&) = delete;
void Initialize(System& system);
void Shutdown();
Cpu& GetCore(std::size_t index);
const Cpu& GetCore(std::size_t index) const;
Cpu& GetCurrentCore();
const Cpu& GetCurrentCore() const;
ExclusiveMonitor& GetExclusiveMonitor();
const ExclusiveMonitor& GetExclusiveMonitor() const;
void RunLoop(bool tight_loop);
void InvalidateAllInstructionCaches();
private:
static constexpr std::size_t NUM_CPU_CORES = 4;
std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
std::unique_ptr<CpuBarrier> barrier;
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
std::size_t active_core{}; ///< Active core, only used in single thread mode
/// Map of guest threads to CPU cores
std::map<std::thread::id, Cpu*> thread_to_cpu;
};
} // namespace Core

View File

@@ -141,28 +141,28 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
return mac_key;
}
std::optional<Key128> DeriveSDSeed() {
boost::optional<Key128> DeriveSDSeed() {
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000043",
"rb+");
if (!save_43.IsOpen())
return {};
return boost::none;
const FileUtil::IOFile sd_private(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
if (!sd_private.IsOpen())
return {};
return boost::none;
std::array<u8, 0x10> private_seed{};
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
return {};
return boost::none;
}
std::array<u8, 0x10> buffer{};
std::size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
if (!save_43.Seek(offset, SEEK_SET)) {
return {};
return boost::none;
}
save_43.ReadBytes(buffer.data(), buffer.size());
@@ -172,12 +172,12 @@ std::optional<Key128> DeriveSDSeed() {
}
if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
return {};
return boost::none;
}
Key128 seed{};
if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
return {};
return boost::none;
}
return seed;
}
@@ -246,6 +246,7 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
}
std::vector<TicketRaw> out;
u32 magic{};
for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
buffer[offset + 3] == 0x0) {
@@ -290,26 +291,26 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
}
template <size_t size>
static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
u64 offset = 0;
for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
if (data[i] == 0x1) {
offset = i + 1;
break;
} else if (data[i] != 0x0) {
return {};
return boost::none;
}
}
return offset;
}
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& key) {
boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& key) {
u32 cert_authority;
std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
if (cert_authority == 0)
return {};
return boost::none;
if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
LOG_INFO(Crypto,
"Attempting to parse ticket with non-standard certificate authority {:08X}.",
@@ -320,7 +321,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
if (rights_id == Key128{})
return {};
return boost::none;
Key128 key_temp{};
@@ -355,17 +356,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
if (m_0 != 0)
return {};
return boost::none;
m_1 = m_1 ^ MGF1<0x20>(m_2);
m_2 = m_2 ^ MGF1<0xDF>(m_1);
const auto offset = FindTicketOffset(m_2);
if (!offset)
return {};
ASSERT(*offset > 0);
if (offset == boost::none)
return boost::none;
ASSERT(offset.get() > 0);
std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size());
return std::make_pair(rights_id, key_temp);
}
@@ -394,7 +395,7 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
if (base.size() < begin + length)
return false;
return std::all_of(base.begin() + begin, base.begin() + begin + length,
[](u8 c) { return std::isxdigit(c); });
[](u8 c) { return std::isdigit(c); });
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -660,8 +661,8 @@ void KeyManager::DeriveSDSeedLazy() {
return;
const auto res = DeriveSDSeed();
if (res)
SetKey(S128KeyType::SDSeed, *res);
if (res != boost::none)
SetKey(S128KeyType::SDSeed, res.get());
}
static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
@@ -712,6 +713,7 @@ void KeyManager::DeriveBase() {
const auto sbk = GetKey(S128KeyType::SecureBoot);
const auto tsec = GetKey(S128KeyType::TSEC);
const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
for (size_t i = 0; i < revisions.size(); ++i) {
if (!revisions[i])
@@ -793,7 +795,7 @@ void KeyManager::DeriveBase() {
void KeyManager::DeriveETicket(PartitionDataManager& data) {
// ETicket keys
const auto es = Service::FileSystem::GetUnionContents().GetEntry(
const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
0x0100000000000033, FileSys::ContentRecordType::Program);
if (es == nullptr)
@@ -888,9 +890,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
for (const auto& raw : res) {
const auto pair = ParseTicket(raw, rsa_key);
if (!pair)
if (pair == boost::none)
continue;
const auto& [rid, key] = *pair;
const auto& [rid, key] = pair.value();
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);

View File

@@ -6,10 +6,9 @@
#include <array>
#include <map>
#include <optional>
#include <string>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/common_types.h"
#include "core/crypto/partition_data_manager.h"
@@ -192,14 +191,14 @@ Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master
std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
const Key128& key);
std::optional<Key128> DeriveSDSeed();
boost::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
// 0x140-0x144 is zero)
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& eticket_extended_key);
boost::optional<std::pair<Key128, Key128>> ParseTicket(
const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
} // namespace Core::Crypto

View File

@@ -516,8 +516,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
out.insert(out.end(), rodata.begin(), rodata.end());
out.insert(out.end(), data.begin(), data.end());
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
offset += sizeof(KIPHeader) + out.size();
if (name == "FS")
package2_fs[static_cast<size_t>(type)] = std::move(out);

View File

@@ -8,9 +8,8 @@
namespace FileSys {
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
dump_root(std::move(dump_root_)),
sysnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_unique<RegisteredCache>(
@@ -33,10 +32,4 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
}
VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
if (title_id == 0)
return nullptr;
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
}
} // namespace FileSys

View File

@@ -17,19 +17,17 @@ class RegisteredCache;
/// registered caches.
class BISFactory {
public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
~BISFactory();
RegisteredCache* GetSystemNANDContents() const;
RegisteredCache* GetUserNANDContents() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
VirtualDir GetModificationDumpRoot(u64 title_id) const;
private:
VirtualDir nand_root;
VirtualDir load_root;
VirtualDir dump_root;
std::unique_ptr<RegisteredCache> sysnand_cache;
std::unique_ptr<RegisteredCache> usrnand_cache;

View File

@@ -168,6 +168,10 @@ VirtualDir XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorXCIMissingPartition;
@@ -176,7 +180,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
continue;
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
auto nca = std::make_shared<NCA>(file);
// TODO(DarkLordZach): Add proper Rev1+ Support
if (nca->IsUpdate())
continue;

View File

@@ -9,7 +9,6 @@
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs.h"
namespace Loader {
@@ -32,18 +31,7 @@ enum class GamecardSize : u8 {
};
struct GamecardInfo {
u64_le firmware_version;
u32_le access_control_flags;
u32_le read_wait_time1;
u32_le read_wait_time2;
u32_le write_wait_time1;
u32_le write_wait_time2;
u32_le firmware_mode;
u32_le cup_version;
std::array<u8, 4> reserved1;
u64_le update_partition_hash;
u64_le cup_id;
std::array<u8, 0x38> reserved2;
std::array<u8, 0x70> data;
};
static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
@@ -106,6 +94,9 @@ public:
VirtualDir GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
@@ -119,7 +110,5 @@ private:
std::shared_ptr<NSP> secure_partition;
std::shared_ptr<NCA> program;
std::vector<std::shared_ptr<NCA>> ncas;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -4,9 +4,10 @@
#include <algorithm>
#include <cstring>
#include <optional>
#include <utility>
#include <boost/optional.hpp>
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
@@ -101,9 +102,8 @@ static bool IsValidNCA(const NCAHeader& header) {
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
Core::Crypto::KeyManager keys_)
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
if (file == nullptr) {
status = Loader::ResultStatus::ErrorNullFile;
return;
@@ -306,18 +306,18 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
subsection_buckets.back().entries.push_back({size, {0}, 0});
std::optional<Core::Crypto::Key128> key = {};
boost::optional<Core::Crypto::Key128> key = boost::none;
if (encrypted) {
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (!key) {
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return false;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
if (!key) {
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return false;
}
@@ -332,7 +332,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
auto bktr = std::make_shared<BKTR>(
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
section.raw.section_ctr);
// BKTR applies to entire IVFC, so make an offset version to level 6
@@ -388,11 +388,11 @@ u8 NCA::GetCryptoRevision() const {
return master_key_id;
}
std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
const auto master_key_id = GetCryptoRevision();
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
return {};
return boost::none;
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -416,25 +416,25 @@ std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type
return out;
}
std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
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;
return {};
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;
return {};
return boost::none;
}
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
status = Loader::ResultStatus::ErrorMissingTitlekek;
return {};
return boost::none;
}
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -458,25 +458,25 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
case NCASectionCryptoType::BKTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
std::optional<Core::Crypto::Key128> key = {};
boost::optional<Core::Crypto::Key128> key = boost::none;
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (!key) {
if (key == boost::none) {
if (status == Loader::ResultStatus::Success)
status = Loader::ResultStatus::ErrorMissingTitlekey;
return nullptr;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
if (!key) {
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return nullptr;
}
}
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
starting_offset);
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
std::move(in), key.value(), starting_offset);
std::vector<u8> iv(16);
for (u8 i = 0; i < 8; ++i)
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
@@ -546,4 +546,7 @@ u64 NCA::GetBaseIVFCOffset() const {
return ivfc_offset;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -6,10 +6,9 @@
#include <array>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -79,8 +78,7 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
u64 bktr_base_ivfc_offset = 0,
Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
u64 bktr_base_ivfc_offset = 0);
~NCA() override;
Loader::ResultStatus GetStatus() const;
@@ -102,6 +100,9 @@ public:
// Returns the base ivfc offset used in BKTR patching.
u64 GetBaseIVFCOffset() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
bool CheckSupportedNCA(const NCAHeader& header);
bool HandlePotentialHeaderDecryption();
@@ -113,8 +114,8 @@ private:
bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
u8 GetCryptoRevision() const;
std::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
std::optional<Core::Crypto::Key128> GetTitlekey();
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
boost::optional<Core::Crypto::Key128> GetTitlekey();
VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
std::vector<VirtualDir> dirs;

View File

@@ -8,23 +8,13 @@
namespace FileSys {
const std::array<const char*, 15> LANGUAGE_NAMES{{
"AmericanEnglish",
"BritishEnglish",
"Japanese",
"French",
"German",
"LatinAmericanSpanish",
"Spanish",
"Italian",
"Dutch",
"CanadianFrench",
"Portuguese",
"Russian",
"Korean",
"Taiwanese",
"Chinese",
}};
const std::array<const char*, 15> LANGUAGE_NAMES = {
"AmericanEnglish", "BritishEnglish", "Japanese",
"French", "German", "LatinAmericanSpanish",
"Spanish", "Italian", "Dutch",
"CanadianFrench", "Portugese", "Russian",
"Korean", "Taiwanese", "Chinese",
};
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
@@ -36,20 +26,18 @@ std::string LanguageEntry::GetDeveloperName() const {
developer_name.size());
}
NACP::NACP() = default;
NACP::NACP(VirtualFile file) {
file->ReadObject(&raw);
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
}
NACP::~NACP() = default;
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
if (language != Language::Default) {
return raw.language_entries.at(static_cast<u8>(language));
return raw->language_entries.at(static_cast<u8>(language));
}
for (const auto& language_entry : raw.language_entries) {
for (const auto& language_entry : raw->language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
@@ -67,29 +55,15 @@ std::string NACP::GetDeveloperName(Language language) const {
}
u64 NACP::GetTitleId() const {
return raw.title_id;
return raw->title_id;
}
u64 NACP::GetDLCBaseTitleId() const {
return raw.dlc_base_title_id;
return raw->dlc_base_title_id;
}
std::string NACP::GetVersionString() const {
return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(),
raw.version_string.size());
}
u64 NACP::GetDefaultNormalSaveSize() const {
return raw.normal_save_data_size;
}
u64 NACP::GetDefaultJournalSaveSize() const {
return raw.journal_sava_data_size;
}
std::vector<u8> NACP::GetRawBytes() const {
std::vector<u8> out(sizeof(RawNACP));
std::memcpy(out.data(), &raw, sizeof(RawNACP));
return out;
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
raw->version_string.size());
}
} // namespace FileSys

View File

@@ -28,30 +28,17 @@ static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size.
// The raw file format of a NACP file.
struct RawNACP {
std::array<LanguageEntry, 16> language_entries;
std::array<u8, 0x25> isbn;
u8 startup_user_account;
INSERT_PADDING_BYTES(2);
u32_le application_attribute;
u32_le supported_languages;
u32_le parental_control;
bool screenshot_enabled;
u8 video_capture_mode;
bool data_loss_confirmation;
INSERT_PADDING_BYTES(1);
INSERT_PADDING_BYTES(0x38);
u64_le title_id;
std::array<u8, 0x20> rating_age;
INSERT_PADDING_BYTES(0x20);
std::array<char, 0x10> version_string;
u64_le dlc_base_title_id;
u64_le title_id_2;
u64_le normal_save_data_size;
u64_le journal_sava_data_size;
INSERT_PADDING_BYTES(0x18);
INSERT_PADDING_BYTES(0x28);
u64_le product_code;
std::array<u64_le, 0x8> local_communication;
u8 logo_type;
u8 logo_handling;
bool runtime_add_on_content_install;
INSERT_PADDING_BYTES(5);
u64_le title_id_3;
std::array<u64_le, 0x7> title_id_array;
INSERT_PADDING_BYTES(0x8);
u64_le title_id_update;
std::array<u8, 0x40> bcat_passphrase;
INSERT_PADDING_BYTES(0xEC0);
@@ -85,7 +72,6 @@ extern const std::array<const char*, 15> LANGUAGE_NAMES;
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {
public:
explicit NACP();
explicit NACP(VirtualFile file);
~NACP();
@@ -95,12 +81,9 @@ public:
u64 GetTitleId() const;
u64 GetDLCBaseTitleId() const;
std::string GetVersionString() const;
u64 GetDefaultNormalSaveSize() const;
u64 GetDefaultJournalSaveSize() const;
std::vector<u8> GetRawBytes() const;
private:
RawNACP raw{};
std::unique_ptr<RawNACP> raw;
};
} // namespace FileSys

View File

@@ -29,8 +29,8 @@ struct Entry {
filename[copy_size] = '\0';
}
char filename[0x301];
INSERT_PADDING_BYTES(3);
char filename[0x300];
INSERT_PADDING_BYTES(4);
EntryType type;
INSERT_PADDING_BYTES(3);
u64 file_size;

View File

@@ -8,10 +8,25 @@
namespace FileSys {
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
namespace ErrCodes {
enum {
NotFound = 1,
TitleNotFound = 1002,
SdCardNotFound = 2001,
RomFSNotFound = 2520,
};
}
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERROR_INVALID_PATH(-1);
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
} // namespace FileSys

View File

@@ -27,6 +27,7 @@
#include <map>
#include <memory>
#include <string>
#include <boost/detail/container_fwd.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"

View File

@@ -99,16 +99,16 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
u16 rle_size{};
if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
return nullptr;
rle_size = Common::swap16(rle_size);
rle_size = Common::swap16(data_size);
offset += sizeof(u16);
const auto data = ips->ReadByte(offset++);
if (!data)
if (data == boost::none)
return nullptr;
if (real_offset + rle_size > in_data.size())
rle_size = static_cast<u16>(in_data.size() - real_offset);
std::memset(in_data.data() + real_offset, *data, rle_size);
std::memset(in_data.data() + real_offset, data.get(), rle_size);
} else { // Standard Patch
auto read = data_size;
if (real_offset + read > in_data.size())

View File

@@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
}
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
return {};
return pfs_dirs;
}
std::string PartitionFilesystem::GetName() const {
@@ -103,4 +103,18 @@ void PartitionFilesystem::PrintDebugInfo() const {
pfs_files[i]->GetName(), pfs_files[i]->GetSize());
}
}
bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
if (iter == pfs_files.end())
return false;
const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
pfs_files[offset] = std::move(pfs_files.back());
pfs_files.pop_back();
pfs_dirs.emplace_back(std::move(dir));
return true;
}
} // namespace FileSys

View File

@@ -35,6 +35,9 @@ public:
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
void PrintDebugInfo() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
struct Header {
u32_le magic;
@@ -81,6 +84,7 @@ private:
std::size_t content_offset = 0;
std::vector<VirtualFile> pfs_files;
std::vector<VirtualDir> pfs_dirs;
};
} // namespace FileSys

View File

@@ -19,18 +19,12 @@
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace FileSys {
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
"main", "main.npdm", "rtld", "sdk", "subsdk0", "subsdk1", "subsdk2",
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
};
struct NSOBuildHeader {
u32_le magic;
INSERT_PADDING_BYTES(0x3C);
@@ -56,82 +50,34 @@ PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
PatchManager::~PatchManager() = default;
u64 PatchManager::GetTitleID() const {
return title_id;
}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr)
return exefs;
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
}
}
const auto installed = Service::FileSystem::GetUnionContents();
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed.GetEntry(update_tid, ContentRecordType::Program);
if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr &&
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
exefs = update->GetExeFS();
}
// LayeredExeFS
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (load_dir != nullptr && load_dir->GetSize() > 0) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(
patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
std::vector<VirtualDir> layers;
layers.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
continue;
auto exefs_dir = subdir->GetSubdirectory("exefs");
if (exefs_dir != nullptr)
layers.push_back(std::move(exefs_dir));
}
layers.push_back(exefs);
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
if (layered != nullptr) {
LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
exefs = std::move(layered);
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
if (update != nullptr) {
if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
update->GetExeFS() != nullptr) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
exefs = update->GetExeFS();
}
}
return exefs;
}
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const {
const auto& disabled = Settings::values.disabled_addons[title_id];
static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) {
std::vector<VirtualFile> out;
out.reserve(patch_dirs.size());
for (const auto& subdir : patch_dirs) {
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
continue;
auto exefs_dir = subdir->GetSubdirectory("exefs");
if (exefs_dir != nullptr) {
for (const auto& file : exefs_dir->GetFiles()) {
@@ -174,18 +120,6 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
const auto build_id_raw = Common::HexArrayToString(header.build_id);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
file->Resize(nso.size());
file->WriteBytes(nso);
}
}
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
@@ -234,8 +168,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
load_dir == nullptr || load_dir->GetSize() <= 0) {
if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
}
@@ -244,7 +177,6 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
return;
}
const auto& disabled = Settings::values.disabled_addons[title_id];
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -254,9 +186,6 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
layers.reserve(patch_dirs.size() + 1);
layers_ext.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
continue;
auto romfs_dir = subdir->GetSubdirectory("romfs");
if (romfs_dir != nullptr)
layers.push_back(std::move(romfs_dir));
@@ -286,12 +215,13 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
VirtualFile update_raw) const {
const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
title_id, static_cast<u8>(type));
title_id, static_cast<u8>(type))
.c_str();
if (type == ContentRecordType::Program || type == ContentRecordType::Data)
LOG_INFO(Loader, "{}", log_string);
if (type == ContentRecordType::Program)
LOG_INFO(Loader, log_string);
else
LOG_DEBUG(Loader, "{}", log_string);
LOG_DEBUG(Loader, log_string);
if (romfs == nullptr)
return romfs;
@@ -300,21 +230,16 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed.GetEntryRaw(update_tid, type);
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
if (!update_disabled && update != nullptr) {
const auto update = installed->GetEntryRaw(update_tid, type);
if (update != nullptr) {
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
romfs = new_nca->GetRomFS();
}
} else if (!update_disabled && update_raw != nullptr) {
} else if (update_raw != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
@@ -344,30 +269,26 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
VirtualFile update_raw) const {
std::map<std::string, std::string, std::less<>> out;
const auto installed = Service::FileSystem::GetUnionContents();
const auto& disabled = Settings::values.disabled_addons[title_id];
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
PatchManager update{update_tid};
auto [nacp, discard_icon_file] = update.GetControlMetadata();
const auto update_disabled =
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
const auto update_label = update_disabled ? "[D] Update" : "Update";
if (nacp != nullptr) {
out.insert_or_assign(update_label, nacp->GetVersionString());
out.insert_or_assign("Update", nacp->GetVersionString());
} else {
if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = installed.GetEntryVersion(update_tid);
if (meta_ver.value_or(0) == 0) {
out.insert_or_assign(update_label, "");
if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = installed->GetEntryVersion(update_tid);
if (meta_ver == boost::none || meta_ver.get() == 0) {
out.insert_or_assign("Update", "");
} else {
out.insert_or_assign(
update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
"Update",
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
}
} else if (update_raw != nullptr) {
out.insert_or_assign(update_label, "PACKED");
out.insert_or_assign("Update", "PACKED");
}
}
@@ -381,25 +302,18 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
if (IsDirValidAndNonEmpty(exefs_dir)) {
bool ips = false;
bool ipswitch = false;
bool layeredfs = false;
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips") {
if (file->GetExtension() == "ips")
ips = true;
} else if (file->GetExtension() == "pchtxt") {
else if (file->GetExtension() == "pchtxt")
ipswitch = true;
} else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(),
file->GetName()) != EXEFS_FILE_NAMES.end()) {
layeredfs = true;
}
}
if (ips)
AppendCommaIfNotEmpty(types, "IPS");
if (ipswitch)
AppendCommaIfNotEmpty(types, "IPSwitch");
if (layeredfs)
AppendCommaIfNotEmpty(types, "LayeredExeFS");
}
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
AppendCommaIfNotEmpty(types, "LayeredFS");
@@ -407,20 +321,19 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
if (types.empty())
continue;
const auto mod_disabled =
std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types);
out.insert_or_assign(mod->GetName(), types);
}
}
// DLC
const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<RegisteredCacheEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
[this, &installed](const RegisteredCacheEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
installed->GetEntry(entry)->GetStatus() ==
Loader::ResultStatus::Success;
});
if (!dlc_match.empty()) {
// Ensure sorted so DLC IDs show in order.
@@ -432,9 +345,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
list += fmt::format("{}", dlc_match.back().title_id & 0x7FF);
const auto dlc_disabled =
std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end();
out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list));
out.insert_or_assign("DLC", std::move(list));
}
return out;
@@ -443,7 +354,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)
return {};

View File

@@ -30,8 +30,6 @@ public:
explicit PatchManager(u64 title_id);
~PatchManager();
u64 GetTitleID() const;
// Currently tracked ExeFS patches:
// - Game Updates
VirtualDir PatchExeFS(VirtualDir exefs) const;
@@ -65,9 +63,6 @@ public:
std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
private:
std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const;
u64 title_id;
};

View File

@@ -40,13 +40,6 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
return Loader::ResultStatus::ErrorBadFileAccessHeader;
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
const u64 read_size = aci_header.kac_size;
const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset;
if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) {
return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors;
}
return Loader::ResultStatus::Success;
}
@@ -78,10 +71,6 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
return aci_file_access.permissions;
}
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
return aci_kernel_capabilities;
}
void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
@@ -92,19 +81,15 @@ void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
npdm_header.has_64_bit_instructions ? "YES" : "NO");
const char* address_space = "Unknown";
auto address_space = "Unknown";
switch (npdm_header.address_space_type) {
case ProgramAddressSpaceType::Is36Bit:
address_space = "64-bit (36-bit address space)";
break;
case ProgramAddressSpaceType::Is39Bit:
address_space = "64-bit (39-bit address space)";
address_space = "64-bit";
break;
case ProgramAddressSpaceType::Is32Bit:
address_space = "32-bit";
break;
case ProgramAddressSpaceType::Is32BitNoMap:
address_space = "32-bit (no map region)";
address_space = "32-bit";
break;
}

View File

@@ -5,7 +5,6 @@
#pragma once
#include <array>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -39,8 +38,6 @@ enum class ProgramFilePermission : u64 {
*/
class ProgramMetadata {
public:
using KernelCapabilityDescriptors = std::vector<u32>;
ProgramMetadata();
~ProgramMetadata();
@@ -53,7 +50,6 @@ public:
u32 GetMainThreadStackSize() const;
u64 GetTitleID() const;
u64 GetFilesystemPermissions() const;
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
void Print() const;
@@ -158,8 +154,6 @@ private:
FileAccessControl acid_file_access;
FileAccessHeader aci_file_access;
KernelCapabilityDescriptors aci_kernel_capabilities;
};
} // namespace FileSys

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
@@ -31,14 +30,6 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
}
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
}
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return !operator==(lhs, rhs);
}
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);
@@ -106,42 +97,40 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
if (file != nullptr) {
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 (std::size_t i = 0; i < 0x100; ++i) {
auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
if (next != nullptr) {
concat.push_back(std::move(next));
} 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 = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
}
return file;
}
const auto nca_dir = dir->GetDirectoryRelative(path);
if (nca_dir == nullptr) {
return nullptr;
}
const auto files = nca_dir->GetFiles();
if (files.size() == 1 && files[0]->GetName() == "00") {
return files[0];
}
std::vector<VirtualFile> concat;
// Since the files are a two-digit hex number, max is FF.
for (std::size_t i = 0; i < 0x100; ++i) {
auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
if (next != nullptr) {
concat.push_back(std::move(next));
} 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;
}
return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
return nullptr;
}
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
@@ -161,28 +150,28 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
return file;
}
static std::optional<NcaID> CheckMapForContentRecord(
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 {};
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 {};
return boost::none;
return std::make_optional(iter->nca_id);
return boost::make_optional(iter->nca_id);
}
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
ContentRecordType type) const {
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)
if (res1 != boost::none)
return res1;
return CheckMapForContentRecord(meta, title_id, type);
}
@@ -227,7 +216,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
if (file == nullptr)
continue;
const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
const auto nca = std::make_shared<NCA>(parser(file, id));
if (nca->GetStatus() != Loader::ResultStatus::Success ||
nca->GetType() != NCAContentType::Meta) {
continue;
@@ -285,14 +274,17 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
return id ? GetFileAtID(*id) : nullptr;
if (id == boost::none)
return nullptr;
return GetFileAtID(id.get());
}
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
if (meta_iter != meta.end())
return meta_iter->second.GetTitleVersion();
@@ -301,12 +293,15 @@ std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
if (yuzu_meta_iter != yuzu_meta.end())
return yuzu_meta_iter->second.GetTitleVersion();
return {};
return boost::none;
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
return id ? parser(GetFileAtID(*id), *id) : nullptr;
if (id == boost::none)
return nullptr;
return parser(GetFileAtID(id.get()), id.get());
}
VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
@@ -317,7 +312,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_unique<NCA>(raw, nullptr, 0, keys);
return std::make_unique<NCA>(raw);
}
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
@@ -360,8 +355,8 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
}
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
boost::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
@@ -369,33 +364,33 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type && *title_type != c.GetType())
if (title_type != boost::none && title_type.get() != c.GetType())
return false;
if (record_type && *record_type != r.type)
if (record_type != boost::none && record_type.get() != r.type)
return false;
if (title_id && *title_id != c.GetTitleID())
if (title_id != boost::none && title_id.get() != c.GetTitleID())
return false;
return true;
});
return out;
}
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) {
const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
if (file == nullptr)
return nullptr;
return std::make_shared<NCA>(file);
}
InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy);
}
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto ncas = nsp.GetNCAsCollapsed();
const auto meta_iter = std::find_if(ncas.begin(), ncas.end(), [](const auto& nca) {
const auto& ncas = nsp->GetNCAsCollapsed();
const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
return nca->GetType() == NCAContentType::Meta;
});
@@ -409,7 +404,7 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
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);
const auto res = RawInstallNCA(*meta_iter, copy, overwrite_if_exists, meta_id);
if (res != InstallResult::Success)
return res;
@@ -421,7 +416,7 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
if (res2 != InstallResult::Success)
return res2;
}
@@ -430,21 +425,21 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
return InstallResult::Success;
}
InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
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
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);
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}, {});
@@ -453,10 +448,10 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
}
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
std::optional<NcaID> override_id) {
const auto in = nca.GetBaseFile();
boost::optional<NcaID> override_id) {
const auto in = nca->GetBaseFile();
Core::Crypto::SHA256Hash hash{};
// Calculate NcaID
@@ -464,12 +459,12 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
// 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) {
id = *override_id;
} else {
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);
@@ -539,14 +534,14 @@ bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
for (const auto& c : caches) {
const auto res = c->GetEntryVersion(title_id);
if (res)
if (res != boost::none)
return res;
}
return {};
return boost::none;
}
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
@@ -598,15 +593,12 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
},
[](const CNMT& c, const ContentRecord& r) { return true; });
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
return out;
}
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
boost::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
@@ -615,18 +607,15 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type && *title_type != c.GetType())
if (title_type != boost::none && title_type.get() != c.GetType())
return false;
if (record_type && *record_type != r.type)
if (record_type != boost::none && record_type.get() != r.type)
return false;
if (title_id && *title_id != c.GetTitleID())
if (title_id != boost::none && title_id.get() != c.GetTitleID())
return false;
return true;
});
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
return out;
}
} // namespace FileSys

View File

@@ -6,12 +6,12 @@
#include <array>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -50,10 +50,6 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
// std unique requires operator== to identify duplicates.
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
/*
* A class that catalogues NCAs in the registered directory structure.
* Nintendo's registered format follows this structure:
@@ -64,8 +60,8 @@ bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
* | 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.)
* (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 {
friend class RegisteredCacheUnion;
@@ -84,7 +80,7 @@ public:
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
std::optional<u32> GetEntryVersion(u64 title_id) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -96,23 +92,25 @@ public:
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
// If a parameter is not boost::none, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
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/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
InstallResult InstallEntry(const NSP& nsp, bool overwrite_if_exists = false,
InstallResult InstallEntry(std::shared_ptr<NSP> nsp, 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(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
InstallResult InstallEntry(std::shared_ptr<NCA> nca, TitleType type,
bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
private:
@@ -123,17 +121,16 @@ private:
std::vector<NcaID> AccumulateFiles() const;
void ProcessFiles(const std::vector<NcaID>& ids);
void AccumulateYuzuMeta();
std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
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(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists, std::optional<NcaID> override_id = {});
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;
Core::Crypto::KeyManager keys;
// maps tid -> NcaID of meta
boost::container::flat_map<u64, NcaID> meta_id;
// maps tid -> meta
@@ -152,7 +149,7 @@ public:
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
std::optional<u32> GetEntryVersion(u64 title_id) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -164,10 +161,11 @@ public:
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
// If a parameter is not boost::none, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
boost::optional<TitleType> title_type = boost::none,
boost::optional<ContentRecordType> record_type = boost::none,
boost::optional<u64> title_id = boost::none) const;
private:
std::vector<RegisteredCache*> caches;

View File

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

View File

@@ -13,18 +13,12 @@
namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
// Delete all temporary storages
// On hardware, it is expected that temporary storage be empty at first use.
dir->DeleteSubdirectoryRecursive("temp");
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
SaveDataFactory::~SaveDataFactory() = default;
@@ -89,24 +83,6 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
return MakeResult<VirtualDir>(std::move(out));
}
VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
}
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
switch (space) {
case SaveDataSpaceId::NandSystem:
return "/system/";
case SaveDataSpaceId::NandUser:
return "/user/";
case SaveDataSpaceId::TemporaryStorage:
return "/temp/";
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
}
}
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id) {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
@@ -114,7 +90,21 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
if (type == SaveDataType::SaveData && title_id == 0)
title_id = Core::CurrentProcess()->GetTitleID();
std::string out = GetSaveDataSpaceIdPath(space);
std::string out;
switch (space) {
case SaveDataSpaceId::NandSystem:
out = "/system/";
break;
case SaveDataSpaceId::NandUser:
out = "/user/";
break;
case SaveDataSpaceId::TemporaryStorage:
out = "/temp/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
}
switch (type) {
case SaveDataType::SystemSaveData:
@@ -126,40 +116,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
case SaveDataType::TemporaryStorage:
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
case SaveDataType::CacheStorage:
return fmt::format("{}save/cache/{:016X}", out, title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
return fmt::format("{}save/unknown_{:X}/{:016X}", out, static_cast<u8>(type), title_id);
}
}
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const {
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
const auto size_file = dir->GetFile(SAVE_DATA_SIZE_FILENAME);
if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize))
return {0, 0};
SaveDataSize out;
if (size_file->ReadObject(&out) != sizeof(SaveDataSize))
return {0, 0};
return out;
}
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) {
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
const auto size_file = dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
if (size_file == nullptr)
return;
size_file->Resize(sizeof(SaveDataSize));
size_file->WriteObject(new_value);
}
} // namespace FileSys

View File

@@ -17,10 +17,8 @@ namespace FileSys {
enum class SaveDataSpaceId : u8 {
NandSystem = 0,
NandUser = 1,
SdCardSystem = 2,
SdCard = 2,
TemporaryStorage = 3,
SdCardUser = 4,
ProperSystem = 100,
};
enum class SaveDataType : u8 {
@@ -46,11 +44,6 @@ struct SaveDataDescriptor {
};
static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
struct SaveDataSize {
u64 normal;
u64 journal;
};
/// File system interface to the SaveData archive
class SaveDataFactory {
public:
@@ -59,15 +52,9 @@ public:
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
u128 user_id, u64 save_id);
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
private:
VirtualDir dir;
};

View File

@@ -205,6 +205,10 @@ VirtualDir NSP::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
exefs = pfs;
@@ -252,7 +256,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
continue;
}
auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
auto next_nca = std::make_shared<NCA>(next_file);
if (next_nca->GetType() == NCAContentType::Program)
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||

View File

@@ -55,6 +55,9 @@ public:
VirtualDir GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
void ReadNCAs(const std::vector<VirtualFile>& files);
@@ -70,8 +73,6 @@ private:
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
Core::Crypto::KeyManager keys;
VirtualFile romfs;
VirtualDir exefs;
};

View File

@@ -1,81 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "common/common_types.h"
#include "core/file_sys/system_archive/ng_word.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys::SystemArchive {
namespace NgWord1Data {
constexpr std::size_t NUMBER_WORD_TXT_FILES = 0x10;
// Should this archive replacement mysteriously not work on a future game, consider updating.
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x19}; // 5.1.0 System Version
constexpr std::array<u8, 30> WORD_TXT{
0xFE, 0xFF, 0x00, 0x5E, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x79, 0x00, 0x62, 0x00,
0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0A,
}; // "^verybadword$" in UTF-16
} // namespace NgWord1Data
VirtualDir NgWord1() {
std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES);
for (std::size_t i = 0; i < files.size(); ++i) {
files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
}
files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
NgWord1Data::WORD_TXT, "common.txt"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
NgWord1Data::VERSION_DAT, "version.dat"));
return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
}
namespace NgWord2Data {
constexpr std::size_t NUMBER_AC_NX_FILES = 0x10;
// Should this archive replacement mysteriously not work on a future game, consider updating.
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x15}; // 5.1.0 System Version
constexpr std::array<u8, 0x2C> AC_NX_DATA{
0x1F, 0x8B, 0x08, 0x08, 0xD5, 0x2C, 0x09, 0x5C, 0x04, 0x00, 0x61, 0x63, 0x72, 0x61, 0x77,
0x00, 0xED, 0xC1, 0x01, 0x0D, 0x00, 0x00, 0x00, 0xC2, 0x20, 0xFB, 0xA7, 0xB6, 0xC7, 0x07,
0x0C, 0x00, 0x00, 0x00, 0xC8, 0x3B, 0x11, 0x00, 0x1C, 0xC7, 0x00, 0x10, 0x00, 0x00,
}; // Deserializes to no bad words
} // namespace NgWord2Data
VirtualDir NgWord2() {
std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3);
for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) {
files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i));
files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i));
files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i));
}
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, "ac_common_b1_nx"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, "ac_common_b2_nx"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>(
NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx"));
files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>(
NgWord2Data::VERSION_DAT, "version.dat"));
return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
}
} // namespace FileSys::SystemArchive

View File

@@ -1,14 +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/file_sys/vfs_types.h"
namespace FileSys::SystemArchive {
VirtualDir NgWord1();
VirtualDir NgWord2();
} // namespace FileSys::SystemArchive

View File

@@ -1,90 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/ng_word.h"
#include "core/file_sys/system_archive/system_archive.h"
namespace FileSys::SystemArchive {
constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800;
constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28;
using SystemArchiveSupplier = VirtualDir (*)();
struct SystemArchiveDescriptor {
u64 title_id;
const char* name;
SystemArchiveSupplier supplier;
};
constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES{{
{0x0100000000000800, "CertStore", nullptr},
{0x0100000000000801, "ErrorMessage", nullptr},
{0x0100000000000802, "MiiModel", nullptr},
{0x0100000000000803, "BrowserDll", nullptr},
{0x0100000000000804, "Help", nullptr},
{0x0100000000000805, "SharedFont", nullptr},
{0x0100000000000806, "NgWord", &NgWord1},
{0x0100000000000807, "SsidList", nullptr},
{0x0100000000000808, "Dictionary", nullptr},
{0x0100000000000809, "SystemVersion", nullptr},
{0x010000000000080A, "AvatarImage", nullptr},
{0x010000000000080B, "LocalNews", nullptr},
{0x010000000000080C, "Eula", nullptr},
{0x010000000000080D, "UrlBlackList", nullptr},
{0x010000000000080E, "TimeZoneBinary", nullptr},
{0x010000000000080F, "CertStoreCruiser", nullptr},
{0x0100000000000810, "FontNintendoExtension", nullptr},
{0x0100000000000811, "FontStandard", nullptr},
{0x0100000000000812, "FontKorean", nullptr},
{0x0100000000000813, "FontChineseTraditional", nullptr},
{0x0100000000000814, "FontChineseSimple", nullptr},
{0x0100000000000815, "FontBfcpx", nullptr},
{0x0100000000000816, "SystemUpdate", nullptr},
{0x0100000000000817, "0100000000000817", nullptr},
{0x0100000000000818, "FirmwareDebugSettings", nullptr},
{0x0100000000000819, "BootImagePackage", nullptr},
{0x010000000000081A, "BootImagePackageSafe", nullptr},
{0x010000000000081B, "BootImagePackageExFat", nullptr},
{0x010000000000081C, "BootImagePackageExFatSafe", nullptr},
{0x010000000000081D, "FatalMessage", nullptr},
{0x010000000000081E, "ControllerIcon", nullptr},
{0x010000000000081F, "PlatformConfigIcosa", nullptr},
{0x0100000000000820, "PlatformConfigCopper", nullptr},
{0x0100000000000821, "PlatformConfigHoag", nullptr},
{0x0100000000000822, "ControllerFirmware", nullptr},
{0x0100000000000823, "NgWord2", &NgWord2},
{0x0100000000000824, "PlatformConfigIcosaMariko", nullptr},
{0x0100000000000825, "ApplicationBlackList", nullptr},
{0x0100000000000826, "RebootlessSystemUpdateVersion", nullptr},
{0x0100000000000827, "ContentActionTable", nullptr},
}};
VirtualFile SynthesizeSystemArchive(const u64 title_id) {
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id)
return nullptr;
const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
if (desc.supplier == nullptr)
return nullptr;
const auto dir = desc.supplier();
if (dir == nullptr)
return nullptr;
const auto romfs = CreateRomFS(dir);
if (romfs == nullptr)
return nullptr;
LOG_INFO(Service_FS, " - System archive generation successful!");
return romfs;
}
} // namespace FileSys::SystemArchive

View File

@@ -1,14 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
namespace FileSys::SystemArchive {
VirtualFile SynthesizeSystemArchive(u64 title_id);
} // namespace FileSys::SystemArchive

View File

@@ -167,13 +167,13 @@ std::string VfsFile::GetExtension() const {
VfsDirectory::~VfsDirectory() = default;
std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
u8 out{};
std::size_t size = Read(&out, 1, offset);
if (size == 1)
return out;
return {};
return boost::none;
}
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
@@ -384,28 +384,6 @@ bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
return success;
}
bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
auto dir = GetSubdirectory(name);
if (dir == nullptr) {
return false;
}
bool success = true;
for (const auto& file : dir->GetFiles()) {
if (!dir->DeleteFile(file->GetName())) {
success = false;
}
}
for (const auto& sdir : dir->GetSubdirectories()) {
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
success = false;
}
}
return success;
}
bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
const auto f1 = GetFile(src);
auto f2 = CreateFile(dest);
@@ -453,34 +431,10 @@ std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name)
return nullptr;
}
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) {
return nullptr;
}
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) {
return nullptr;
}
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
return nullptr;
}
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) {
return nullptr;
}
bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
return false;
}
bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
return false;
}
bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
return false;
}
bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
return false;
}
@@ -518,14 +472,10 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
std::vector<u8> temp(std::min(block_size, src->GetSize()));
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
const auto read = std::min(block_size, src->GetSize() - i);
const auto block = src->Read(temp.data(), read, i);
if (src->Read(temp.data(), read, i) != read) {
if (dest->Write(temp.data(), read, i) != read)
return false;
}
if (dest->Write(temp.data(), read, i) != read) {
return false;
}
}
return true;

View File

@@ -4,15 +4,13 @@
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
@@ -105,8 +103,8 @@ public:
// into file. Returns number of bytes successfully written.
virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
// Reads exactly one byte at the offset provided, returning std::nullopt on error.
virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
// Reads exactly one byte at the offset provided, returning boost::none on error.
virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
// Reads size bytes starting at offset in file into a vector.
virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
@@ -150,7 +148,7 @@ public:
template <typename T>
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset);
return Write(data, number_elements * sizeof(T), offset);
}
// Writes size bytes starting at memory location data to offset in file.
@@ -166,7 +164,7 @@ public:
template <typename T>
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
return Write(&data, sizeof(T), offset);
}
// Renames the file to name. Returns whether or not the operation was successsful.
@@ -245,18 +243,12 @@ public:
// any failure.
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path);
// Deletes the subdirectory with the given name and returns true on success.
// Deletes the subdirectory with name and returns true on success.
virtual bool DeleteSubdirectory(std::string_view name) = 0;
// Deletes all subdirectories and files within the provided directory and then deletes
// the directory itself. Returns true on success.
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
// the subdirectory. Returns true on success.
virtual bool DeleteSubdirectoryRecursive(std::string_view name);
// Deletes all subdirectories and files within the provided directory.
// Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory.
virtual bool CleanSubdirectoryRecursive(std::string_view name);
// Returns whether or not the file with name name was deleted successfully.
// Returnes whether or not the file with name name was deleted successfully.
virtual bool DeleteFile(std::string_view name) = 0;
// Returns whether or not this directory was renamed to name.
@@ -270,8 +262,36 @@ public:
// item name -> type.
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
// subdirectory in one call.
template <typename Directory>
bool InterpretAsDirectory(std::string_view file) {
auto file_p = GetFile(file);
if (file_p == nullptr) {
return false;
}
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
}
bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
const std::string& file) {
auto file_p = GetFile(file);
if (file_p == nullptr)
return false;
return ReplaceFileWithSubdirectory(file_p, function(file_p));
}
// Returns the full path of this directory as a string, recursively
virtual std::string GetFullPath() const;
protected:
// Backend for InterpretAsDirectory.
// Removes all references to file and adds a reference to dir in the directory's implementation.
virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
};
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
@@ -282,13 +302,7 @@ public:
bool IsReadable() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path) override;
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path) override;
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
bool DeleteSubdirectory(std::string_view name) override;
bool DeleteSubdirectoryRecursive(std::string_view name) override;
bool CleanSubdirectoryRecursive(std::string_view name) override;
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
};

View File

@@ -126,4 +126,7 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) {
return true;
}
bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -39,6 +39,9 @@ public:
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualDir> dirs;
std::string name;

View File

@@ -57,11 +57,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
}
std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
if (r_offset < size)
return file->ReadByte(offset + r_offset);
return {};
return boost::none;
}
std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {

View File

@@ -29,7 +29,7 @@ public:
bool IsReadable() const override;
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
std::optional<u8> ReadByte(std::size_t offset) const override;
boost::optional<u8> ReadByte(std::size_t offset) const override;
std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
std::vector<u8> ReadAllBytes() const override;
bool WriteByte(u8 data, std::size_t offset) override;

View File

@@ -430,4 +430,7 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
return out;
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -100,6 +100,9 @@ public:
std::string GetFullPath() const override;
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);

View File

@@ -53,10 +53,10 @@ public:
return 0;
}
std::optional<u8> ReadByte(std::size_t offset) const override {
boost::optional<u8> ReadByte(std::size_t offset) const override {
if (offset < size)
return value;
return {};
return boost::none;
}
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <utility>
#include "core/file_sys/vfs_vector.h"
@@ -131,4 +132,11 @@ void VectorVfsDirectory::AddFile(VirtualFile file) {
void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
dirs.push_back(std::move(dir));
}
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
if (!DeleteFile(file->GetName()))
return false;
dirs.emplace_back(std::move(dir));
return true;
}
} // namespace FileSys

View File

@@ -4,63 +4,10 @@
#pragma once
#include <cstring>
#include "core/file_sys/vfs.h"
namespace FileSys {
// An implementation of VfsFile that is backed by a statically-sized array
template <std::size_t size>
class ArrayVfsFile : public VfsFile {
public:
ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr)
: data(data), name(std::move(name)), parent(std::move(parent)) {}
std::string GetName() const override {
return name;
}
std::size_t GetSize() const override {
return size;
}
bool Resize(std::size_t new_size) override {
return false;
}
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override {
return parent;
}
bool IsWritable() const override {
return false;
}
bool IsReadable() const override {
return true;
}
std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override {
const auto read = std::min(length, size - offset);
std::memcpy(data_, data.data() + offset, read);
return read;
}
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
return 0;
}
bool Rename(std::string_view name) override {
this->name = name;
return true;
}
private:
std::array<u8, size> data;
std::string name;
VirtualDir parent;
};
// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
class VectorVfsFile : public VfsFile {
public:
@@ -110,6 +57,9 @@ public:
virtual void AddFile(VirtualFile file);
virtual void AddDirectory(VirtualDir dir);
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualFile> files;
std::vector<VirtualDir> dirs;

View File

@@ -163,4 +163,7 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -51,6 +51,9 @@ public:
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus Parse(std::string_view path);

View File

@@ -1,19 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/applets/profile_select.h"
#include "core/settings.h"
namespace Core::Frontend {
ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const {
Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
}
} // namespace Core::Frontend

View File

@@ -1,27 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <optional>
#include "core/hle/service/acc/profile_manager.h"
namespace Core::Frontend {
class ProfileSelectApplet {
public:
virtual ~ProfileSelectApplet();
virtual void SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0;
};
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
public:
void SelectProfile(
std::function<void(std::optional<Service::Account::UUID>)> callback) const override;
};
} // namespace Core::Frontend

View File

@@ -1,29 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/backend.h"
#include "common/string_util.h"
#include "core/frontend/applets/software_keyboard.h"
namespace Core::Frontend {
SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
void DefaultSoftwareKeyboardApplet::RequestText(
std::function<void(std::optional<std::u16string>)> out,
SoftwareKeyboardParameters parameters) const {
if (parameters.initial_text.empty())
out(u"yuzu");
out(parameters.initial_text);
}
void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
std::u16string error_message, std::function<void()> finished_check) const {
LOG_WARNING(Service_AM,
"(STUBBED) called - Default fallback software keyboard does not support text "
"check! (error_message={})",
Common::UTF16ToUTF8(error_message));
finished_check();
}
} // namespace Core::Frontend

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