Merge pull request #4908 from hamish-milne/feature/savestates-2
Save states
This commit is contained in:
commit
c605bb42db
@ -1,5 +1,9 @@
|
||||
# CMake 3.8 required for 17 to be a valid value for CXX_STANDARD
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.15)
|
||||
# Don't override the warning flags in MSVC:
|
||||
cmake_policy(SET CMP0092 NEW)
|
||||
endif ()
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
include(DownloadExternals)
|
||||
@ -35,6 +39,8 @@ CMAKE_DEPENDENT_OPTION(ENABLE_MF "Use Media Foundation decoder (preferred over F
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ON "MINGW" OFF)
|
||||
|
||||
option(USE_SYSTEM_BOOST "Use the system Boost libs (instead of the bundled ones)" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(ENABLE_FDK "Use FDK AAC decoder" OFF "NOT ENABLE_FFMPEG_AUDIO_DECODER;NOT ENABLE_MF" OFF)
|
||||
|
||||
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
@ -126,16 +132,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
find_package(Boost 1.66.0 QUIET)
|
||||
if (NOT Boost_FOUND)
|
||||
message(STATUS "Boost 1.66.0 or newer not found, falling back to externals")
|
||||
|
||||
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_NO_SYSTEM_PATHS OFF)
|
||||
find_package(Boost QUIET REQUIRED)
|
||||
endif()
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
@ -339,8 +335,21 @@ git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
if (NOT USE_SYSTEM_BOOST)
|
||||
add_definitions( -DBOOST_ALL_NO_LIB )
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(externals)
|
||||
|
||||
# Boost
|
||||
if (USE_SYSTEM_BOOST)
|
||||
find_package(Boost 1.70.0 QUIET REQUIRED)
|
||||
else()
|
||||
add_library(Boost::boost ALIAS boost)
|
||||
add_library(Boost::serialization ALIAS boost_serialization)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(dist/installer)
|
||||
|
||||
|
19
externals/CMakeLists.txt
vendored
19
externals/CMakeLists.txt
vendored
@ -1,9 +1,28 @@
|
||||
# Definitions for all external bundled libraries
|
||||
|
||||
# Suppress warnings from external libraries
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
add_compile_options(/W0)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
|
||||
include(DownloadExternals)
|
||||
include(ExternalProject)
|
||||
|
||||
# Boost
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_NO_SYSTEM_PATHS ON)
|
||||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
|
||||
# Boost::serialization
|
||||
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
|
||||
add_library(boost_serialization STATIC ${boost_serialization_SRC})
|
||||
target_link_libraries(boost_serialization PUBLIC boost)
|
||||
|
||||
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
|
||||
|
||||
# Catch
|
||||
add_library(catch-single-include INTERFACE)
|
||||
target_include_directories(catch-single-include INTERFACE catch/single_include)
|
||||
|
2
externals/boost
vendored
2
externals/boost
vendored
@ -1 +1 @@
|
||||
Subproject commit 502437b2ae3f1da821aa7d5d5174ec356fa89269
|
||||
Subproject commit 36603a1e665e849d29b1735a12c0a51284a10dd0
|
@ -31,6 +31,7 @@ if (MSVC)
|
||||
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
|
||||
# /Zc:inline - Let codegen omit inline functions in object files
|
||||
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
|
||||
# /external:* - Suppress warnings from external headers
|
||||
add_compile_options(
|
||||
/W3
|
||||
/MP
|
||||
@ -42,6 +43,10 @@ if (MSVC)
|
||||
/Zc:externConstexpr
|
||||
/Zc:inline
|
||||
/Zc:throwingNew
|
||||
/experimental:external
|
||||
/external:I "${CMAKE_SOURCE_DIR}/externals"
|
||||
/external:anglebrackets
|
||||
/external:W0
|
||||
)
|
||||
|
||||
# /GS- - No stack buffer overflow checks
|
||||
|
@ -35,7 +35,7 @@ add_library(audio_core STATIC
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
target_link_libraries(audio_core PUBLIC common)
|
||||
target_link_libraries(audio_core PRIVATE SoundTouch teakra)
|
||||
|
||||
if(ENABLE_MF)
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/serialization/access.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/common_types.h"
|
||||
@ -113,6 +114,10 @@ private:
|
||||
Common::RingBuffer<s16, 0x2000, 2> fifo;
|
||||
std::array<s16, 2> last_frame{};
|
||||
TimeStretcher time_stretcher;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
@ -2,6 +2,11 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/weak_ptr.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#ifdef HAVE_MF
|
||||
#include "audio_core/hle/wmf_decoder.h"
|
||||
@ -26,11 +31,22 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(AudioCore::DspHle)
|
||||
|
||||
using InterruptType = Service::DSP::DSP_DSP::InterruptType;
|
||||
using Service::DSP::DSP_DSP;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
DspHle::DspHle() : DspHle(Core::System::GetInstance().Memory()) {}
|
||||
|
||||
template <class Archive>
|
||||
void DspHle::serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<DspInterface>(*this);
|
||||
ar&* impl.get();
|
||||
}
|
||||
SERIALIZE_IMPL(DspHle)
|
||||
|
||||
static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles
|
||||
|
||||
struct DspHle::Impl final {
|
||||
@ -64,7 +80,7 @@ private:
|
||||
void AudioTickCallback(s64 cycles_late);
|
||||
|
||||
DspState dsp_state = DspState::Off;
|
||||
std::array<std::vector<u8>, num_dsp_pipe> pipe_data;
|
||||
std::array<std::vector<u8>, num_dsp_pipe> pipe_data{};
|
||||
|
||||
HLE::DspMemory dsp_memory;
|
||||
std::array<HLE::Source, HLE::num_sources> sources{{
|
||||
@ -74,14 +90,25 @@ private:
|
||||
HLE::Source(15), HLE::Source(16), HLE::Source(17), HLE::Source(18), HLE::Source(19),
|
||||
HLE::Source(20), HLE::Source(21), HLE::Source(22), HLE::Source(23),
|
||||
}};
|
||||
HLE::Mixers mixers;
|
||||
HLE::Mixers mixers{};
|
||||
|
||||
DspHle& parent;
|
||||
Core::TimingEventType* tick_event;
|
||||
Core::TimingEventType* tick_event{};
|
||||
|
||||
std::unique_ptr<HLE::DecoderBase> decoder;
|
||||
std::unique_ptr<HLE::DecoderBase> decoder{};
|
||||
|
||||
std::weak_ptr<DSP_DSP> dsp_dsp;
|
||||
std::weak_ptr<DSP_DSP> dsp_dsp{};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& dsp_state;
|
||||
ar& pipe_data;
|
||||
ar& dsp_memory.raw_memory;
|
||||
ar& sources;
|
||||
ar& mixers;
|
||||
ar& dsp_dsp;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
DspHle::Impl::Impl(DspHle& parent_, Memory::MemorySystem& memory) : parent(parent_) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/dsp_interface.h"
|
||||
#include "common/common_types.h"
|
||||
@ -42,6 +43,14 @@ private:
|
||||
struct Impl;
|
||||
friend struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
DspHle();
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int);
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(AudioCore::DspHle)
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/hle/shared_memory.h"
|
||||
|
||||
@ -54,6 +55,17 @@ private:
|
||||
void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
|
||||
/// INTERNAL: Generate DspStatus based on internal state.
|
||||
DspStatus GetCurrentStatus() const;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& current_frame;
|
||||
ar& state.intermediate_mixer_volume;
|
||||
ar& state.mixer1_enabled;
|
||||
ar& state.mixer2_enabled;
|
||||
ar& state.intermediate_mix_buffer;
|
||||
ar& state.output_format;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::HLE
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <boost/serialization/access.hpp>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/hle/common.h"
|
||||
#include "common/bit_field.h"
|
||||
@ -56,6 +57,12 @@ private:
|
||||
return (value << 16) | (value >> 16);
|
||||
}
|
||||
u32_le storage;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& storage;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivially copyable");
|
||||
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/deque.hpp>
|
||||
#include <boost/serialization/priority_queue.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <queue>
|
||||
#include "audio_core/audio_types.h"
|
||||
#include "audio_core/codec.h"
|
||||
@ -85,6 +89,24 @@ private:
|
||||
bool from_queue;
|
||||
u32_dsp play_position; // = 0;
|
||||
bool has_played; // = false;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& physical_address;
|
||||
ar& length;
|
||||
ar& adpcm_ps;
|
||||
ar& adpcm_yn;
|
||||
ar& adpcm_dirty;
|
||||
ar& is_looping;
|
||||
ar& buffer_id;
|
||||
ar& mono_or_stereo;
|
||||
ar& format;
|
||||
ar& from_queue;
|
||||
ar& play_position;
|
||||
ar& has_played;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
struct BufferOrder {
|
||||
@ -107,7 +129,7 @@ private:
|
||||
|
||||
// Buffer queue
|
||||
|
||||
std::priority_queue<Buffer, std::vector<Buffer>, BufferOrder> input_queue;
|
||||
std::priority_queue<Buffer, std::vector<Buffer>, BufferOrder> input_queue = {};
|
||||
MonoOrStereo mono_or_stereo = MonoOrStereo::Mono;
|
||||
Format format = Format::ADPCM;
|
||||
|
||||
@ -115,7 +137,7 @@ private:
|
||||
|
||||
u32 current_sample_number = 0;
|
||||
u32 next_sample_number = 0;
|
||||
AudioInterp::StereoBuffer16 current_buffer;
|
||||
AudioInterp::StereoBuffer16 current_buffer = {};
|
||||
|
||||
// buffer_id state
|
||||
|
||||
@ -135,7 +157,27 @@ private:
|
||||
|
||||
// Filter state
|
||||
|
||||
SourceFilters filters;
|
||||
SourceFilters filters = {};
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& enabled;
|
||||
ar& sync;
|
||||
ar& gain;
|
||||
ar& input_queue;
|
||||
ar& mono_or_stereo;
|
||||
ar& format;
|
||||
ar& current_sample_number;
|
||||
ar& next_sample_number;
|
||||
ar& current_buffer;
|
||||
ar& buffer_update;
|
||||
ar& current_buffer_id;
|
||||
ar& adpcm_coeffs;
|
||||
ar& rate_multiplier;
|
||||
ar& interpolation_mode;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
|
||||
} state;
|
||||
|
||||
@ -150,6 +192,12 @@ private:
|
||||
bool DequeueBuffer();
|
||||
/// INTERNAL: Generates a SourceStatus::Status based on our internal state.
|
||||
SourceStatus::Status GetCurrentStatus();
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& state;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::HLE
|
||||
|
@ -132,7 +132,9 @@ void OpenGLWindow::Present() {
|
||||
return;
|
||||
|
||||
context->makeCurrent(this);
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
if (VideoCore::g_renderer) {
|
||||
VideoCore::g_renderer->TryPresent(100);
|
||||
}
|
||||
context->swapBuffers(this);
|
||||
auto f = context->versionFunctions<QOpenGLFunctions_3_3_Core>();
|
||||
f->glFinish();
|
||||
|
@ -57,7 +57,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
||||
// This must be in alphabetical order according to action name as it must have the same order as
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 21> default_hotkeys{
|
||||
const std::array<UISettings::Shortcut, 23> default_hotkeys{
|
||||
{{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral("\\"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
@ -73,6 +73,8 @@ const std::array<UISettings::Shortcut, 21> default_hotkeys{
|
||||
{QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Save to Oldest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
|
||||
|
@ -216,7 +216,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
|
||||
|
||||
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
||||
const auto* process = thread.owner_process;
|
||||
const auto& process = thread.owner_process;
|
||||
|
||||
QString processor;
|
||||
switch (thread.processor_id) {
|
||||
@ -237,6 +237,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
break;
|
||||
}
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("object id = %1").arg(thread.GetObjectId())));
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
|
||||
list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId())));
|
||||
list.push_back(
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <clocale>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <QDesktopWidget>
|
||||
@ -78,6 +79,7 @@
|
||||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/savestate.h"
|
||||
#include "core/settings.h"
|
||||
#include "game_list_p.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@ -171,6 +173,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
|
||||
InitializeWidgets();
|
||||
InitializeDebugWidgets();
|
||||
InitializeRecentFileMenuActions();
|
||||
InitializeSaveStateMenuActions();
|
||||
InitializeHotkeys();
|
||||
ShowUpdaterWidgets();
|
||||
|
||||
@ -396,6 +399,32 @@ void GMainWindow::InitializeRecentFileMenuActions() {
|
||||
UpdateRecentFiles();
|
||||
}
|
||||
|
||||
void GMainWindow::InitializeSaveStateMenuActions() {
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
actions_load_state[i] = new QAction(this);
|
||||
actions_load_state[i]->setData(i + 1);
|
||||
connect(actions_load_state[i], &QAction::triggered, this, &GMainWindow::OnLoadState);
|
||||
ui.menu_Load_State->addAction(actions_load_state[i]);
|
||||
|
||||
actions_save_state[i] = new QAction(this);
|
||||
actions_save_state[i]->setData(i + 1);
|
||||
connect(actions_save_state[i], &QAction::triggered, this, &GMainWindow::OnSaveState);
|
||||
ui.menu_Save_State->addAction(actions_save_state[i]);
|
||||
}
|
||||
|
||||
connect(ui.action_Load_from_Newest_Slot, &QAction::triggered,
|
||||
[this] { actions_load_state[newest_slot - 1]->trigger(); });
|
||||
connect(ui.action_Save_to_Oldest_Slot, &QAction::triggered,
|
||||
[this] { actions_save_state[oldest_slot - 1]->trigger(); });
|
||||
|
||||
connect(ui.menu_Load_State->menuAction(), &QAction::hovered, this,
|
||||
&GMainWindow::UpdateSaveStates);
|
||||
connect(ui.menu_Save_State->menuAction(), &QAction::hovered, this,
|
||||
&GMainWindow::UpdateSaveStates);
|
||||
|
||||
UpdateSaveStates();
|
||||
}
|
||||
|
||||
void GMainWindow::InitializeHotkeys() {
|
||||
hotkey_registry.LoadHotkeys();
|
||||
|
||||
@ -509,6 +538,10 @@ void GMainWindow::InitializeHotkeys() {
|
||||
OnCaptureScreenshot();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey(main_window, ui.action_Load_from_Newest_Slot->text(), this),
|
||||
&QShortcut::activated, ui.action_Load_from_Newest_Slot, &QAction::trigger);
|
||||
connect(hotkey_registry.GetHotkey(main_window, ui.action_Save_to_Oldest_Slot->text(), this),
|
||||
&QShortcut::activated, ui.action_Save_to_Oldest_Slot, &QAction::trigger);
|
||||
}
|
||||
|
||||
void GMainWindow::ShowUpdaterWidgets() {
|
||||
@ -687,6 +720,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
if (emulation_running) {
|
||||
ui.action_Enable_Frame_Advancing->setChecked(true);
|
||||
ui.action_Advance_Frame->setEnabled(true);
|
||||
Core::System::GetInstance().frame_limiter.SetFrameAdvancing(true);
|
||||
Core::System::GetInstance().frame_limiter.AdvanceFrame();
|
||||
}
|
||||
});
|
||||
@ -1091,6 +1125,8 @@ void GMainWindow::ShutdownGame() {
|
||||
game_fps_label->setVisible(false);
|
||||
emu_frametime_label->setVisible(false);
|
||||
|
||||
UpdateSaveStates();
|
||||
|
||||
emulation_running = false;
|
||||
|
||||
if (defer_update_prompt) {
|
||||
@ -1137,6 +1173,62 @@ void GMainWindow::UpdateRecentFiles() {
|
||||
ui.menu_recent_files->setEnabled(num_recent_files != 0);
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateSaveStates() {
|
||||
if (!Core::System::GetInstance().IsPoweredOn()) {
|
||||
ui.menu_Load_State->setEnabled(false);
|
||||
ui.menu_Save_State->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ui.menu_Load_State->setEnabled(true);
|
||||
ui.menu_Save_State->setEnabled(true);
|
||||
ui.action_Load_from_Newest_Slot->setEnabled(false);
|
||||
|
||||
oldest_slot = newest_slot = 0;
|
||||
oldest_slot_time = std::numeric_limits<u64>::max();
|
||||
newest_slot_time = 0;
|
||||
|
||||
u64 title_id;
|
||||
if (Core::System::GetInstance().GetAppLoader().ReadProgramId(title_id) !=
|
||||
Loader::ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
auto savestates = Core::ListSaveStates(title_id);
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
actions_load_state[i]->setEnabled(false);
|
||||
actions_load_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||
actions_save_state[i]->setText(tr("Slot %1").arg(i + 1));
|
||||
}
|
||||
for (const auto& savestate : savestates) {
|
||||
const auto text = tr("Slot %1 - %2")
|
||||
.arg(savestate.slot)
|
||||
.arg(QDateTime::fromSecsSinceEpoch(savestate.time)
|
||||
.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")));
|
||||
actions_load_state[savestate.slot - 1]->setEnabled(true);
|
||||
actions_load_state[savestate.slot - 1]->setText(text);
|
||||
actions_save_state[savestate.slot - 1]->setText(text);
|
||||
|
||||
ui.action_Load_from_Newest_Slot->setEnabled(true);
|
||||
|
||||
if (savestate.time > newest_slot_time) {
|
||||
newest_slot = savestate.slot;
|
||||
newest_slot_time = savestate.time;
|
||||
}
|
||||
if (savestate.time < oldest_slot_time) {
|
||||
oldest_slot = savestate.slot;
|
||||
oldest_slot_time = savestate.time;
|
||||
}
|
||||
}
|
||||
for (u32 i = 0; i < Core::SaveStateSlotCount; ++i) {
|
||||
if (!actions_load_state[i]->isEnabled()) {
|
||||
// Prefer empty slot
|
||||
oldest_slot = i + 1;
|
||||
oldest_slot_time = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListLoadFile(QString game_path) {
|
||||
BootGame(game_path);
|
||||
}
|
||||
@ -1385,7 +1477,7 @@ void GMainWindow::OnCIAInstallFinished() {
|
||||
|
||||
void GMainWindow::OnMenuRecentFile() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
assert(action);
|
||||
ASSERT(action);
|
||||
|
||||
const QString filename = action->data().toString();
|
||||
if (QFileInfo::exists(filename)) {
|
||||
@ -1429,6 +1521,8 @@ void GMainWindow::OnStartGame() {
|
||||
ui.action_Capture_Screenshot->setEnabled(true);
|
||||
|
||||
discord_rpc->Update();
|
||||
|
||||
UpdateSaveStates();
|
||||
}
|
||||
|
||||
void GMainWindow::OnPauseGame() {
|
||||
@ -1576,6 +1670,23 @@ void GMainWindow::OnCheats() {
|
||||
cheat_dialog.exec();
|
||||
}
|
||||
|
||||
void GMainWindow::OnSaveState() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
assert(action);
|
||||
|
||||
Core::System::GetInstance().SendSignal(Core::System::Signal::Save, action->data().toUInt());
|
||||
Core::System::GetInstance().frame_limiter.AdvanceFrame();
|
||||
newest_slot = action->data().toUInt();
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadState() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
assert(action);
|
||||
|
||||
Core::System::GetInstance().SendSignal(Core::System::Signal::Load, action->data().toUInt());
|
||||
Core::System::GetInstance().frame_limiter.AdvanceFrame();
|
||||
}
|
||||
|
||||
void GMainWindow::OnConfigure() {
|
||||
ConfigureDialog configureDialog(this, hotkey_registry,
|
||||
!multiplayer_state->IsHostingPublicRoom());
|
||||
@ -1968,6 +2079,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
|
||||
|
||||
title = tr("System Archive Not Found");
|
||||
status_message = tr("System Archive Missing");
|
||||
} else if (result == Core::System::ResultStatus::ErrorSavestate) {
|
||||
title = tr("Save/load Error");
|
||||
message = QString::fromStdString(details);
|
||||
} else {
|
||||
title = tr("Fatal Error");
|
||||
message =
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
@ -14,6 +15,7 @@
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/savestate.h"
|
||||
#include "ui_main.h"
|
||||
|
||||
class AboutDialog;
|
||||
@ -106,6 +108,7 @@ private:
|
||||
void InitializeWidgets();
|
||||
void InitializeDebugWidgets();
|
||||
void InitializeRecentFileMenuActions();
|
||||
void InitializeSaveStateMenuActions();
|
||||
|
||||
void SetDefaultUIGeometry();
|
||||
void SyncMenuUISettings();
|
||||
@ -149,6 +152,8 @@ private:
|
||||
*/
|
||||
void UpdateRecentFiles();
|
||||
|
||||
void UpdateSaveStates();
|
||||
|
||||
/**
|
||||
* If the emulation is running,
|
||||
* asks the user if he really want to close the emulator
|
||||
@ -163,6 +168,8 @@ private slots:
|
||||
void OnStartGame();
|
||||
void OnPauseGame();
|
||||
void OnStopGame();
|
||||
void OnSaveState();
|
||||
void OnLoadState();
|
||||
void OnMenuReportCompatibility();
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
@ -282,6 +289,13 @@ private:
|
||||
bool defer_update_prompt = false;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
std::array<QAction*, Core::SaveStateSlotCount> actions_load_state;
|
||||
std::array<QAction*, Core::SaveStateSlotCount> actions_save_state;
|
||||
|
||||
u32 oldest_slot;
|
||||
u64 oldest_slot_time;
|
||||
u32 newest_slot;
|
||||
u64 newest_slot_time;
|
||||
|
||||
QTranslator translator;
|
||||
|
||||
|
@ -79,11 +79,28 @@
|
||||
<property name="title">
|
||||
<string>&Emulation</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_Save_State">
|
||||
<property name="title">
|
||||
<string>Save State</string>
|
||||
</property>
|
||||
<addaction name="action_Save_to_Oldest_Slot"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Load_State">
|
||||
<property name="title">
|
||||
<string>Load State</string>
|
||||
</property>
|
||||
<addaction name="action_Load_from_Newest_Slot"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<addaction name="action_Start"/>
|
||||
<addaction name="action_Pause"/>
|
||||
<addaction name="action_Stop"/>
|
||||
<addaction name="action_Restart"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menu_Load_State"/>
|
||||
<addaction name="menu_Save_State"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Report_Compatibility"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Configure"/>
|
||||
@ -217,6 +234,22 @@
|
||||
<string>&Stop</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Save">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_FAQ">
|
||||
<property name="text">
|
||||
<string>FAQ</string>
|
||||
@ -235,6 +268,16 @@
|
||||
<string>Single Window Mode</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Save_to_Oldest_Slot">
|
||||
<property name="text">
|
||||
<string>Save to Oldest Slot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_from_Newest_Slot">
|
||||
<property name="text">
|
||||
<string>Load from Newest Slot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Configure">
|
||||
<property name="text">
|
||||
<string>Configure...</string>
|
||||
|
@ -54,6 +54,7 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
add_library(common STATIC
|
||||
alignment.h
|
||||
announce_multiplayer_room.h
|
||||
archives.h
|
||||
assert.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
@ -66,6 +67,7 @@ add_library(common STATIC
|
||||
common_funcs.h
|
||||
common_paths.h
|
||||
common_types.h
|
||||
construct.h
|
||||
file_util.cpp
|
||||
file_util.h
|
||||
hash.h
|
||||
@ -78,6 +80,8 @@ add_library(common STATIC
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
math_util.h
|
||||
memory_ref.h
|
||||
memory_ref.cpp
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
@ -89,6 +93,11 @@ add_library(common STATIC
|
||||
scm_rev.cpp
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
serialization/atomic.h
|
||||
serialization/boost_discrete_interval.hpp
|
||||
serialization/boost_flat_set.h
|
||||
serialization/boost_small_vector.hpp
|
||||
serialization/boost_vector.hpp
|
||||
string_util.cpp
|
||||
string_util.h
|
||||
swap.h
|
||||
@ -121,7 +130,7 @@ endif()
|
||||
|
||||
create_target_directory_groups(common)
|
||||
|
||||
target_link_libraries(common PUBLIC fmt microprofile)
|
||||
target_link_libraries(common PUBLIC fmt microprofile Boost::boost Boost::serialization)
|
||||
target_link_libraries(common PRIVATE libzstd_static)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_link_libraries(common PRIVATE xbyak)
|
||||
|
21
src/common/archives.h
Normal file
21
src/common/archives.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/archive/binary_oarchive.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
|
||||
using iarchive = boost::archive::binary_iarchive;
|
||||
using oarchive = boost::archive::binary_oarchive;
|
||||
|
||||
#define SERIALIZE_IMPL(A) \
|
||||
template void A::serialize<iarchive>(iarchive & ar, const unsigned int file_version); \
|
||||
template void A::serialize<oarchive>(oarchive & ar, const unsigned int file_version);
|
||||
|
||||
#define SERIALIZE_EXPORT_IMPL(A) \
|
||||
BOOST_CLASS_EXPORT_IMPLEMENT(A) \
|
||||
BOOST_SERIALIZATION_REGISTER_ARCHIVE(iarchive) \
|
||||
BOOST_SERIALIZATION_REGISTER_ARCHIVE(oarchive)
|
@ -47,6 +47,7 @@
|
||||
#define DUMP_DIR "dump"
|
||||
#define LOAD_DIR "load"
|
||||
#define SHADER_DIR "shaders"
|
||||
#define STATES_DIR "states"
|
||||
|
||||
// Filenames
|
||||
// Files in the directory returned by GetUserPath(UserPath::LogDir)
|
||||
|
34
src/common/construct.h
Normal file
34
src/common/construct.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/serialization/serialization.hpp>
|
||||
|
||||
/// Allows classes to define `save_construct` and `load_construct` methods for serialization
|
||||
/// This is used where we don't call the default constructor during deserialization, as a shortcut
|
||||
/// instead of using load_construct_data directly
|
||||
class construct_access {
|
||||
public:
|
||||
template <class Archive, class T>
|
||||
static void save_construct(Archive& ar, const T* t, const unsigned int file_version) {
|
||||
t->save_construct(ar, file_version);
|
||||
}
|
||||
template <class Archive, class T>
|
||||
static void load_construct(Archive& ar, T* t, const unsigned int file_version) {
|
||||
T::load_construct(ar, t, file_version);
|
||||
}
|
||||
};
|
||||
|
||||
#define BOOST_SERIALIZATION_CONSTRUCT(T) \
|
||||
namespace boost::serialization { \
|
||||
template <class Archive> \
|
||||
void save_construct_data(Archive& ar, const T* t, const unsigned int file_version) { \
|
||||
construct_access::save_construct(ar, t, file_version); \
|
||||
} \
|
||||
template <class Archive> \
|
||||
void load_construct_data(Archive& ar, T* t, const unsigned int file_version) { \
|
||||
construct_access::load_construct(ar, t, file_version); \
|
||||
} \
|
||||
}
|
@ -726,6 +726,34 @@ void SetUserPath(const std::string& path) {
|
||||
g_paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
|
||||
g_paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
|
||||
g_paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
|
||||
g_paths.emplace(UserPath::StatesDir, user_path + STATES_DIR DIR_SEP);
|
||||
}
|
||||
|
||||
std::string g_currentRomPath{};
|
||||
|
||||
void SetCurrentRomPath(const std::string& path) {
|
||||
g_currentRomPath = path;
|
||||
}
|
||||
|
||||
bool StringReplace(std::string& haystack, const std::string& a, const std::string& b, bool swap) {
|
||||
const auto& needle = swap ? b : a;
|
||||
const auto& replacement = swap ? a : b;
|
||||
if (needle.empty()) {
|
||||
return false;
|
||||
}
|
||||
auto index = haystack.find(needle, 0);
|
||||
if (index == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
haystack.replace(index, needle.size(), replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SerializePath(const std::string& input, bool is_saving) {
|
||||
auto result = input;
|
||||
StringReplace(result, "%CITRA_ROM_FILE%", g_currentRomPath, is_saving);
|
||||
StringReplace(result, "%CITRA_USER_DIR%", GetUserPath(UserPath::UserDir), is_saving);
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::string& GetUserPath(UserPath path) {
|
||||
@ -882,8 +910,9 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
||||
|
||||
IOFile::IOFile() {}
|
||||
|
||||
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
|
||||
Open(filename, openmode, flags);
|
||||
IOFile::IOFile(const std::string& filename, const char openmode[], int flags)
|
||||
: filename(filename), openmode(openmode), flags(flags) {
|
||||
Open();
|
||||
}
|
||||
|
||||
IOFile::~IOFile() {
|
||||
@ -902,10 +931,14 @@ IOFile& IOFile::operator=(IOFile&& other) noexcept {
|
||||
void IOFile::Swap(IOFile& other) noexcept {
|
||||
std::swap(m_file, other.m_file);
|
||||
std::swap(m_good, other.m_good);
|
||||
std::swap(filename, other.filename);
|
||||
std::swap(openmode, other.openmode);
|
||||
std::swap(flags, other.flags);
|
||||
}
|
||||
|
||||
bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
|
||||
bool IOFile::Open() {
|
||||
Close();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (flags != 0) {
|
||||
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
|
||||
@ -916,7 +949,7 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
|
||||
Common::UTF8ToUTF16W(openmode).c_str()) == 0;
|
||||
}
|
||||
#else
|
||||
m_file = std::fopen(filename.c_str(), openmode);
|
||||
m_file = std::fopen(filename.c_str(), openmode.c_str());
|
||||
m_good = m_file != nullptr;
|
||||
#endif
|
||||
|
||||
|
@ -14,6 +14,9 @@
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/wrapper.hpp>
|
||||
#include "common/common_types.h"
|
||||
#ifdef _MSC_VER
|
||||
#include "common/string_util.h"
|
||||
@ -34,10 +37,39 @@ enum class UserPath {
|
||||
RootDir,
|
||||
SDMCDir,
|
||||
ShaderDir,
|
||||
StatesDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
|
||||
// Replaces install-specific paths with standard placeholders, and back again
|
||||
std::string SerializePath(const std::string& input, bool is_saving);
|
||||
|
||||
// A serializable path string
|
||||
struct Path : public boost::serialization::wrapper_traits<const Path> {
|
||||
std::string& str;
|
||||
|
||||
explicit Path(std::string& _str) : str(_str) {}
|
||||
|
||||
static const Path make(std::string& str) {
|
||||
return Path(str);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void save(Archive& ar, const unsigned int) const {
|
||||
auto s_path = SerializePath(str, true);
|
||||
ar << s_path;
|
||||
}
|
||||
template <class Archive>
|
||||
void load(Archive& ar, const unsigned int) const {
|
||||
ar >> str;
|
||||
str = SerializePath(str, false);
|
||||
}
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER();
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
// FileSystem tree node/
|
||||
struct FSTEntry {
|
||||
bool isDirectory;
|
||||
@ -45,6 +77,17 @@ struct FSTEntry {
|
||||
std::string physicalName; // name on disk
|
||||
std::string virtualName; // name in FST names table
|
||||
std::vector<FSTEntry> children;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& isDirectory;
|
||||
ar& size;
|
||||
ar& Path::make(physicalName);
|
||||
ar& Path::make(virtualName);
|
||||
ar& children;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
// Returns true if file filename exists
|
||||
@ -137,6 +180,8 @@ bool SetCurrentDir(const std::string& directory);
|
||||
|
||||
void SetUserPath(const std::string& path = "");
|
||||
|
||||
void SetCurrentRomPath(const std::string& path);
|
||||
|
||||
// Returns a pointer to a string with a Citra data dir in the user's home
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
const std::string& GetUserPath(UserPath path);
|
||||
@ -221,7 +266,6 @@ public:
|
||||
|
||||
void Swap(IOFile& other) noexcept;
|
||||
|
||||
bool Open(const std::string& filename, const char openmode[], int flags = 0);
|
||||
bool Close();
|
||||
|
||||
template <typename T>
|
||||
@ -305,8 +349,31 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool Open();
|
||||
|
||||
std::FILE* m_file = nullptr;
|
||||
bool m_good = true;
|
||||
|
||||
std::string filename;
|
||||
std::string openmode;
|
||||
u32 flags;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& Path::make(filename);
|
||||
ar& openmode;
|
||||
ar& flags;
|
||||
u64 pos;
|
||||
if (Archive::is_saving::value) {
|
||||
pos = Tell();
|
||||
}
|
||||
ar& pos;
|
||||
if (Archive::is_loading::value) {
|
||||
Open();
|
||||
Seek(pos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileUtil
|
||||
|
8
src/common/memory_ref.cpp
Normal file
8
src/common/memory_ref.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "common/memory_ref.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(BufferMem)
|
136
src/common/memory_ref.h
Normal file
136
src/common/memory_ref.h
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
/// Abstract host-side memory - for example a static buffer, or local vector
|
||||
class BackingMem {
|
||||
public:
|
||||
virtual ~BackingMem() = default;
|
||||
virtual u8* GetPtr() = 0;
|
||||
virtual const u8* GetPtr() const = 0;
|
||||
virtual std::size_t GetSize() const = 0;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// Backing memory implemented by a local buffer
|
||||
class BufferMem : public BackingMem {
|
||||
public:
|
||||
BufferMem() = default;
|
||||
explicit BufferMem(std::size_t size) : data(size) {}
|
||||
|
||||
u8* GetPtr() override {
|
||||
return data.data();
|
||||
}
|
||||
|
||||
const u8* GetPtr() const override {
|
||||
return data.data();
|
||||
}
|
||||
|
||||
std::size_t GetSize() const override {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
std::vector<u8>& Vector() {
|
||||
return data;
|
||||
}
|
||||
|
||||
const std::vector<u8>& Vector() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> data;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<BackingMem>(*this);
|
||||
ar& data;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(BufferMem);
|
||||
|
||||
/// A managed reference to host-side memory. Fast enough to be used everywhere instead of u8*
|
||||
/// Supports serialization.
|
||||
class MemoryRef {
|
||||
public:
|
||||
MemoryRef() = default;
|
||||
MemoryRef(std::nullptr_t) {}
|
||||
MemoryRef(std::shared_ptr<BackingMem> backing_mem_)
|
||||
: backing_mem(std::move(backing_mem_)), offset(0) {
|
||||
Init();
|
||||
}
|
||||
MemoryRef(std::shared_ptr<BackingMem> backing_mem_, u64 offset_)
|
||||
: backing_mem(std::move(backing_mem_)), offset(offset_) {
|
||||
ASSERT(offset < backing_mem->GetSize());
|
||||
Init();
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return cptr != nullptr;
|
||||
}
|
||||
operator u8*() {
|
||||
return cptr;
|
||||
}
|
||||
u8* GetPtr() {
|
||||
return cptr;
|
||||
}
|
||||
operator const u8*() const {
|
||||
return cptr;
|
||||
}
|
||||
const u8* GetPtr() const {
|
||||
return cptr;
|
||||
}
|
||||
std::size_t GetSize() const {
|
||||
return csize;
|
||||
}
|
||||
MemoryRef& operator+=(u32 offset_by) {
|
||||
ASSERT(offset_by < csize);
|
||||
offset += offset_by;
|
||||
Init();
|
||||
return *this;
|
||||
}
|
||||
MemoryRef operator+(u32 offset_by) const {
|
||||
ASSERT(offset_by < csize);
|
||||
return MemoryRef(backing_mem, offset + offset_by);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<BackingMem> backing_mem{};
|
||||
u64 offset{};
|
||||
// Cached values for speed
|
||||
u8* cptr{};
|
||||
std::size_t csize{};
|
||||
|
||||
void Init() {
|
||||
if (backing_mem) {
|
||||
cptr = backing_mem->GetPtr() + offset;
|
||||
csize = static_cast<std::size_t>(backing_mem->GetSize() - offset);
|
||||
} else {
|
||||
cptr = nullptr;
|
||||
csize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& backing_mem;
|
||||
ar& offset;
|
||||
Init();
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
29
src/common/serialization/atomic.h
Normal file
29
src/common/serialization/atomic.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
|
||||
namespace boost::serialization {
|
||||
|
||||
template <class Archive, class T>
|
||||
void serialize(Archive& ar, std::atomic<T>& value, const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, value, file_version);
|
||||
}
|
||||
|
||||
template <class Archive, class T>
|
||||
void save(Archive& ar, const std::atomic<T>& value, const unsigned int file_version) {
|
||||
ar << value.load();
|
||||
}
|
||||
|
||||
template <class Archive, class T>
|
||||
void load(Archive& ar, std::atomic<T>& value, const unsigned int file_version) {
|
||||
T tmp;
|
||||
ar >> tmp;
|
||||
value.store(tmp);
|
||||
}
|
||||
|
||||
} // namespace boost::serialization
|
38
src/common/serialization/boost_discrete_interval.hpp
Normal file
38
src/common/serialization/boost_discrete_interval.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/icl/discrete_interval.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace boost::serialization {
|
||||
|
||||
template <class Archive, class DomainT, ICL_COMPARE Compare>
|
||||
void save(Archive& ar, const boost::icl::discrete_interval<DomainT, Compare>& obj,
|
||||
const unsigned int file_version) {
|
||||
ar << obj.lower();
|
||||
ar << obj.upper();
|
||||
ar << obj.bounds()._bits;
|
||||
}
|
||||
|
||||
template <class Archive, class DomainT, ICL_COMPARE Compare>
|
||||
void load(Archive& ar, boost::icl::discrete_interval<DomainT, Compare>& obj,
|
||||
const unsigned int file_version) {
|
||||
DomainT upper, lower;
|
||||
boost::icl::bound_type bounds;
|
||||
ar >> lower;
|
||||
ar >> upper;
|
||||
ar >> bounds;
|
||||
obj = boost::icl::discrete_interval(lower, upper, boost::icl::interval_bounds(bounds));
|
||||
}
|
||||
|
||||
template <class Archive, class DomainT, ICL_COMPARE Compare>
|
||||
void serialize(Archive& ar, boost::icl::discrete_interval<DomainT, Compare>& obj,
|
||||
const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, obj, file_version);
|
||||
}
|
||||
|
||||
} // namespace boost::serialization
|
38
src/common/serialization/boost_flat_set.h
Normal file
38
src/common/serialization/boost_flat_set.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace boost::serialization {
|
||||
|
||||
template <class Archive, class T>
|
||||
void save(Archive& ar, const boost::container::flat_set<T>& set, const unsigned int file_version) {
|
||||
ar << static_cast<u64>(set.size());
|
||||
for (auto& v : set) {
|
||||
ar << v;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class T>
|
||||
void load(Archive& ar, boost::container::flat_set<T>& set, const unsigned int file_version) {
|
||||
u64 count{};
|
||||
ar >> count;
|
||||
set.clear();
|
||||
for (u64 i = 0; i < count; i++) {
|
||||
T value{};
|
||||
ar >> value;
|
||||
set.insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class T>
|
||||
void serialize(Archive& ar, boost::container::flat_set<T>& set, const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, set, file_version);
|
||||
}
|
||||
|
||||
} // namespace boost::serialization
|
38
src/common/serialization/boost_interval_set.hpp
Normal file
38
src/common/serialization/boost_interval_set.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
#include "common/serialization/boost_discrete_interval.hpp"
|
||||
|
||||
namespace boost::serialization {
|
||||
|
||||
template <class Archive, class T>
|
||||
void save(Archive& ar, const boost::icl::interval_set<T>& set, const unsigned int file_version) {
|
||||
ar << static_cast<u64>(set.iterative_size());
|
||||
for (auto& v : set) {
|
||||
ar << v;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class T>
|
||||
void load(Archive& ar, boost::icl::interval_set<T>& set, const unsigned int file_version) {
|
||||
u64 count{};
|
||||
ar >> count;
|
||||
set.clear();
|
||||
for (u64 i = 0; i < count; i++) {
|
||||
typename boost::icl::interval_set<T>::interval_type value{};
|
||||
ar >> value;
|
||||
set += value;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive, class T>
|
||||
void serialize(Archive& ar, boost::icl::interval_set<T>& set, const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, set, file_version);
|
||||
}
|
||||
|
||||
} // namespace boost::serialization
|
144
src/common/serialization/boost_small_vector.hpp
Normal file
144
src/common/serialization/boost_small_vector.hpp
Normal file
@ -0,0 +1,144 @@
|
||||
#ifndef BOOST_SERIALIZATION_BOOST_SMALL_VECTOR_HPP
|
||||
#define BOOST_SERIALIZATION_BOOST_SMALL_VECTOR_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// boost_vector.hpp: serialization for boost vector templates
|
||||
|
||||
// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
|
||||
// fast array serialization (C) Copyright 2005 Matthias Troyer
|
||||
// Use, modification and distribution is subject to the Boost Software
|
||||
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// See http://www.boost.org for updates, documentation, and revision history.
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/detail/workaround.hpp>
|
||||
|
||||
#include <boost/archive/detail/basic_iarchive.hpp>
|
||||
#include <boost/serialization/access.hpp>
|
||||
#include <boost/serialization/collection_size_type.hpp>
|
||||
#include <boost/serialization/item_version_type.hpp>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
|
||||
#include <boost/mpl/bool_fwd.hpp>
|
||||
#include <boost/mpl/if.hpp>
|
||||
#include <boost/serialization/array_wrapper.hpp>
|
||||
#include <boost/serialization/collections_load_imp.hpp>
|
||||
#include <boost/serialization/collections_save_imp.hpp>
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
|
||||
// default is being compatible with version 1.34.1 files, not 1.35 files
|
||||
#ifndef BOOST_SERIALIZATION_VECTOR_VERSIONED
|
||||
#define BOOST_SERIALIZATION_VECTOR_VERSIONED(V) (V == 4 || V == 5)
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// vector< T >
|
||||
|
||||
// the default versions
|
||||
|
||||
template <class Archive, class U, std::size_t N>
|
||||
inline void save(Archive& ar, const boost::container::small_vector<U, N>& t,
|
||||
const unsigned int /* file_version */, mpl::false_) {
|
||||
boost::serialization::stl::save_collection<Archive, boost::container::small_vector<U, N>>(ar,
|
||||
t);
|
||||
}
|
||||
|
||||
template <class Archive, class U, std::size_t N>
|
||||
inline void load(Archive& ar, boost::container::small_vector<U, N>& t,
|
||||
const unsigned int /* file_version */, mpl::false_) {
|
||||
const boost::archive::library_version_type library_version(ar.get_library_version());
|
||||
// retrieve number of elements
|
||||
item_version_type item_version(0);
|
||||
collection_size_type count;
|
||||
ar >> BOOST_SERIALIZATION_NVP(count);
|
||||
if (boost::archive::library_version_type(3) < library_version) {
|
||||
ar >> BOOST_SERIALIZATION_NVP(item_version);
|
||||
}
|
||||
t.reserve(count);
|
||||
stl::collection_load_impl(ar, t, count, item_version);
|
||||
}
|
||||
|
||||
// the optimized versions
|
||||
|
||||
template <class Archive, class U, std::size_t N>
|
||||
inline void save(Archive& ar, const boost::container::small_vector<U, N>& t,
|
||||
const unsigned int /* file_version */, mpl::true_) {
|
||||
const collection_size_type count(t.size());
|
||||
ar << BOOST_SERIALIZATION_NVP(count);
|
||||
if (!t.empty())
|
||||
// explict template arguments to pass intel C++ compiler
|
||||
ar << serialization::make_array<const U, collection_size_type>(static_cast<const U*>(&t[0]),
|
||||
count);
|
||||
}
|
||||
|
||||
template <class Archive, class U, std::size_t N>
|
||||
inline void load(Archive& ar, boost::container::small_vector<U, N>& t,
|
||||
const unsigned int /* file_version */, mpl::true_) {
|
||||
collection_size_type count(t.size());
|
||||
ar >> BOOST_SERIALIZATION_NVP(count);
|
||||
t.resize(count);
|
||||
unsigned int item_version = 0;
|
||||
if (BOOST_SERIALIZATION_VECTOR_VERSIONED(ar.get_library_version())) {
|
||||
ar >> BOOST_SERIALIZATION_NVP(item_version);
|
||||
}
|
||||
if (!t.empty())
|
||||
// explict template arguments to pass intel C++ compiler
|
||||
ar >> serialization::make_array<U, collection_size_type>(static_cast<U*>(&t[0]), count);
|
||||
}
|
||||
|
||||
// dispatch to either default or optimized versions
|
||||
|
||||
template <class Archive, class U, std::size_t N>
|
||||
inline void save(Archive& ar, const boost::container::small_vector<U, N>& t,
|
||||
const unsigned int file_version) {
|
||||
typedef typename boost::serialization::use_array_optimization<Archive>::template apply<
|
||||
typename remove_const<U>::type>::type use_optimized;
|
||||
save(ar, t, file_version, use_optimized());
|
||||
}
|
||||
|
||||
template <class Archive, class U, std::size_t N>
|
||||
inline void load(Archive& ar, boost::container::small_vector<U, N>& t,
|
||||
const unsigned int file_version) {
|
||||
#ifdef BOOST_SERIALIZATION_VECTOR_135_HPP
|
||||
if (ar.get_library_version() == boost::archive::library_version_type(5)) {
|
||||
load(ar, t, file_version, boost::is_arithmetic<U>());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
typedef typename boost::serialization::use_array_optimization<Archive>::template apply<
|
||||
typename remove_const<U>::type>::type use_optimized;
|
||||
load(ar, t, file_version, use_optimized());
|
||||
}
|
||||
|
||||
// split non-intrusive serialization function member into separate
|
||||
// non intrusive save/load member functions
|
||||
template <class Archive, class U, std::size_t N>
|
||||
inline void serialize(Archive& ar, boost::container::small_vector<U, N>& t,
|
||||
const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, t, file_version);
|
||||
}
|
||||
|
||||
// split non-intrusive serialization function member into separate
|
||||
// non intrusive save/load member functions
|
||||
template <class Archive, std::size_t N>
|
||||
inline void serialize(Archive& ar, boost::container::small_vector<bool, N>& t,
|
||||
const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, t, file_version);
|
||||
}
|
||||
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_SERIALIZATION_BOOST_SMALL_VECTOR_HPP
|
149
src/common/serialization/boost_vector.hpp
Normal file
149
src/common/serialization/boost_vector.hpp
Normal file
@ -0,0 +1,149 @@
|
||||
#ifndef BOOST_SERIALIZATION_BOOST_VECTOR_HPP
|
||||
#define BOOST_SERIALIZATION_BOOST_VECTOR_HPP
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
#if defined(_MSC_VER)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// boost_vector.hpp: serialization for boost vector templates
|
||||
|
||||
// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
|
||||
// fast array serialization (C) Copyright 2005 Matthias Troyer
|
||||
// Use, modification and distribution is subject to the Boost Software
|
||||
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// See http://www.boost.org for updates, documentation, and revision history.
|
||||
|
||||
#include <boost/container/vector.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/detail/workaround.hpp>
|
||||
|
||||
#include <boost/archive/detail/basic_iarchive.hpp>
|
||||
#include <boost/serialization/access.hpp>
|
||||
#include <boost/serialization/collection_size_type.hpp>
|
||||
#include <boost/serialization/item_version_type.hpp>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
|
||||
#include <boost/mpl/bool_fwd.hpp>
|
||||
#include <boost/mpl/if.hpp>
|
||||
#include <boost/serialization/array_wrapper.hpp>
|
||||
#include <boost/serialization/collections_load_imp.hpp>
|
||||
#include <boost/serialization/collections_save_imp.hpp>
|
||||
#include <boost/serialization/split_free.hpp>
|
||||
|
||||
// default is being compatible with version 1.34.1 files, not 1.35 files
|
||||
#ifndef BOOST_SERIALIZATION_VECTOR_VERSIONED
|
||||
#define BOOST_SERIALIZATION_VECTOR_VERSIONED(V) (V == 4 || V == 5)
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace serialization {
|
||||
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// vector< T >
|
||||
|
||||
// the default versions
|
||||
|
||||
template <class Archive, class U, class Allocator, class Options>
|
||||
inline void save(Archive& ar, const boost::container::vector<U, Allocator, Options>& t,
|
||||
const unsigned int /* file_version */, mpl::false_) {
|
||||
boost::serialization::stl::save_collection<Archive,
|
||||
boost::container::vector<U, Allocator, Options>>(ar,
|
||||
t);
|
||||
}
|
||||
|
||||
template <class Archive, class U, class Allocator, class Options>
|
||||
inline void load(Archive& ar, boost::container::vector<U, Allocator, Options>& t,
|
||||
const unsigned int /* file_version */, mpl::false_) {
|
||||
const boost::archive::library_version_type library_version(ar.get_library_version());
|
||||
// retrieve number of elements
|
||||
item_version_type item_version(0);
|
||||
collection_size_type count;
|
||||
ar >> BOOST_SERIALIZATION_NVP(count);
|
||||
if (boost::archive::library_version_type(3) < library_version) {
|
||||
ar >> BOOST_SERIALIZATION_NVP(item_version);
|
||||
}
|
||||
t.reserve(count);
|
||||
stl::collection_load_impl(ar, t, count, item_version);
|
||||
}
|
||||
|
||||
// the optimized versions
|
||||
|
||||
template <class Archive, class U, class Allocator, class Options>
|
||||
inline void save(Archive& ar, const boost::container::vector<U, Allocator, Options>& t,
|
||||
const unsigned int /* file_version */, mpl::true_) {
|
||||
const collection_size_type count(t.size());
|
||||
ar << BOOST_SERIALIZATION_NVP(count);
|
||||
if (!t.empty())
|
||||
// explict template arguments to pass intel C++ compiler
|
||||
ar << serialization::make_array<const U, collection_size_type>(static_cast<const U*>(&t[0]),
|
||||
count);
|
||||
}
|
||||
|
||||
template <class Archive, class U, class Allocator, class Options>
|
||||
inline void load(Archive& ar, boost::container::vector<U, Allocator, Options>& t,
|
||||
const unsigned int /* file_version */, mpl::true_) {
|
||||
collection_size_type count(t.size());
|
||||
ar >> BOOST_SERIALIZATION_NVP(count);
|
||||
t.resize(count);
|
||||
unsigned int item_version = 0;
|
||||
if (BOOST_SERIALIZATION_VECTOR_VERSIONED(ar.get_library_version())) {
|
||||
ar >> BOOST_SERIALIZATION_NVP(item_version);
|
||||
}
|
||||
if (!t.empty())
|
||||
// explict template arguments to pass intel C++ compiler
|
||||
ar >> serialization::make_array<U, collection_size_type>(static_cast<U*>(&t[0]), count);
|
||||
}
|
||||
|
||||
// dispatch to either default or optimized versions
|
||||
|
||||
template <class Archive, class U, class Allocator, class Options>
|
||||
inline void save(Archive& ar, const boost::container::vector<U, Allocator, Options>& t,
|
||||
const unsigned int file_version) {
|
||||
typedef typename boost::serialization::use_array_optimization<Archive>::template apply<
|
||||
typename remove_const<U>::type>::type use_optimized;
|
||||
save(ar, t, file_version, use_optimized());
|
||||
}
|
||||
|
||||
template <class Archive, class U, class Allocator, class Options>
|
||||
inline void load(Archive& ar, boost::container::vector<U, Allocator, Options>& t,
|
||||
const unsigned int file_version) {
|
||||
#ifdef BOOST_SERIALIZATION_VECTOR_135_HPP
|
||||
if (ar.get_library_version() == boost::archive::library_version_type(5)) {
|
||||
load(ar, t, file_version, boost::is_arithmetic<U>());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
typedef typename boost::serialization::use_array_optimization<Archive>::template apply<
|
||||
typename remove_const<U>::type>::type use_optimized;
|
||||
load(ar, t, file_version, use_optimized());
|
||||
}
|
||||
|
||||
// split non-intrusive serialization function member into separate
|
||||
// non intrusive save/load member functions
|
||||
template <class Archive, class U, class Allocator, class Options>
|
||||
inline void serialize(Archive& ar, boost::container::vector<U, Allocator, Options>& t,
|
||||
const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, t, file_version);
|
||||
}
|
||||
|
||||
// split non-intrusive serialization function member into separate
|
||||
// non intrusive save/load member functions
|
||||
template <class Archive, class Allocator, class Options>
|
||||
inline void serialize(Archive& ar, boost::container::vector<bool, Allocator, Options>& t,
|
||||
const unsigned int file_version) {
|
||||
boost::serialization::split_free(ar, t, file_version);
|
||||
}
|
||||
|
||||
} // namespace serialization
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/serialization/collection_traits.hpp>
|
||||
|
||||
BOOST_SERIALIZATION_COLLECTION_TRAITS(boost::container::vector)
|
||||
|
||||
#endif // BOOST_SERIALIZATION_BOOST_VECTOR_HPP
|
@ -7,6 +7,9 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <boost/serialization/deque.hpp>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@ -157,6 +160,52 @@ private:
|
||||
Queue* first;
|
||||
// The priority level queues of thread ids.
|
||||
std::array<Queue, NUM_QUEUES> queues;
|
||||
|
||||
s64 ToIndex(const Queue* q) const {
|
||||
if (q == nullptr) {
|
||||
return -2;
|
||||
} else if (q == UnlinkedTag()) {
|
||||
return -1;
|
||||
} else {
|
||||
return q - queues.data();
|
||||
}
|
||||
}
|
||||
|
||||
Queue* ToPointer(s64 idx) {
|
||||
if (idx == -1) {
|
||||
return UnlinkedTag();
|
||||
} else if (idx < 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return &queues[idx];
|
||||
}
|
||||
}
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void save(Archive& ar, const unsigned int file_version) const {
|
||||
const s64 idx = ToIndex(first);
|
||||
ar << idx;
|
||||
for (std::size_t i = 0; i < NUM_QUEUES; i++) {
|
||||
const s64 idx1 = ToIndex(queues[i].next_nonempty);
|
||||
ar << idx1;
|
||||
ar << queues[i].data;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void load(Archive& ar, const unsigned int file_version) {
|
||||
s64 idx;
|
||||
ar >> idx;
|
||||
first = ToPointer(idx);
|
||||
for (std::size_t i = 0; i < NUM_QUEUES; i++) {
|
||||
ar >> idx;
|
||||
queues[i].next_nonempty = ToPointer(idx);
|
||||
ar >> queues[i].data;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <boost/serialization/access.hpp>
|
||||
|
||||
namespace Common {
|
||||
|
||||
@ -44,6 +45,13 @@ class Vec4;
|
||||
|
||||
template <typename T>
|
||||
class Vec2 {
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& x;
|
||||
ar& y;
|
||||
}
|
||||
|
||||
public:
|
||||
T x;
|
||||
T y;
|
||||
@ -191,6 +199,14 @@ inline float Vec2<float>::Normalize() {
|
||||
|
||||
template <typename T>
|
||||
class Vec3 {
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& x;
|
||||
ar& y;
|
||||
ar& z;
|
||||
}
|
||||
|
||||
public:
|
||||
T x;
|
||||
T y;
|
||||
@ -399,6 +415,15 @@ using Vec3f = Vec3<float>;
|
||||
|
||||
template <typename T>
|
||||
class Vec4 {
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& x;
|
||||
ar& y;
|
||||
ar& z;
|
||||
ar& w;
|
||||
}
|
||||
|
||||
public:
|
||||
T x;
|
||||
T y;
|
||||
|
@ -164,6 +164,7 @@ add_library(core STATIC
|
||||
hle/kernel/server_session.cpp
|
||||
hle/kernel/server_session.h
|
||||
hle/kernel/session.h
|
||||
hle/kernel/session.cpp
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/shared_memory.h
|
||||
hle/kernel/shared_page.cpp
|
||||
@ -450,6 +451,8 @@ add_library(core STATIC
|
||||
rpc/server.h
|
||||
rpc/udp_server.cpp
|
||||
rpc/udp_server.h
|
||||
savestate.cpp
|
||||
savestate.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
telemetry_session.cpp
|
||||
@ -469,7 +472,7 @@ endif()
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives Boost::serialization)
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
get_directory_property(OPENSSL_LIBS
|
||||
|
@ -6,10 +6,14 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/skyeye_common/arm_regformat.h"
|
||||
#include "core/arm/skyeye_common/vfp/asm_vfp.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Memory {
|
||||
struct PageTable;
|
||||
@ -23,6 +27,48 @@ public:
|
||||
virtual ~ARM_Interface() {}
|
||||
|
||||
class ThreadContext {
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template <class Archive>
|
||||
void save(Archive& ar, const unsigned int file_version) const {
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
const auto r = GetCpuRegister(i);
|
||||
ar << r;
|
||||
}
|
||||
std::size_t fpu_reg_count = file_version == 0 ? 16 : 64;
|
||||
for (std::size_t i = 0; i < fpu_reg_count; i++) {
|
||||
const auto r = GetFpuRegister(i);
|
||||
ar << r;
|
||||
}
|
||||
const auto r1 = GetCpsr();
|
||||
ar << r1;
|
||||
const auto r2 = GetFpscr();
|
||||
ar << r2;
|
||||
const auto r3 = GetFpexc();
|
||||
ar << r3;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void load(Archive& ar, const unsigned int file_version) {
|
||||
u32 r;
|
||||
for (std::size_t i = 0; i < 16; i++) {
|
||||
ar >> r;
|
||||
SetCpuRegister(i, r);
|
||||
}
|
||||
std::size_t fpu_reg_count = file_version == 0 ? 16 : 64;
|
||||
for (std::size_t i = 0; i < fpu_reg_count; i++) {
|
||||
ar >> r;
|
||||
SetFpuRegister(i, r);
|
||||
}
|
||||
ar >> r;
|
||||
SetCpsr(r);
|
||||
ar >> r;
|
||||
SetFpscr(r);
|
||||
ar >> r;
|
||||
SetFpexc(r);
|
||||
}
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
public:
|
||||
virtual ~ThreadContext() = default;
|
||||
|
||||
@ -77,7 +123,7 @@ public:
|
||||
virtual void InvalidateCacheRange(u32 start_address, std::size_t length) = 0;
|
||||
|
||||
/// Notify CPU emulation that page tables have changed
|
||||
virtual void PageTableChanged(Memory::PageTable* new_page_table) = 0;
|
||||
virtual void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) = 0;
|
||||
|
||||
/**
|
||||
* Set the Program Counter to an address
|
||||
@ -150,7 +196,7 @@ public:
|
||||
* @param reg The CP15 register to retrieve the value from.
|
||||
* @return the value stored in the given CP15 register.
|
||||
*/
|
||||
virtual u32 GetCP15Register(CP15Register reg) = 0;
|
||||
virtual u32 GetCP15Register(CP15Register reg) const = 0;
|
||||
|
||||
/**
|
||||
* Stores the given value into the indicated CP15 register.
|
||||
@ -180,6 +226,8 @@ public:
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
virtual void PurgeState() = 0;
|
||||
|
||||
std::shared_ptr<Core::Timing::Timer> GetTimer() {
|
||||
return timer;
|
||||
}
|
||||
@ -189,8 +237,101 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
// This us used for serialization. Returning nullptr is valid if page tables are not used.
|
||||
virtual std::shared_ptr<Memory::PageTable> GetPageTable() const = 0;
|
||||
|
||||
std::shared_ptr<Core::Timing::Timer> timer;
|
||||
|
||||
private:
|
||||
u32 id;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
|
||||
template <class Archive>
|
||||
void save(Archive& ar, const unsigned int file_version) const {
|
||||
ar << timer;
|
||||
ar << id;
|
||||
const auto page_table = GetPageTable();
|
||||
ar << page_table;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
const auto r = GetReg(i);
|
||||
ar << r;
|
||||
}
|
||||
const auto pc = GetPC();
|
||||
ar << pc;
|
||||
const auto cpsr = GetCPSR();
|
||||
ar << cpsr;
|
||||
int vfp_reg_count = file_version == 0 ? 32 : 64;
|
||||
for (int i = 0; i < vfp_reg_count; i++) {
|
||||
const auto r = GetVFPReg(i);
|
||||
ar << r;
|
||||
}
|
||||
for (std::size_t i = 0; i < VFPSystemRegister::VFP_SYSTEM_REGISTER_COUNT; i++) {
|
||||
const auto reg = static_cast<VFPSystemRegister>(i);
|
||||
u32 r = 0;
|
||||
switch (reg) {
|
||||
case VFP_FPSCR:
|
||||
case VFP_FPEXC:
|
||||
r = GetVFPSystemReg(reg);
|
||||
}
|
||||
ar << r;
|
||||
}
|
||||
for (std::size_t i = 0; i < CP15Register::CP15_REGISTER_COUNT; i++) {
|
||||
const auto reg = static_cast<CP15Register>(i);
|
||||
u32 r = 0;
|
||||
switch (reg) {
|
||||
case CP15_THREAD_UPRW:
|
||||
case CP15_THREAD_URO:
|
||||
r = GetCP15Register(reg);
|
||||
}
|
||||
ar << r;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void load(Archive& ar, const unsigned int file_version) {
|
||||
PurgeState();
|
||||
ar >> timer;
|
||||
ar >> id;
|
||||
std::shared_ptr<Memory::PageTable> page_table{};
|
||||
ar >> page_table;
|
||||
SetPageTable(page_table);
|
||||
u32 r;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
ar >> r;
|
||||
SetReg(i, r);
|
||||
}
|
||||
ar >> r;
|
||||
SetPC(r);
|
||||
ar >> r;
|
||||
SetCPSR(r);
|
||||
int vfp_reg_count = file_version == 0 ? 32 : 64;
|
||||
for (int i = 0; i < vfp_reg_count; i++) {
|
||||
ar >> r;
|
||||
SetVFPReg(i, r);
|
||||
}
|
||||
for (std::size_t i = 0; i < VFPSystemRegister::VFP_SYSTEM_REGISTER_COUNT; i++) {
|
||||
ar >> r;
|
||||
const auto reg = static_cast<VFPSystemRegister>(i);
|
||||
switch (reg) {
|
||||
case VFP_FPSCR:
|
||||
case VFP_FPEXC:
|
||||
SetVFPSystemReg(reg, r);
|
||||
}
|
||||
}
|
||||
for (std::size_t i = 0; i < CP15Register::CP15_REGISTER_COUNT; i++) {
|
||||
ar >> r;
|
||||
const auto reg = static_cast<CP15Register>(i);
|
||||
switch (reg) {
|
||||
case CP15_THREAD_UPRW:
|
||||
case CP15_THREAD_URO:
|
||||
SetCP15Register(reg, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
};
|
||||
|
||||
BOOST_CLASS_VERSION(ARM_Interface, 1)
|
||||
BOOST_CLASS_VERSION(ARM_Interface::ThreadContext, 1)
|
||||
|
@ -153,7 +153,7 @@ ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u
|
||||
std::shared_ptr<Core::Timing::Timer> timer)
|
||||
: ARM_Interface(id, timer), system(*system), memory(memory),
|
||||
cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
|
||||
PageTableChanged(memory.GetCurrentPageTable());
|
||||
SetPageTable(memory.GetCurrentPageTable());
|
||||
}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
@ -229,7 +229,7 @@ void ARM_Dynarmic::SetCPSR(u32 cpsr) {
|
||||
jit->SetCpsr(cpsr);
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) {
|
||||
u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) const {
|
||||
switch (reg) {
|
||||
case CP15_THREAD_UPRW:
|
||||
return cp15_state.cp15_thread_uprw;
|
||||
@ -287,17 +287,27 @@ void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, std::size_t length) {
|
||||
jit->InvalidateCacheRange(start_address, length);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged(Memory::PageTable* new_page_table) {
|
||||
current_page_table = new_page_table;
|
||||
std::shared_ptr<Memory::PageTable> ARM_Dynarmic::GetPageTable() const {
|
||||
return current_page_table;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) {
|
||||
current_page_table = page_table;
|
||||
Dynarmic::A32::Context ctx{};
|
||||
if (jit) {
|
||||
jit->SaveContext(ctx);
|
||||
}
|
||||
|
||||
auto iter = jits.find(current_page_table);
|
||||
if (iter != jits.end()) {
|
||||
jit = iter->second.get();
|
||||
jit->LoadContext(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
auto new_jit = MakeJit();
|
||||
jit = new_jit.get();
|
||||
jit->LoadContext(ctx);
|
||||
jits.emplace(current_page_table, std::move(new_jit));
|
||||
}
|
||||
|
||||
@ -311,8 +321,12 @@ void ARM_Dynarmic::ServeBreak() {
|
||||
std::unique_ptr<Dynarmic::A32::Jit> ARM_Dynarmic::MakeJit() {
|
||||
Dynarmic::A32::UserConfig config;
|
||||
config.callbacks = cb.get();
|
||||
config.page_table = ¤t_page_table->pointers;
|
||||
config.page_table = ¤t_page_table->GetPointerArray();
|
||||
config.coprocessors[15] = std::make_shared<DynarmicCP15>(cp15_state);
|
||||
config.define_unpredictable_behaviour = true;
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PurgeState() {
|
||||
ClearInstructionCache();
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
void SetVFPSystemReg(VFPSystemRegister reg, u32 value) override;
|
||||
u32 GetCPSR() const override;
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
u32 GetCP15Register(CP15Register reg) override;
|
||||
u32 GetCP15Register(CP15Register reg) const override;
|
||||
void SetCP15Register(CP15Register reg, u32 value) override;
|
||||
|
||||
std::unique_ptr<ThreadContext> NewContext() const override;
|
||||
@ -52,7 +52,11 @@ public:
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void InvalidateCacheRange(u32 start_address, std::size_t length) override;
|
||||
void PageTableChanged(Memory::PageTable* new_page_table) override;
|
||||
void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) override;
|
||||
void PurgeState() override;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Memory::PageTable> GetPageTable() const override;
|
||||
|
||||
private:
|
||||
void ServeBreak();
|
||||
@ -67,6 +71,6 @@ private:
|
||||
CP15State cp15_state;
|
||||
|
||||
Dynarmic::A32::Jit* jit = nullptr;
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
std::map<Memory::PageTable*, std::unique_ptr<Dynarmic::A32::Jit>> jits;
|
||||
std::shared_ptr<Memory::PageTable> current_page_table = nullptr;
|
||||
std::map<std::shared_ptr<Memory::PageTable>, std::unique_ptr<Dynarmic::A32::Jit>> jits;
|
||||
};
|
||||
|
@ -95,10 +95,16 @@ void ARM_DynCom::InvalidateCacheRange(u32, std::size_t) {
|
||||
ClearInstructionCache();
|
||||
}
|
||||
|
||||
void ARM_DynCom::PageTableChanged(Memory::PageTable*) {
|
||||
void ARM_DynCom::SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) {
|
||||
ClearInstructionCache();
|
||||
}
|
||||
|
||||
std::shared_ptr<Memory::PageTable> ARM_DynCom::GetPageTable() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ARM_DynCom::PurgeState() {}
|
||||
|
||||
void ARM_DynCom::SetPC(u32 pc) {
|
||||
state->Reg[15] = pc;
|
||||
}
|
||||
@ -139,7 +145,7 @@ void ARM_DynCom::SetCPSR(u32 cpsr) {
|
||||
state->Cpsr = cpsr;
|
||||
}
|
||||
|
||||
u32 ARM_DynCom::GetCP15Register(CP15Register reg) {
|
||||
u32 ARM_DynCom::GetCP15Register(CP15Register reg) const {
|
||||
return state->CP15[reg];
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,6 @@ public:
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void InvalidateCacheRange(u32 start_address, std::size_t length) override;
|
||||
void PageTableChanged(Memory::PageTable* new_page_table) override;
|
||||
|
||||
void SetPC(u32 pc) override;
|
||||
u32 GetPC() const override;
|
||||
@ -42,14 +41,19 @@ public:
|
||||
void SetVFPSystemReg(VFPSystemRegister reg, u32 value) override;
|
||||
u32 GetCPSR() const override;
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
u32 GetCP15Register(CP15Register reg) override;
|
||||
u32 GetCP15Register(CP15Register reg) const override;
|
||||
void SetCP15Register(CP15Register reg, u32 value) override;
|
||||
|
||||
std::unique_ptr<ThreadContext> NewContext() const override;
|
||||
void SaveContext(const std::unique_ptr<ThreadContext>& arg) override;
|
||||
void LoadContext(const std::unique_ptr<ThreadContext>& arg) override;
|
||||
|
||||
void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) override;
|
||||
void PrepareReschedule() override;
|
||||
void PurgeState() override;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Memory::PageTable> GetPageTable() const override;
|
||||
|
||||
private:
|
||||
void ExecuteInstructions(u64 num_instructions);
|
||||
|
@ -18,6 +18,10 @@ constexpr u64 run_interval_ticks = BASE_CLOCK_RATE_ARM11 / 60;
|
||||
|
||||
CheatEngine::CheatEngine(Core::System& system_) : system(system_) {
|
||||
LoadCheatFile();
|
||||
Connect();
|
||||
}
|
||||
|
||||
void CheatEngine::Connect() {
|
||||
event = system.CoreTiming().RegisterEvent(
|
||||
"CheatCore::run_event",
|
||||
[this](u64 thread_id, s64 cycle_late) { RunCallback(thread_id, cycle_late); });
|
||||
|
@ -26,6 +26,7 @@ class CheatEngine {
|
||||
public:
|
||||
explicit CheatEngine(Core::System& system);
|
||||
~CheatEngine();
|
||||
void Connect();
|
||||
std::vector<std::shared_ptr<CheatBase>> GetCheats() const;
|
||||
void AddCheat(const std::shared_ptr<CheatBase>& cheat);
|
||||
void RemoveCheat(int index);
|
||||
|
@ -2,8 +2,11 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include "audio_core/dsp_interface.h"
|
||||
#include "audio_core/hle/hle.h"
|
||||
#include "audio_core/lle/lle.h"
|
||||
@ -23,25 +26,48 @@
|
||||
#endif
|
||||
#include "core/custom_tex_cache.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/global.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/gsp/gsp.h"
|
||||
#include "core/hle/service/pm/pm_app.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hw/gpu.h"
|
||||
#include "core/hw/hw.h"
|
||||
#include "core/hw/lcd.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/movie.h"
|
||||
#include "core/rpc/rpc_server.h"
|
||||
#include "core/settings.h"
|
||||
#include "network/network.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
/*static*/ System System::s_instance;
|
||||
|
||||
template <>
|
||||
Core::System& Global() {
|
||||
return System::GetInstance();
|
||||
}
|
||||
|
||||
template <>
|
||||
Kernel::KernelSystem& Global() {
|
||||
return System::GetInstance().Kernel();
|
||||
}
|
||||
|
||||
template <>
|
||||
Core::Timing& Global() {
|
||||
return System::GetInstance().CoreTiming();
|
||||
}
|
||||
|
||||
System::~System() = default;
|
||||
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
if (std::any_of(cpu_cores.begin(), cpu_cores.end(),
|
||||
@ -67,6 +93,52 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
}
|
||||
}
|
||||
|
||||
Signal signal{Signal::None};
|
||||
u32 param{};
|
||||
{
|
||||
std::lock_guard lock{signal_mutex};
|
||||
if (current_signal != Signal::None) {
|
||||
signal = current_signal;
|
||||
param = signal_param;
|
||||
current_signal = Signal::None;
|
||||
}
|
||||
}
|
||||
switch (signal) {
|
||||
case Signal::Reset:
|
||||
Reset();
|
||||
return ResultStatus::Success;
|
||||
case Signal::Shutdown:
|
||||
return ResultStatus::ShutdownRequested;
|
||||
case Signal::Load: {
|
||||
LOG_INFO(Core, "Begin load");
|
||||
try {
|
||||
System::LoadState(param);
|
||||
LOG_INFO(Core, "Load completed");
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(Core, "Error loading: {}", e.what());
|
||||
status_details = e.what();
|
||||
return ResultStatus::ErrorSavestate;
|
||||
}
|
||||
frame_limiter.WaitOnce();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
case Signal::Save: {
|
||||
LOG_INFO(Core, "Begin save");
|
||||
try {
|
||||
System::SaveState(param);
|
||||
LOG_INFO(Core, "Save completed");
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(Core, "Error saving: {}", e.what());
|
||||
status_details = e.what();
|
||||
return ResultStatus::ErrorSavestate;
|
||||
}
|
||||
frame_limiter.WaitOnce();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// All cores should have executed the same amount of ticks. If this is not the case an event was
|
||||
// scheduled with a cycles_into_future smaller then the current downcount.
|
||||
// So we have to get those cores to the same global time first
|
||||
@ -141,20 +213,26 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
HW::Update();
|
||||
Reschedule();
|
||||
|
||||
if (reset_requested.exchange(false)) {
|
||||
Reset();
|
||||
} else if (shutdown_requested.exchange(false)) {
|
||||
return ResultStatus::ShutdownRequested;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool System::SendSignal(System::Signal signal, u32 param) {
|
||||
std::lock_guard lock{signal_mutex};
|
||||
if (current_signal != signal && current_signal != Signal::None) {
|
||||
LOG_ERROR(Core, "Unable to {} as {} is ongoing", signal, current_signal);
|
||||
return false;
|
||||
}
|
||||
current_signal = signal;
|
||||
signal_param = param;
|
||||
return true;
|
||||
}
|
||||
|
||||
System::ResultStatus System::SingleStep() {
|
||||
return RunLoop(false);
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
FileUtil::SetCurrentRomPath(filepath);
|
||||
app_loader = Loader::GetLoader(filepath);
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
@ -180,7 +258,11 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
ASSERT(system_mode.first);
|
||||
auto n3ds_mode = app_loader->LoadKernelN3dsMode();
|
||||
ASSERT(n3ds_mode.first);
|
||||
ResultStatus init_result{Init(emu_window, *system_mode.first, *n3ds_mode.first)};
|
||||
u32 num_cores = 2;
|
||||
if (Settings::values.is_new_3ds) {
|
||||
num_cores = 4;
|
||||
}
|
||||
ResultStatus init_result{Init(emu_window, *system_mode.first, *n3ds_mode.first, num_cores)};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
||||
static_cast<u32>(init_result));
|
||||
@ -206,7 +288,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
}
|
||||
}
|
||||
cheat_engine = std::make_unique<Cheats::CheatEngine>(*this);
|
||||
u64 title_id{0};
|
||||
title_id = 0;
|
||||
if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
|
||||
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
|
||||
static_cast<u32>(load_result));
|
||||
@ -237,7 +319,8 @@ void System::PrepareReschedule() {
|
||||
}
|
||||
|
||||
PerfStats::Results System::GetAndResetPerfStats() {
|
||||
return perf_stats->GetAndResetStats(timing->GetGlobalTimeUs());
|
||||
return (perf_stats && timing) ? perf_stats->GetAndResetStats(timing->GetGlobalTimeUs())
|
||||
: PerfStats::Results{};
|
||||
}
|
||||
|
||||
void System::Reschedule() {
|
||||
@ -252,14 +335,10 @@ void System::Reschedule() {
|
||||
}
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode) {
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode,
|
||||
u32 num_cores) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
std::size_t num_cores = 2;
|
||||
if (Settings::values.is_new_3ds) {
|
||||
num_cores = 4;
|
||||
}
|
||||
|
||||
memory = std::make_unique<Memory::MemorySystem>();
|
||||
|
||||
timing = std::make_unique<Timing>(num_cores, Settings::values.cpu_clock_percentage);
|
||||
@ -269,19 +348,19 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
|
||||
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
for (std::size_t i = 0; i < num_cores; ++i) {
|
||||
for (u32 i = 0; i < num_cores; ++i) {
|
||||
cpu_cores.push_back(
|
||||
std::make_shared<ARM_Dynarmic>(this, *memory, i, timing->GetTimer(i)));
|
||||
}
|
||||
#else
|
||||
for (std::size_t i = 0; i < num_cores; ++i) {
|
||||
for (u32 i = 0; i < num_cores; ++i) {
|
||||
cpu_cores.push_back(
|
||||
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
|
||||
}
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
} else {
|
||||
for (std::size_t i = 0; i < num_cores; ++i) {
|
||||
for (u32 i = 0; i < num_cores; ++i) {
|
||||
cpu_cores.push_back(
|
||||
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
|
||||
}
|
||||
@ -307,7 +386,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
|
||||
|
||||
rpc_server = std::make_unique<RPC::RPCServer>();
|
||||
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>(*this);
|
||||
service_manager = std::make_unique<Service::SM::ServiceManager>(*this);
|
||||
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
|
||||
|
||||
HW::Init(*memory);
|
||||
@ -419,7 +498,7 @@ void System::RegisterImageInterface(std::shared_ptr<Frontend::ImageInterface> im
|
||||
registered_image_interface = std::move(image_interface);
|
||||
}
|
||||
|
||||
void System::Shutdown() {
|
||||
void System::Shutdown(bool is_deserializing) {
|
||||
// Log last frame performance stats
|
||||
const auto perf_results = GetAndResetPerfStats();
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
|
||||
@ -432,20 +511,22 @@ void System::Shutdown() {
|
||||
perf_stats->GetMeanFrametime());
|
||||
|
||||
// Shutdown emulation session
|
||||
GDBStub::Shutdown();
|
||||
VideoCore::Shutdown();
|
||||
HW::Shutdown();
|
||||
if (!is_deserializing) {
|
||||
GDBStub::Shutdown();
|
||||
perf_stats.reset();
|
||||
cheat_engine.reset();
|
||||
app_loader.reset();
|
||||
}
|
||||
telemetry_session.reset();
|
||||
perf_stats.reset();
|
||||
rpc_server.reset();
|
||||
cheat_engine.reset();
|
||||
archive_manager.reset();
|
||||
service_manager.reset();
|
||||
dsp_core.reset();
|
||||
cpu_cores.clear();
|
||||
kernel.reset();
|
||||
timing.reset();
|
||||
app_loader.reset();
|
||||
|
||||
if (video_dumper->IsDumping()) {
|
||||
video_dumper->StopDumping();
|
||||
@ -469,4 +550,63 @@ void System::Reset() {
|
||||
Load(*m_emu_window, m_filepath);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void System::serialize(Archive& ar, const unsigned int file_version) {
|
||||
|
||||
u32 num_cores;
|
||||
if (Archive::is_saving::value) {
|
||||
num_cores = this->GetNumCores();
|
||||
}
|
||||
ar& num_cores;
|
||||
|
||||
if (Archive::is_loading::value) {
|
||||
// When loading, we want to make sure any lingering state gets cleared out before we begin.
|
||||
// Shutdown, but persist a few things between loads...
|
||||
Shutdown(true);
|
||||
|
||||
// Re-initialize everything like it was before
|
||||
auto system_mode = this->app_loader->LoadKernelSystemMode();
|
||||
auto n3ds_mode = this->app_loader->LoadKernelN3dsMode();
|
||||
Init(*m_emu_window, *system_mode.first, *n3ds_mode.first, num_cores);
|
||||
}
|
||||
|
||||
// flush on save, don't flush on load
|
||||
bool should_flush = !Archive::is_loading::value;
|
||||
Memory::RasterizerClearAll(should_flush);
|
||||
ar&* timing.get();
|
||||
for (u32 i = 0; i < num_cores; i++) {
|
||||
ar&* cpu_cores[i].get();
|
||||
}
|
||||
ar&* service_manager.get();
|
||||
ar&* archive_manager.get();
|
||||
ar& GPU::g_regs;
|
||||
ar& LCD::g_regs;
|
||||
|
||||
// NOTE: DSP doesn't like being destroyed and recreated. So instead we do an inline
|
||||
// serialization; this means that the DSP Settings need to match for loading to work.
|
||||
auto dsp_hle = dynamic_cast<AudioCore::DspHle*>(dsp_core.get());
|
||||
if (dsp_hle) {
|
||||
ar&* dsp_hle;
|
||||
} else {
|
||||
throw std::runtime_error("LLE audio not supported for save states");
|
||||
}
|
||||
|
||||
ar&* memory.get();
|
||||
ar&* kernel.get();
|
||||
VideoCore::serialize(ar, file_version);
|
||||
if (file_version >= 1) {
|
||||
ar& Movie::GetInstance();
|
||||
}
|
||||
|
||||
// This needs to be set from somewhere - might as well be here!
|
||||
if (Archive::is_loading::value) {
|
||||
Service::GSP::SetGlobalModule(*this);
|
||||
memory->SetDSP(*dsp_core);
|
||||
cheat_engine->Connect();
|
||||
VideoCore::g_renderer->Sync();
|
||||
}
|
||||
}
|
||||
|
||||
SERIALIZE_IMPL(System)
|
||||
|
||||
} // namespace Core
|
||||
|
@ -5,7 +5,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/custom_tex_cache.h"
|
||||
#include "core/frontend/applets/mii_selector.h"
|
||||
@ -87,10 +89,13 @@ public:
|
||||
/// generic drivers installed
|
||||
ErrorVideoCore_ErrorBelowGL33, ///< Error in the video core due to the user not having
|
||||
/// OpenGL 3.3 or higher
|
||||
ErrorSavestate, ///< Error saving or loading
|
||||
ShutdownRequested, ///< Emulated program requested a system shutdown
|
||||
ErrorUnknown ///< Any other error
|
||||
};
|
||||
|
||||
~System();
|
||||
|
||||
/**
|
||||
* Run the core CPU loop
|
||||
* This function runs the core for the specified number of CPU instructions before trying to
|
||||
@ -110,19 +115,23 @@ public:
|
||||
ResultStatus SingleStep();
|
||||
|
||||
/// Shutdown the emulated system.
|
||||
void Shutdown();
|
||||
void Shutdown(bool is_deserializing = false);
|
||||
|
||||
/// Shutdown and then load again
|
||||
void Reset();
|
||||
|
||||
enum class Signal : u32 { None, Shutdown, Reset, Save, Load };
|
||||
|
||||
bool SendSignal(Signal signal, u32 param = 0);
|
||||
|
||||
/// Request reset of the system
|
||||
void RequestReset() {
|
||||
reset_requested = true;
|
||||
SendSignal(Signal::Reset);
|
||||
}
|
||||
|
||||
/// Request shutdown of the system
|
||||
void RequestShutdown() {
|
||||
shutdown_requested = true;
|
||||
SendSignal(Signal::Shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,7 +188,7 @@ public:
|
||||
};
|
||||
|
||||
u32 GetNumCores() const {
|
||||
return cpu_cores.size();
|
||||
return static_cast<u32>(cpu_cores.size());
|
||||
}
|
||||
|
||||
void InvalidateCacheRange(u32 start_address, std::size_t length) {
|
||||
@ -295,6 +304,10 @@ public:
|
||||
return registered_image_interface;
|
||||
}
|
||||
|
||||
void SaveState(u32 slot) const;
|
||||
|
||||
void LoadState(u32 slot);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
@ -303,7 +316,8 @@ private:
|
||||
* @param system_mode The system mode.
|
||||
* @return ResultStatus code, indicating if the operation succeeded.
|
||||
*/
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode);
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode,
|
||||
u32 num_cores);
|
||||
|
||||
/// Reschedule the core emulation
|
||||
void Reschedule();
|
||||
@ -325,7 +339,7 @@ private:
|
||||
std::unique_ptr<Core::TelemetrySession> telemetry_session;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
std::unique_ptr<Service::SM::ServiceManager> service_manager;
|
||||
|
||||
/// Frontend applets
|
||||
std::shared_ptr<Frontend::MiiSelector> registered_mii_selector;
|
||||
@ -362,9 +376,15 @@ private:
|
||||
/// Saved variables for reset
|
||||
Frontend::EmuWindow* m_emu_window;
|
||||
std::string m_filepath;
|
||||
u64 title_id;
|
||||
|
||||
std::atomic<bool> reset_requested;
|
||||
std::atomic<bool> shutdown_requested;
|
||||
std::mutex signal_mutex;
|
||||
Signal current_signal;
|
||||
u32 signal_param;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version);
|
||||
};
|
||||
|
||||
inline ARM_Interface& GetRunningCore() {
|
||||
@ -384,3 +404,5 @@ inline AudioCore::DspInterface& DSP() {
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
BOOST_CLASS_VERSION(Core::System, 1)
|
||||
|
@ -23,8 +23,9 @@ bool Timing::Event::operator<(const Timing::Event& right) const {
|
||||
Timing::Timing(std::size_t num_cores, u32 cpu_clock_percentage) {
|
||||
timers.resize(num_cores);
|
||||
for (std::size_t i = 0; i < num_cores; ++i) {
|
||||
timers[i] = std::make_shared<Timer>(100.0 / cpu_clock_percentage);
|
||||
timers[i] = std::make_shared<Timer>();
|
||||
}
|
||||
UpdateClockSpeed(cpu_clock_percentage);
|
||||
current_timer = timers[0];
|
||||
}
|
||||
|
||||
@ -37,14 +38,12 @@ void Timing::UpdateClockSpeed(u32 cpu_clock_percentage) {
|
||||
TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||
// check for existing type with same name.
|
||||
// we want event type names to remain unique so that we can use them for serialization.
|
||||
ASSERT_MSG(event_types.find(name) == event_types.end(),
|
||||
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
|
||||
"during Init to avoid breaking save states.",
|
||||
name);
|
||||
|
||||
auto info = event_types.emplace(name, TimingEventType{callback, nullptr});
|
||||
auto info = event_types.emplace(name, TimingEventType{});
|
||||
TimingEventType* event_type = &info.first->second;
|
||||
event_type->name = &info.first->first;
|
||||
if (callback != nullptr) {
|
||||
event_type->callback = callback;
|
||||
}
|
||||
return event_type;
|
||||
}
|
||||
|
||||
@ -123,7 +122,7 @@ std::shared_ptr<Timing::Timer> Timing::GetTimer(std::size_t cpu_id) {
|
||||
return timers[cpu_id];
|
||||
}
|
||||
|
||||
Timing::Timer::Timer(double cpu_clock_scale_) : cpu_clock_scale(cpu_clock_scale_) {}
|
||||
Timing::Timer::Timer() = default;
|
||||
|
||||
Timing::Timer::~Timer() {
|
||||
MoveEvents();
|
||||
@ -184,7 +183,11 @@ void Timing::Timer::Advance(s64 max_slice_length) {
|
||||
Event evt = std::move(event_queue.front());
|
||||
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
event_queue.pop_back();
|
||||
evt.type->callback(evt.userdata, executed_ticks - evt.time);
|
||||
if (evt.type->callback != nullptr) {
|
||||
evt.type->callback(evt.userdata, executed_ticks - evt.time);
|
||||
} else {
|
||||
LOG_ERROR(Core, "Event '{}' has no callback", *evt.type->name);
|
||||
}
|
||||
}
|
||||
|
||||
is_timer_sane = false;
|
||||
|
@ -23,9 +23,12 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/global.h"
|
||||
|
||||
// The timing we get from the assembly is 268,111,855.956 Hz
|
||||
// It is possible that this number isn't just an integer because the compiler could have
|
||||
@ -133,6 +136,7 @@ struct TimingEventType {
|
||||
};
|
||||
|
||||
class Timing {
|
||||
|
||||
public:
|
||||
struct Event {
|
||||
s64 time;
|
||||
@ -142,13 +146,36 @@ public:
|
||||
|
||||
bool operator>(const Event& right) const;
|
||||
bool operator<(const Event& right) const;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void save(Archive& ar, const unsigned int) const {
|
||||
ar& time;
|
||||
ar& fifo_order;
|
||||
ar& userdata;
|
||||
std::string name = *(type->name);
|
||||
ar << name;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void load(Archive& ar, const unsigned int) {
|
||||
ar& time;
|
||||
ar& fifo_order;
|
||||
ar& userdata;
|
||||
std::string name;
|
||||
ar >> name;
|
||||
type = Global<Timing>().RegisterEvent(name, nullptr);
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
};
|
||||
|
||||
static constexpr int MAX_SLICE_LENGTH = 20000;
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer(double cpu_clock_scale);
|
||||
Timer();
|
||||
~Timer();
|
||||
|
||||
s64 GetMaxSliceLength() const;
|
||||
@ -195,6 +222,19 @@ public:
|
||||
// Stores a scaling for the internal clockspeed. Changing this number results in
|
||||
// under/overclocking the guest cpu
|
||||
double cpu_clock_scale = 1.0;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
MoveEvents();
|
||||
// NOTE: ts_queue should be empty now
|
||||
ar& event_queue;
|
||||
ar& event_fifo_id;
|
||||
ar& slice_length;
|
||||
ar& downcount;
|
||||
ar& executed_ticks;
|
||||
ar& idled_cycles;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
explicit Timing(std::size_t num_cores, u32 cpu_clock_percentage);
|
||||
@ -246,6 +286,15 @@ private:
|
||||
// Stores a scaling for the internal clockspeed. Changing this number results in
|
||||
// under/overclocking the guest cpu
|
||||
double cpu_clock_scale = 1.0;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
// event_types set during initialization of other things
|
||||
ar& global_timer;
|
||||
ar& timers;
|
||||
ar& current_timer;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@ -64,6 +66,32 @@ private:
|
||||
std::vector<u8> binary;
|
||||
std::string string;
|
||||
std::u16string u16str;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& type;
|
||||
switch (type) {
|
||||
case LowPathType::Binary:
|
||||
ar& binary;
|
||||
break;
|
||||
case LowPathType::Char:
|
||||
ar& string;
|
||||
break;
|
||||
case LowPathType::Wchar: {
|
||||
std::vector<char16_t> data;
|
||||
if (Archive::is_saving::value) {
|
||||
std::copy(u16str.begin(), u16str.end(), std::back_inserter(data));
|
||||
}
|
||||
ar& data;
|
||||
if (Archive::is_loading::value) {
|
||||
u16str = std::u16string(data.data(), data.size());
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// Parameters of the archive, as specified in the Create or Format call.
|
||||
@ -169,6 +197,13 @@ public:
|
||||
|
||||
protected:
|
||||
std::unique_ptr<DelayGenerator> delay_generator;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& delay_generator;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class ArchiveFactory : NonCopyable {
|
||||
@ -205,6 +240,10 @@ public:
|
||||
* @return Format information about the archive or error code
|
||||
*/
|
||||
virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const = 0;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -19,6 +20,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_ExtSaveData)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/**
|
||||
@ -77,6 +80,8 @@ public:
|
||||
static constexpr u64 IPCDelayNanoseconds(3085068);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
/**
|
||||
@ -162,6 +167,14 @@ public:
|
||||
}
|
||||
return SaveDataArchive::CreateFile(path, size);
|
||||
}
|
||||
|
||||
private:
|
||||
ExtSaveDataArchive() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<SaveDataArchive>(*this);
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
struct ExtSaveDataArchivePath {
|
||||
@ -297,3 +310,6 @@ void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ExtSaveDataDelayGenerator)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ExtSaveDataArchive)
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -54,6 +56,15 @@ private:
|
||||
|
||||
/// Returns a path with the correct SaveIdHigh value for Shared extdata paths.
|
||||
Path GetCorrectedPath(const Path& path);
|
||||
|
||||
ArchiveFactory_ExtSaveData() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& shared;
|
||||
ar& mount_point;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -93,4 +104,9 @@ std::string GetExtDataContainerPath(const std::string& mount_point, bool shared)
|
||||
*/
|
||||
Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low);
|
||||
|
||||
class ExtSaveDataDelayGenerator;
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_ExtSaveData)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ExtSaveDataDelayGenerator)
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "bad_word_list.app.romfs.h"
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -28,6 +29,10 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::NCCHArchive)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::NCCHFile)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_NCCH)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
struct NCCHArchivePath {
|
||||
|
@ -7,6 +7,9 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -63,6 +66,17 @@ public:
|
||||
protected:
|
||||
u64 title_id;
|
||||
Service::FS::MediaType media_type;
|
||||
|
||||
private:
|
||||
NCCHArchive() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveBackend>(*this);
|
||||
ar& title_id;
|
||||
ar& media_type;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
// File backend for NCCH files
|
||||
@ -82,6 +96,15 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<u8> file_buffer;
|
||||
|
||||
NCCHFile() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<FileBackend>(*this);
|
||||
ar& file_buffer;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// File system interface to the NCCH archive
|
||||
@ -97,6 +120,17 @@ public:
|
||||
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::NCCHArchive)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::NCCHFile)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_NCCH)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include "common/archives.h"
|
||||
#include "core/file_sys/archive_other_savedata.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@ -12,6 +13,9 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_OtherSaveDataPermitted)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_OtherSaveDataGeneral)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
// TODO(wwylele): The storage info in exheader should be checked before accessing these archives
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -27,8 +30,15 @@ public:
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
std::string mount_point;
|
||||
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
|
||||
|
||||
ArchiveFactory_OtherSaveDataPermitted() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& sd_savedata_source;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// File system interface to the OtherSaveDataGeneral archive
|
||||
@ -47,8 +57,18 @@ public:
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
std::string mount_point;
|
||||
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
|
||||
|
||||
ArchiveFactory_OtherSaveDataGeneral() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& sd_savedata_source;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_OtherSaveDataPermitted)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_OtherSaveDataGeneral)
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <utility>
|
||||
#include "common/archives.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/archive_savedata.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@ -10,6 +11,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SaveData)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
ArchiveFactory_SaveData::ArchiveFactory_SaveData(
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -27,8 +29,17 @@ public:
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
|
||||
|
||||
private:
|
||||
std::string mount_point;
|
||||
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
|
||||
|
||||
ArchiveFactory_SaveData() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& sd_savedata_source;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SaveData)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "common/archives.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/archive_sdmc.h"
|
||||
@ -15,6 +16,9 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::SDMCArchive)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SDMC)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class SDMCDelayGenerator : public DelayGenerator {
|
||||
@ -37,6 +41,8 @@ public:
|
||||
static constexpr u64 IPCDelayNanoseconds(269082);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
|
||||
@ -405,3 +411,5 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path
|
||||
return ResultCode(-1);
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::SDMCDelayGenerator)
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@ -42,6 +45,14 @@ public:
|
||||
protected:
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const;
|
||||
std::string mount_point;
|
||||
|
||||
SDMCArchive() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveBackend>(*this);
|
||||
ar& mount_point;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// File system interface to the SDMC archive
|
||||
@ -66,6 +77,20 @@ public:
|
||||
|
||||
private:
|
||||
std::string sdmc_directory;
|
||||
|
||||
ArchiveFactory_SDMC() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& sdmc_directory;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class SDMCDelayGenerator;
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCArchive)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SDMC)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCDelayGenerator)
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "common/archives.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/archive_sdmcwriteonly.h"
|
||||
#include "core/file_sys/directory_backend.h"
|
||||
@ -13,6 +14,9 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::SDMCWriteOnlyArchive)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SDMCWriteOnly)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class SDMCWriteOnlyDelayGenerator : public DelayGenerator {
|
||||
@ -35,6 +39,8 @@ public:
|
||||
static constexpr u64 IPCDelayNanoseconds(269082);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
|
||||
@ -96,3 +102,5 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const P
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::SDMCWriteOnlyDelayGenerator)
|
||||
|
@ -31,6 +31,14 @@ public:
|
||||
const Mode& mode) const override;
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
|
||||
private:
|
||||
SDMCWriteOnlyArchive() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<SDMCArchive>(*this);
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// File system interface to the SDMC write-only archive
|
||||
@ -55,6 +63,20 @@ public:
|
||||
|
||||
private:
|
||||
std::string sdmc_directory;
|
||||
|
||||
ArchiveFactory_SDMCWriteOnly() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& sdmc_directory;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class SDMCWriteOnlyDelayGenerator;
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCWriteOnlyArchive)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SDMCWriteOnly)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCWriteOnlyDelayGenerator)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
@ -16,6 +17,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SelfNCCH)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class SelfNCCHFilePathType : u32 {
|
||||
@ -74,6 +77,15 @@ public:
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<u8>> data;
|
||||
|
||||
ExeFSSectionFile() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<FileBackend>(*this);
|
||||
ar& data;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
// SelfNCCHArchive represents the running application itself. From this archive the application can
|
||||
@ -231,6 +243,15 @@ private:
|
||||
}
|
||||
|
||||
NCCHData ncch_data;
|
||||
|
||||
SelfNCCHArchive() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveBackend>(*this);
|
||||
ar& ncch_data;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
|
||||
@ -297,3 +318,6 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&,
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ExeFSSectionFile)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::SelfNCCHArchive)
|
||||
|
@ -8,6 +8,10 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/unordered_map.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -24,6 +28,17 @@ struct NCCHData {
|
||||
std::shared_ptr<std::vector<u8>> banner;
|
||||
std::shared_ptr<RomFSReader> romfs_file;
|
||||
std::shared_ptr<RomFSReader> update_romfs_file;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& icon;
|
||||
ar& logo;
|
||||
ar& banner;
|
||||
ar& romfs_file;
|
||||
ar& update_romfs_file;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// File system interface to the SelfNCCH archive
|
||||
@ -45,6 +60,20 @@ public:
|
||||
private:
|
||||
/// Mapping of ProgramId -> NCCHData
|
||||
std::unordered_map<u64, NCCHData> ncch_data;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& ncch_data;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class ExeFSSectionFile;
|
||||
class SelfNCCHArchive;
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SelfNCCH)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ExeFSSectionFile)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::SelfNCCHArchive)
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||
@ -13,6 +14,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveSource_SDSaveData)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
namespace {
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@ -27,6 +29,15 @@ public:
|
||||
|
||||
private:
|
||||
std::string mount_point;
|
||||
|
||||
ArchiveSource_SDSaveData() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& mount_point;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveSource_SDSaveData)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/archive_systemsavedata.h"
|
||||
@ -17,6 +18,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SystemSaveData)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) {
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -31,6 +33,14 @@ public:
|
||||
|
||||
private:
|
||||
std::string base_path;
|
||||
|
||||
ArchiveFactory_SystemSaveData() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveFactory>(*this);
|
||||
ar& base_path;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -60,3 +70,5 @@ std::string GetSystemSaveDataContainerPath(const std::string& mount_point);
|
||||
Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low);
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SystemSaveData)
|
||||
|
@ -3,8 +3,11 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/archives.h"
|
||||
#include "core/file_sys/delay_generator.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::DefaultDelayGenerator)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
DelayGenerator::~DelayGenerator() = default;
|
||||
|
@ -5,8 +5,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
#define SERIALIZE_DELAY_GENERATOR \
|
||||
private: \
|
||||
template <class Archive> \
|
||||
void serialize(Archive& ar, const unsigned int) { \
|
||||
ar& boost::serialization::base_object<DelayGenerator>(*this); \
|
||||
} \
|
||||
friend class boost::serialization::access;
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class DelayGenerator {
|
||||
@ -16,12 +26,20 @@ public:
|
||||
virtual u64 GetOpenDelayNs() = 0;
|
||||
|
||||
// TODO (B3N30): Add getter for all other file/directory io operations
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class DefaultDelayGenerator : public DelayGenerator {
|
||||
public:
|
||||
u64 GetReadDelayNs(std::size_t length) override;
|
||||
u64 GetOpenDelayNs() override;
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::DefaultDelayGenerator);
|
||||
|
@ -53,6 +53,11 @@ public:
|
||||
* @return true if the directory closed correctly
|
||||
*/
|
||||
virtual bool Close() const = 0;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -14,6 +15,9 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::DiskFile)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::DiskDirectory)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
ResultVal<std::size_t> DiskFile::Read(const u64 offset, const std::size_t length,
|
||||
|
@ -8,6 +8,9 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/unique_ptr.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
@ -43,6 +46,17 @@ public:
|
||||
protected:
|
||||
Mode mode;
|
||||
std::unique_ptr<FileUtil::IOFile> file;
|
||||
|
||||
private:
|
||||
DiskFile() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<FileBackend>(*this);
|
||||
ar& mode.hex;
|
||||
ar& file;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class DiskDirectory : public DirectoryBackend {
|
||||
@ -65,6 +79,27 @@ protected:
|
||||
// We need to remember the last entry we returned, so a subsequent call to Read will continue
|
||||
// from the next one. This iterator will always point to the next unread entry.
|
||||
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
|
||||
|
||||
private:
|
||||
DiskDirectory() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<DirectoryBackend>(*this);
|
||||
ar& directory;
|
||||
u64 child_index;
|
||||
if (Archive::is_saving::value) {
|
||||
child_index = children_iterator - directory.children.begin();
|
||||
}
|
||||
ar& child_index;
|
||||
if (Archive::is_loading::value) {
|
||||
children_iterator = directory.children.begin() + child_index;
|
||||
}
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::DiskFile)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::DiskDirectory)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <boost/serialization/unique_ptr.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "delay_generator.h"
|
||||
@ -90,6 +91,12 @@ public:
|
||||
|
||||
protected:
|
||||
std::unique_ptr<DelayGenerator> delay_generator;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& delay_generator;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/ivfc_archive.h"
|
||||
@ -12,6 +13,12 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::IVFCFile)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::IVFCFileInMemory)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::IVFCDelayGenerator)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::RomFSDelayGenerator)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::ExeFSDelayGenerator)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
IVFCArchive::IVFCArchive(std::shared_ptr<RomFSReader> file,
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
@ -38,6 +40,8 @@ class IVFCDelayGenerator : public DelayGenerator {
|
||||
static constexpr u64 IPCDelayNanoseconds(9438006);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
class RomFSDelayGenerator : public DelayGenerator {
|
||||
@ -60,6 +64,8 @@ public:
|
||||
static constexpr u64 IPCDelayNanoseconds(9438006);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
class ExeFSDelayGenerator : public DelayGenerator {
|
||||
@ -82,6 +88,8 @@ public:
|
||||
static constexpr u64 IPCDelayNanoseconds(9438006);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
/**
|
||||
@ -128,6 +136,15 @@ public:
|
||||
|
||||
private:
|
||||
std::shared_ptr<RomFSReader> romfs_file;
|
||||
|
||||
IVFCFile() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<FileBackend>(*this);
|
||||
ar& romfs_file;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class IVFCDirectory : public DirectoryBackend {
|
||||
@ -159,6 +176,23 @@ private:
|
||||
std::vector<u8> romfs_file;
|
||||
u64 data_offset;
|
||||
u64 data_size;
|
||||
|
||||
IVFCFileInMemory() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<FileBackend>(*this);
|
||||
ar& romfs_file;
|
||||
ar& data_offset;
|
||||
ar& data_size;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCFile)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCFileInMemory)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCDelayGenerator)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::RomFSDelayGenerator)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ExeFSDelayGenerator)
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/alignment.h"
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
@ -13,6 +14,8 @@
|
||||
#include "core/file_sys/layered_fs.h"
|
||||
#include "core/file_sys/patch.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::LayeredFS)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
struct FileRelocationInfo {
|
||||
@ -51,11 +54,16 @@ struct FileMetadata {
|
||||
};
|
||||
static_assert(sizeof(FileMetadata) == 0x20, "Size of FileMetadata is not correct");
|
||||
|
||||
LayeredFS::LayeredFS(std::shared_ptr<RomFSReader> romfs_, std::string patch_path_,
|
||||
std::string patch_ext_path_, bool load_relocations)
|
||||
: romfs(std::move(romfs_)), patch_path(std::move(patch_path_)),
|
||||
patch_ext_path(std::move(patch_ext_path_)) {
|
||||
LayeredFS::LayeredFS() = default;
|
||||
|
||||
LayeredFS::LayeredFS(std::shared_ptr<RomFSReader> romfs_, std::string patch_path_,
|
||||
std::string patch_ext_path_, bool load_relocations_)
|
||||
: romfs(std::move(romfs_)), patch_path(std::move(patch_path_)),
|
||||
patch_ext_path(std::move(patch_ext_path_)), load_relocations(load_relocations_) {
|
||||
Load();
|
||||
}
|
||||
|
||||
void LayeredFS::Load() {
|
||||
romfs->ReadFile(0, sizeof(header), reinterpret_cast<u8*>(&header));
|
||||
|
||||
ASSERT_MSG(header.header_length == sizeof(header), "Header size is incorrect");
|
||||
@ -273,7 +281,7 @@ std::size_t GetNameSize(const std::string& name) {
|
||||
}
|
||||
|
||||
void LayeredFS::PrepareBuildDirectory(Directory& current) {
|
||||
directory_metadata_offset_map.emplace(¤t, current_directory_offset);
|
||||
directory_metadata_offset_map.emplace(¤t, static_cast<u32>(current_directory_offset));
|
||||
directory_list.emplace_back(¤t);
|
||||
current_directory_offset += sizeof(DirectoryMetadata) + GetNameSize(current.name);
|
||||
}
|
||||
@ -282,7 +290,7 @@ void LayeredFS::PrepareBuildFile(File& current) {
|
||||
if (current.relocation.type == 3) { // Deleted files are not counted
|
||||
return;
|
||||
}
|
||||
file_metadata_offset_map.emplace(¤t, current_file_offset);
|
||||
file_metadata_offset_map.emplace(¤t, static_cast<u32>(current_file_offset));
|
||||
file_list.emplace_back(¤t);
|
||||
current_file_offset += sizeof(FileMetadata) + GetNameSize(current.name);
|
||||
}
|
||||
@ -361,7 +369,7 @@ void LayeredFS::BuildDirectories() {
|
||||
|
||||
// Write metadata and name
|
||||
std::u16string u16name = Common::UTF8ToUTF16(directory->name);
|
||||
metadata.name_length = u16name.size() * 2;
|
||||
metadata.name_length = static_cast<u32_le>(u16name.size() * 2);
|
||||
|
||||
std::memcpy(directory_metadata_table.data() + written, &metadata, sizeof(metadata));
|
||||
written += sizeof(metadata);
|
||||
@ -410,7 +418,7 @@ void LayeredFS::BuildFiles() {
|
||||
|
||||
// Write metadata and name
|
||||
std::u16string u16name = Common::UTF8ToUTF16(file->name);
|
||||
metadata.name_length = u16name.size() * 2;
|
||||
metadata.name_length = static_cast<u32_le>(u16name.size() * 2);
|
||||
|
||||
std::memcpy(file_metadata_table.data() + written, &metadata, sizeof(metadata));
|
||||
written += sizeof(metadata);
|
||||
|
@ -9,6 +9,10 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/romfs_reader.h"
|
||||
@ -92,9 +96,12 @@ private:
|
||||
|
||||
void RebuildMetadata();
|
||||
|
||||
void Load();
|
||||
|
||||
std::shared_ptr<RomFSReader> romfs;
|
||||
std::string patch_path;
|
||||
std::string patch_ext_path;
|
||||
bool load_relocations;
|
||||
|
||||
RomFSHeader header;
|
||||
Directory root;
|
||||
@ -118,6 +125,24 @@ private:
|
||||
u64 current_file_offset{}; // current file metadata offset
|
||||
std::vector<u8> file_metadata_table; // rebuilt file metadata table
|
||||
u64 current_data_offset{}; // current assigned data offset
|
||||
|
||||
LayeredFS();
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<RomFSReader>(*this);
|
||||
ar& romfs;
|
||||
ar& patch_path;
|
||||
ar& patch_ext_path;
|
||||
ar& load_relocations;
|
||||
if (Archive::is_loading::value) {
|
||||
Load();
|
||||
}
|
||||
// NOTE: Everything else is essentially cached, updated when we call Load
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::LayeredFS)
|
||||
|
@ -1,15 +1,18 @@
|
||||
#include <algorithm>
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/modes.h>
|
||||
#include "common/archives.h"
|
||||
#include "core/file_sys/romfs_reader.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::DirectRomFSReader)
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::size_t DirectRomFSReader::ReadFile(std::size_t offset, std::size_t length, u8* buffer) {
|
||||
if (length == 0)
|
||||
return 0; // Crypto++ does not like zero size buffer
|
||||
file.Seek(file_offset + offset, SEEK_SET);
|
||||
std::size_t read_length = std::min(length, data_size - offset);
|
||||
std::size_t read_length = std::min(length, static_cast<std::size_t>(data_size) - offset);
|
||||
read_length = file.ReadBytes(buffer, read_length);
|
||||
if (is_encrypted) {
|
||||
CryptoPP::CTR_Mode<CryptoPP::AES>::Decryption d(key.data(), key.size(), ctr.data());
|
||||
|
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
|
||||
@ -15,6 +18,11 @@ public:
|
||||
|
||||
virtual std::size_t GetSize() const = 0;
|
||||
virtual std::size_t ReadFile(std::size_t offset, std::size_t length, u8* buffer) = 0;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -45,9 +53,26 @@ private:
|
||||
FileUtil::IOFile file;
|
||||
std::array<u8, 16> key;
|
||||
std::array<u8, 16> ctr;
|
||||
std::size_t file_offset;
|
||||
std::size_t crypto_offset;
|
||||
std::size_t data_size;
|
||||
u64 file_offset;
|
||||
u64 crypto_offset;
|
||||
u64 data_size;
|
||||
|
||||
DirectRomFSReader() = default;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<RomFSReader>(*this);
|
||||
ar& is_encrypted;
|
||||
ar& file;
|
||||
ar& key;
|
||||
ar& ctr;
|
||||
ar& file_offset;
|
||||
ar& crypto_offset;
|
||||
ar& data_size;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::DirectRomFSReader)
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/disk_archive.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
@ -33,6 +34,8 @@ public:
|
||||
static constexpr u64 IPCDelayNanoseconds(269082);
|
||||
return IPCDelayNanoseconds;
|
||||
}
|
||||
|
||||
SERIALIZE_DELAY_GENERATOR
|
||||
};
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
|
||||
@ -353,3 +356,6 @@ u64 SaveDataArchive::GetFreeBytes() const {
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::SaveDataArchive)
|
||||
SERIALIZE_EXPORT_IMPL(FileSys::SaveDataDelayGenerator)
|
||||
|
@ -38,6 +38,22 @@ public:
|
||||
|
||||
protected:
|
||||
std::string mount_point;
|
||||
SaveDataArchive() = default;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<ArchiveBackend>(*this);
|
||||
ar& mount_point;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class SaveDataDelayGenerator;
|
||||
class ExtSaveDataArchive;
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::SaveDataArchive)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::SaveDataDelayGenerator)
|
||||
BOOST_CLASS_EXPORT_KEY(FileSys::ExtSaveDataArchive)
|
||||
|
21
src/core/global.h
Normal file
21
src/core/global.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
|
||||
template <class T>
|
||||
T& Global();
|
||||
|
||||
// Declare explicit specialisation to prevent automatic instantiation
|
||||
class System;
|
||||
template <>
|
||||
System& Global();
|
||||
|
||||
class Timing;
|
||||
template <>
|
||||
Timing& Global();
|
||||
|
||||
} // namespace Core
|
@ -3,8 +3,10 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/global.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@ -14,6 +16,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Kernel namespace
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::AddressArbiter)
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void AddressArbiter::WaitThread(std::shared_ptr<Thread> thread, VAddr wait_address) {
|
||||
@ -76,16 +80,18 @@ std::shared_ptr<AddressArbiter> KernelSystem::CreateAddressArbiter(std::string n
|
||||
return address_arbiter;
|
||||
}
|
||||
|
||||
void AddressArbiter::WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object) {
|
||||
ASSERT(reason == ThreadWakeupReason::Timeout);
|
||||
// Remove the newly-awakened thread from the Arbiter's waiting list.
|
||||
waiting_threads.erase(std::remove(waiting_threads.begin(), waiting_threads.end(), thread),
|
||||
waiting_threads.end());
|
||||
};
|
||||
|
||||
ResultCode AddressArbiter::ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type,
|
||||
VAddr address, s32 value, u64 nanoseconds) {
|
||||
|
||||
auto timeout_callback = [this](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object) {
|
||||
ASSERT(reason == ThreadWakeupReason::Timeout);
|
||||
// Remove the newly-awakened thread from the Arbiter's waiting list.
|
||||
waiting_threads.erase(std::remove(waiting_threads.begin(), waiting_threads.end(), thread),
|
||||
waiting_threads.end());
|
||||
};
|
||||
auto timeout_callback = std::dynamic_pointer_cast<WakeupCallback>(shared_from_this());
|
||||
|
||||
switch (type) {
|
||||
|
||||
|
@ -6,8 +6,15 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
// Address arbiters are an underlying kernel synchronization object that can be created/used via
|
||||
@ -30,7 +37,7 @@ enum class ArbitrationType : u32 {
|
||||
DecrementAndWaitIfLessThanWithTimeout,
|
||||
};
|
||||
|
||||
class AddressArbiter final : public Object {
|
||||
class AddressArbiter final : public Object, public WakeupCallback {
|
||||
public:
|
||||
explicit AddressArbiter(KernelSystem& kernel);
|
||||
~AddressArbiter() override;
|
||||
@ -52,6 +59,9 @@ public:
|
||||
ResultCode ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type, VAddr address,
|
||||
s32 value, u64 nanoseconds);
|
||||
|
||||
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object);
|
||||
|
||||
private:
|
||||
KernelSystem& kernel;
|
||||
|
||||
@ -67,6 +77,21 @@ private:
|
||||
|
||||
/// Threads waiting for the address arbiter to be signaled.
|
||||
std::vector<std::shared_ptr<Thread>> waiting_threads;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& boost::serialization::base_object<Object>(*this);
|
||||
if (file_version > 0) {
|
||||
ar& boost::serialization::base_object<WakeupCallback>(*this);
|
||||
}
|
||||
ar& name;
|
||||
ar& waiting_threads;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::AddressArbiter)
|
||||
BOOST_CLASS_VERSION(Kernel::AddressArbiter, 1)
|
||||
CONSTRUCT_KERNEL_OBJECT(Kernel::AddressArbiter)
|
||||
|
@ -2,7 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/global.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
@ -11,6 +13,8 @@
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::ClientPort)
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientPort::ClientPort(KernelSystem& kernel) : Object(kernel), kernel(kernel) {}
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
@ -59,6 +62,20 @@ private:
|
||||
std::string name; ///< Name of client port (optional)
|
||||
|
||||
friend class KernelSystem;
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& boost::serialization::base_object<Object>(*this);
|
||||
ar& server_port;
|
||||
ar& max_sessions;
|
||||
ar& active_sessions;
|
||||
ar& name;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::ClientPort)
|
||||
CONSTRUCT_KERNEL_OBJECT(Kernel::ClientPort)
|
||||
|
@ -2,8 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
@ -11,6 +11,8 @@
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::ClientSession)
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientSession::ClientSession(KernelSystem& kernel) : Object(kernel) {}
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -46,6 +50,18 @@ public:
|
||||
|
||||
/// The parent session, which links to the server endpoint.
|
||||
std::shared_ptr<Session> parent;
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& boost::serialization::base_object<Object>(*this);
|
||||
ar& name;
|
||||
ar& parent;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::ClientSession)
|
||||
CONSTRUCT_KERNEL_OBJECT(Kernel::ClientSession)
|
||||
|
@ -3,10 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/archives.h"
|
||||
#include "core/hle/kernel/config_mem.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(ConfigMem::Handler)
|
||||
|
||||
namespace ConfigMem {
|
||||
|
||||
Handler::Handler() {
|
||||
|
@ -9,8 +9,11 @@
|
||||
// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
|
||||
// putting this as a subset of HLE for now.
|
||||
|
||||
#include <boost/serialization/binary_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_ref.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@ -49,13 +52,34 @@ struct ConfigMemDef {
|
||||
static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE,
|
||||
"Config Memory structure size is wrong");
|
||||
|
||||
class Handler {
|
||||
class Handler : public BackingMem {
|
||||
public:
|
||||
Handler();
|
||||
ConfigMemDef& GetConfigMem();
|
||||
|
||||
u8* GetPtr() override {
|
||||
return reinterpret_cast<u8*>(&config_mem);
|
||||
}
|
||||
|
||||
const u8* GetPtr() const override {
|
||||
return reinterpret_cast<const u8*>(&config_mem);
|
||||
}
|
||||
|
||||
std::size_t GetSize() const override {
|
||||
return sizeof(config_mem);
|
||||
}
|
||||
|
||||
private:
|
||||
ConfigMemDef config_mem;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& boost::serialization::base_object<BackingMem>(*this);
|
||||
ar& boost::serialization::make_binary_object(&config_mem, sizeof(config_mem));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ConfigMem
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(ConfigMem::Handler)
|
||||
|
@ -5,11 +5,14 @@
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::Event)
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
Event::Event(KernelSystem& kernel) : WaitObject(kernel) {}
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
@ -49,6 +52,18 @@ private:
|
||||
std::string name; ///< Name of event (optional)
|
||||
|
||||
friend class KernelSystem;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& boost::serialization::base_object<WaitObject>(*this);
|
||||
ar& reset_type;
|
||||
ar& signaled;
|
||||
ar& name;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::Event)
|
||||
CONSTRUCT_KERNEL_OBJECT(Kernel::Event)
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -116,6 +118,15 @@ private:
|
||||
u16 next_free_slot;
|
||||
|
||||
KernelSystem& kernel;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& objects;
|
||||
ar& generations;
|
||||
ar& next_generation;
|
||||
ar& next_free_slot;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -16,6 +16,47 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class HLERequestContext::ThreadCallback : public Kernel::WakeupCallback {
|
||||
|
||||
public:
|
||||
ThreadCallback(std::shared_ptr<HLERequestContext> context_,
|
||||
std::shared_ptr<HLERequestContext::WakeupCallback> callback_)
|
||||
: context(std::move(context_)), callback(std::move(callback_)) {}
|
||||
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object) {
|
||||
ASSERT(thread->status == ThreadStatus::WaitHleEvent);
|
||||
if (callback) {
|
||||
callback->WakeUp(thread, *context, reason);
|
||||
}
|
||||
|
||||
auto& process = thread->owner_process;
|
||||
// We must copy the entire command buffer *plus* the entire static buffers area, since
|
||||
// the translation might need to read from it in order to retrieve the StaticBuffer
|
||||
// target addresses.
|
||||
std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff;
|
||||
Memory::MemorySystem& memory = context->kernel.memory;
|
||||
memory.ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
|
||||
cmd_buff.size() * sizeof(u32));
|
||||
context->WriteToOutgoingCommandBuffer(cmd_buff.data(), *process);
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
memory.WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
|
||||
cmd_buff.size() * sizeof(u32));
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadCallback() = default;
|
||||
std::shared_ptr<HLERequestContext::WakeupCallback> callback{};
|
||||
std::shared_ptr<HLERequestContext> context{};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<Kernel::WakeupCallback>(*this);
|
||||
ar& callback;
|
||||
ar& context;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
SessionRequestHandler::SessionInfo::SessionInfo(std::shared_ptr<ServerSession> session,
|
||||
std::unique_ptr<SessionDataBase> data)
|
||||
: session(std::move(session)), data(std::move(data)) {}
|
||||
@ -33,34 +74,16 @@ void SessionRequestHandler::ClientDisconnected(std::shared_ptr<ServerSession> se
|
||||
connected_sessions.end());
|
||||
}
|
||||
|
||||
std::shared_ptr<Event> HLERequestContext::SleepClientThread(const std::string& reason,
|
||||
std::chrono::nanoseconds timeout,
|
||||
WakeupCallback&& callback) {
|
||||
std::shared_ptr<Event> HLERequestContext::SleepClientThread(
|
||||
const std::string& reason, std::chrono::nanoseconds timeout,
|
||||
std::shared_ptr<WakeupCallback> callback) {
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->wakeup_callback = [context = *this,
|
||||
callback](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object) mutable {
|
||||
ASSERT(thread->status == ThreadStatus::WaitHleEvent);
|
||||
callback(thread, context, reason);
|
||||
|
||||
auto& process = thread->owner_process;
|
||||
// We must copy the entire command buffer *plus* the entire static buffers area, since
|
||||
// the translation might need to read from it in order to retrieve the StaticBuffer
|
||||
// target addresses.
|
||||
std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff;
|
||||
Memory::MemorySystem& memory = context.kernel.memory;
|
||||
memory.ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
|
||||
cmd_buff.size() * sizeof(u32));
|
||||
context.WriteToOutgoingCommandBuffer(cmd_buff.data(), *process);
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
memory.WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
|
||||
cmd_buff.size() * sizeof(u32));
|
||||
};
|
||||
thread->wakeup_callback = std::make_shared<ThreadCallback>(shared_from_this(), callback);
|
||||
|
||||
auto event = kernel.CreateEvent(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
thread->status = ThreadStatus::WaitHleEvent;
|
||||
thread->wait_objects = {event};
|
||||
event->AddWaitingThread(SharedFrom(thread));
|
||||
event->AddWaitingThread(thread);
|
||||
|
||||
if (timeout.count() > 0)
|
||||
thread->WakeAfterDelay(timeout.count());
|
||||
@ -68,8 +91,10 @@ std::shared_ptr<Event> HLERequestContext::SleepClientThread(const std::string& r
|
||||
return event;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext() : kernel(Core::Global<KernelSystem>()) {}
|
||||
|
||||
HLERequestContext::HLERequestContext(KernelSystem& kernel, std::shared_ptr<ServerSession> session,
|
||||
Thread* thread)
|
||||
std::shared_ptr<Thread> thread)
|
||||
: kernel(kernel), session(std::move(session)), thread(thread) {
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
@ -98,8 +123,9 @@ void HLERequestContext::AddStaticBuffer(u8 buffer_id, std::vector<u8> data) {
|
||||
static_buffers[buffer_id] = std::move(data);
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
|
||||
Process& src_process) {
|
||||
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(
|
||||
const u32_le* src_cmdbuf, std::shared_ptr<Process> src_process_) {
|
||||
auto& src_process = *src_process_;
|
||||
IPC::Header header{src_cmdbuf[0]};
|
||||
|
||||
std::size_t untranslated_size = 1u + header.normal_params_size;
|
||||
@ -158,7 +184,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr
|
||||
}
|
||||
case IPC::DescriptorType::MappedBuffer: {
|
||||
u32 next_id = static_cast<u32>(request_mapped_buffers.size());
|
||||
request_mapped_buffers.emplace_back(kernel.memory, src_process, descriptor,
|
||||
request_mapped_buffers.emplace_back(kernel.memory, src_process_, descriptor,
|
||||
src_cmdbuf[i], next_id);
|
||||
cmd_buf[i++] = next_id;
|
||||
break;
|
||||
@ -170,7 +196,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr
|
||||
|
||||
if (should_record) {
|
||||
std::vector<u32> translated_cmdbuf{cmd_buf.begin(), cmd_buf.begin() + command_size};
|
||||
kernel.GetIPCRecorder().SetRequestInfo(SharedFrom(thread), std::move(untranslated_cmdbuf),
|
||||
kernel.GetIPCRecorder().SetRequestInfo(thread, std::move(untranslated_cmdbuf),
|
||||
std::move(translated_cmdbuf));
|
||||
}
|
||||
|
||||
@ -248,7 +274,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf,
|
||||
|
||||
if (should_record) {
|
||||
std::vector<u32> translated_cmdbuf{dst_cmdbuf, dst_cmdbuf + command_size};
|
||||
kernel.GetIPCRecorder().SetReplyInfo(SharedFrom(thread), std::move(untranslated_cmdbuf),
|
||||
kernel.GetIPCRecorder().SetReplyInfo(thread, std::move(untranslated_cmdbuf),
|
||||
std::move(translated_cmdbuf));
|
||||
}
|
||||
|
||||
@ -262,13 +288,15 @@ MappedBuffer& HLERequestContext::GetMappedBuffer(u32 id_from_cmdbuf) {
|
||||
|
||||
void HLERequestContext::ReportUnimplemented() const {
|
||||
if (kernel.GetIPCRecorder().IsEnabled()) {
|
||||
kernel.GetIPCRecorder().SetHLEUnimplemented(SharedFrom(thread));
|
||||
kernel.GetIPCRecorder().SetHLEUnimplemented(thread);
|
||||
}
|
||||
}
|
||||
|
||||
MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, const Process& process, u32 descriptor,
|
||||
VAddr address, u32 id)
|
||||
: memory(&memory), id(id), address(address), process(&process) {
|
||||
MappedBuffer::MappedBuffer() : memory(&Core::Global<Core::System>().Memory()) {}
|
||||
|
||||
MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process> process,
|
||||
u32 descriptor, VAddr address, u32 id)
|
||||
: memory(&memory), id(id), address(address), process(std::move(process)) {
|
||||
IPC::MappedBufferDescInfo desc{descriptor};
|
||||
size = desc.size;
|
||||
perms = desc.perms;
|
||||
@ -287,3 +315,5 @@ void MappedBuffer::Write(const void* src_buffer, std::size_t offset, std::size_t
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::HLERequestContext::ThreadCallback)
|
||||
|
@ -11,7 +11,12 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/serialization/assume_abstract.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/unique_ptr.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/serialization/boost_small_vector.hpp"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
@ -68,6 +73,11 @@ public:
|
||||
/// in each service must inherit from this.
|
||||
struct SessionDataBase {
|
||||
virtual ~SessionDataBase() = default;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
protected:
|
||||
@ -90,15 +100,33 @@ protected:
|
||||
|
||||
std::shared_ptr<ServerSession> session;
|
||||
std::unique_ptr<SessionDataBase> data;
|
||||
|
||||
private:
|
||||
SessionInfo() = default;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& session;
|
||||
ar& data;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
/// List of sessions that are connected to this handler. A ServerSession whose server endpoint
|
||||
/// is an HLE implementation is kept alive by this list for the duration of the connection.
|
||||
std::vector<SessionInfo> connected_sessions;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& connected_sessions;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
// NOTE: The below classes are ephemeral and don't need serialization
|
||||
|
||||
class MappedBuffer {
|
||||
public:
|
||||
MappedBuffer(Memory::MemorySystem& memory, const Process& process, u32 descriptor,
|
||||
MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process> process, u32 descriptor,
|
||||
VAddr address, u32 id);
|
||||
|
||||
// interface for service
|
||||
@ -122,9 +150,21 @@ private:
|
||||
Memory::MemorySystem* memory;
|
||||
u32 id;
|
||||
VAddr address;
|
||||
const Process* process;
|
||||
std::size_t size;
|
||||
std::shared_ptr<Process> process;
|
||||
u32 size;
|
||||
IPC::MappedBufferPermissions perms;
|
||||
|
||||
MappedBuffer();
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& id;
|
||||
ar& address;
|
||||
ar& process;
|
||||
ar& size;
|
||||
ar& perms;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -156,9 +196,10 @@ private:
|
||||
* id of the memory interface and let kernel convert it back to client vaddr. No real unmapping is
|
||||
* needed in this case, though.
|
||||
*/
|
||||
class HLERequestContext {
|
||||
class HLERequestContext : public std::enable_shared_from_this<HLERequestContext> {
|
||||
public:
|
||||
HLERequestContext(KernelSystem& kernel, std::shared_ptr<ServerSession> session, Thread* thread);
|
||||
HLERequestContext(KernelSystem& kernel, std::shared_ptr<ServerSession> session,
|
||||
std::shared_ptr<Thread> thread);
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
@ -174,8 +215,17 @@ public:
|
||||
return session;
|
||||
}
|
||||
|
||||
using WakeupCallback = std::function<void(
|
||||
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
|
||||
class WakeupCallback {
|
||||
public:
|
||||
virtual ~WakeupCallback() = default;
|
||||
virtual void WakeUp(std::shared_ptr<Thread> thread, HLERequestContext& context,
|
||||
ThreadWakeupReason reason) = 0;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/**
|
||||
* Puts the specified guest thread to sleep until the returned event is signaled or until the
|
||||
@ -190,7 +240,7 @@ public:
|
||||
*/
|
||||
std::shared_ptr<Event> SleepClientThread(const std::string& reason,
|
||||
std::chrono::nanoseconds timeout,
|
||||
WakeupCallback&& callback);
|
||||
std::shared_ptr<WakeupCallback> callback);
|
||||
|
||||
/**
|
||||
* Resolves a object id from the request command buffer into a pointer to an object. See the
|
||||
@ -230,24 +280,42 @@ public:
|
||||
MappedBuffer& GetMappedBuffer(u32 id_from_cmdbuf);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process);
|
||||
ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
|
||||
std::shared_ptr<Process> src_process);
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process) const;
|
||||
|
||||
/// Reports an unimplemented function.
|
||||
void ReportUnimplemented() const;
|
||||
|
||||
class ThreadCallback;
|
||||
friend class ThreadCallback;
|
||||
|
||||
private:
|
||||
KernelSystem& kernel;
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
std::shared_ptr<ServerSession> session;
|
||||
Thread* thread;
|
||||
std::shared_ptr<Thread> thread;
|
||||
// TODO(yuriks): Check common usage of this and optimize size accordingly
|
||||
boost::container::small_vector<std::shared_ptr<Object>, 8> request_handles;
|
||||
// The static buffers will be created when the IPC request is translated.
|
||||
std::array<std::vector<u8>, IPC::MAX_STATIC_BUFFERS> static_buffers;
|
||||
// The mapped buffers will be created when the IPC request is translated
|
||||
boost::container::small_vector<MappedBuffer, 8> request_mapped_buffers;
|
||||
|
||||
HLERequestContext();
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& cmd_buf;
|
||||
ar& session;
|
||||
ar& thread;
|
||||
ar& request_handles;
|
||||
ar& static_buffers;
|
||||
ar& request_mapped_buffers;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::HLERequestContext::ThreadCallback)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/alignment.h"
|
||||
#include "common/memory_ref.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@ -71,7 +72,7 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
if (handle == CurrentThread) {
|
||||
object = src_thread;
|
||||
} else if (handle == CurrentProcess) {
|
||||
object = SharedFrom(src_process);
|
||||
object = src_process;
|
||||
} else if (handle != 0) {
|
||||
object = src_process->handle_table.GetGeneric(handle);
|
||||
if (descriptor == IPC::DescriptorType::MoveHandle) {
|
||||
@ -193,28 +194,29 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
|
||||
// TODO(Subv): Perform permission checks.
|
||||
|
||||
// Reserve a page of memory before the mapped buffer
|
||||
auto reserve_buffer = std::make_unique<u8[]>(Memory::PAGE_SIZE);
|
||||
std::shared_ptr<BackingMem> reserve_buffer =
|
||||
std::make_shared<BufferMem>(Memory::PAGE_SIZE);
|
||||
dst_process->vm_manager.MapBackingMemoryToBase(
|
||||
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer.get(),
|
||||
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer,
|
||||
Memory::PAGE_SIZE, Kernel::MemoryState::Reserved);
|
||||
|
||||
auto buffer = std::make_unique<u8[]>(num_pages * Memory::PAGE_SIZE);
|
||||
memory.ReadBlock(*src_process, source_address, buffer.get() + page_offset, size);
|
||||
std::shared_ptr<BackingMem> buffer =
|
||||
std::make_shared<BufferMem>(num_pages * Memory::PAGE_SIZE);
|
||||
memory.ReadBlock(*src_process, source_address, buffer->GetPtr() + page_offset, size);
|
||||
|
||||
// Map the page(s) into the target process' address space.
|
||||
target_address =
|
||||
dst_process->vm_manager
|
||||
.MapBackingMemoryToBase(Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE,
|
||||
buffer.get(), num_pages * Memory::PAGE_SIZE,
|
||||
Kernel::MemoryState::Shared)
|
||||
buffer, buffer->GetSize(), Kernel::MemoryState::Shared)
|
||||
.Unwrap();
|
||||
|
||||
cmd_buf[i++] = target_address + page_offset;
|
||||
|
||||
// Reserve a page of memory after the mapped buffer
|
||||
dst_process->vm_manager.MapBackingMemoryToBase(
|
||||
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer.get(),
|
||||
Memory::PAGE_SIZE, Kernel::MemoryState::Reserved);
|
||||
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer,
|
||||
reserve_buffer->GetSize(), Kernel::MemoryState::Reserved);
|
||||
|
||||
mapped_buffer_context.push_back({permissions, size, source_address,
|
||||
target_address + page_offset, std::move(buffer),
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
@ -24,8 +25,20 @@ struct MappedBufferContext {
|
||||
VAddr source_address;
|
||||
VAddr target_address;
|
||||
|
||||
std::unique_ptr<u8[]> buffer;
|
||||
std::unique_ptr<u8[]> reserve_buffer;
|
||||
std::shared_ptr<BackingMem> buffer;
|
||||
std::shared_ptr<BackingMem> reserve_buffer;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& permissions;
|
||||
ar& size;
|
||||
ar& source_address;
|
||||
ar& target_address;
|
||||
ar& buffer;
|
||||
ar& reserve_buffer;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
/// Performs IPC command buffer translation from one process to another.
|
||||
|
@ -52,7 +52,7 @@ void Recorder::RegisterRequest(const std::shared_ptr<Kernel::ClientSession>& cli
|
||||
|
||||
RequestRecord record = {/* id */ ++record_count,
|
||||
/* status */ RequestStatus::Sent,
|
||||
/* client_process */ GetObjectInfo(client_thread->owner_process),
|
||||
/* client_process */ GetObjectInfo(client_thread->owner_process.get()),
|
||||
/* client_thread */ GetObjectInfo(client_thread.get()),
|
||||
/* client_session */ GetObjectInfo(client_session.get()),
|
||||
/* client_port */ GetObjectInfo(client_session->parent->port.get()),
|
||||
@ -82,7 +82,7 @@ void Recorder::SetRequestInfo(const std::shared_ptr<Kernel::Thread>& client_thre
|
||||
record.translated_request_cmdbuf = std::move(translated_cmdbuf);
|
||||
|
||||
if (server_thread) {
|
||||
record.server_process = GetObjectInfo(server_thread->owner_process);
|
||||
record.server_process = GetObjectInfo(server_thread->owner_process.get());
|
||||
record.server_thread = GetObjectInfo(server_thread.get());
|
||||
} else {
|
||||
record.is_hle = true;
|
||||
|
@ -2,6 +2,11 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/unordered_map.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/archives.h"
|
||||
#include "common/serialization/atomic.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/config_mem.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@ -22,6 +27,8 @@ KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
||||
u32 num_cores, u8 n3ds_mode)
|
||||
: memory(memory), timing(timing),
|
||||
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
|
||||
std::generate(memory_regions.begin(), memory_regions.end(),
|
||||
[] { return std::make_shared<MemoryRegionInfo>(); });
|
||||
MemoryInit(system_mode, n3ds_mode);
|
||||
|
||||
resource_limits = std::make_unique<ResourceLimitList>(*this);
|
||||
@ -58,24 +65,23 @@ std::shared_ptr<Process> KernelSystem::GetCurrentProcess() const {
|
||||
|
||||
void KernelSystem::SetCurrentProcess(std::shared_ptr<Process> process) {
|
||||
current_process = process;
|
||||
SetCurrentMemoryPageTable(&process->vm_manager.page_table);
|
||||
SetCurrentMemoryPageTable(process->vm_manager.page_table);
|
||||
}
|
||||
|
||||
void KernelSystem::SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id) {
|
||||
if (current_cpu->GetID() == core_id) {
|
||||
current_process = process;
|
||||
SetCurrentMemoryPageTable(&process->vm_manager.page_table);
|
||||
SetCurrentMemoryPageTable(process->vm_manager.page_table);
|
||||
} else {
|
||||
stored_processes[core_id] = process;
|
||||
thread_managers[core_id]->cpu->PageTableChanged(&process->vm_manager.page_table);
|
||||
thread_managers[core_id]->cpu->SetPageTable(process->vm_manager.page_table);
|
||||
}
|
||||
}
|
||||
|
||||
void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
|
||||
void KernelSystem::SetCurrentMemoryPageTable(std::shared_ptr<Memory::PageTable> page_table) {
|
||||
memory.SetCurrentPageTable(page_table);
|
||||
if (current_cpu != nullptr) {
|
||||
// Notify the CPU the page table in memory has changed
|
||||
current_cpu->PageTableChanged(page_table);
|
||||
current_cpu->SetPageTable(page_table);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,4 +156,29 @@ void KernelSystem::ResetThreadIDs() {
|
||||
next_thread_id = 0;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void KernelSystem::serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& memory_regions;
|
||||
ar& named_ports;
|
||||
// current_cpu set externally
|
||||
// NB: subsystem references and prepare_reschedule_callback are constant
|
||||
ar&* resource_limits.get();
|
||||
ar& next_object_id;
|
||||
ar&* timer_manager.get();
|
||||
ar& next_process_id;
|
||||
ar& process_list;
|
||||
ar& current_process;
|
||||
// NB: core count checked in 'core'
|
||||
for (auto& thread_manager : thread_managers) {
|
||||
ar&* thread_manager.get();
|
||||
}
|
||||
ar& config_mem_handler;
|
||||
ar& shared_page_handler;
|
||||
ar& stored_processes;
|
||||
ar& next_thread_id;
|
||||
// Deliberately don't include debugger info to allow debugging through loads
|
||||
}
|
||||
|
||||
SERIALIZE_IMPL(KernelSystem)
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -132,7 +132,8 @@ public:
|
||||
*/
|
||||
ResultVal<std::shared_ptr<Thread>> CreateThread(std::string name, VAddr entry_point,
|
||||
u32 priority, u32 arg, s32 processor_id,
|
||||
VAddr stack_top, Process& owner_process);
|
||||
VAddr stack_top,
|
||||
std::shared_ptr<Process> owner_process);
|
||||
|
||||
/**
|
||||
* Creates a semaphore.
|
||||
@ -213,7 +214,7 @@ public:
|
||||
void SetCurrentProcess(std::shared_ptr<Process> process);
|
||||
void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id);
|
||||
|
||||
void SetCurrentMemoryPageTable(Memory::PageTable* page_table);
|
||||
void SetCurrentMemoryPageTable(std::shared_ptr<Memory::PageTable> page_table);
|
||||
|
||||
void SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpu);
|
||||
|
||||
@ -236,11 +237,11 @@ public:
|
||||
IPCDebugger::Recorder& GetIPCRecorder();
|
||||
const IPCDebugger::Recorder& GetIPCRecorder() const;
|
||||
|
||||
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
|
||||
std::shared_ptr<MemoryRegionInfo> GetMemoryRegion(MemoryRegion region);
|
||||
|
||||
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
|
||||
|
||||
std::array<MemoryRegionInfo, 3> memory_regions;
|
||||
std::array<std::shared_ptr<MemoryRegionInfo>, 3> memory_regions{};
|
||||
|
||||
/// Adds a port to the named port table
|
||||
void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);
|
||||
@ -291,12 +292,16 @@ private:
|
||||
|
||||
std::vector<std::unique_ptr<ThreadManager>> thread_managers;
|
||||
|
||||
std::unique_ptr<ConfigMem::Handler> config_mem_handler;
|
||||
std::unique_ptr<SharedPage::Handler> shared_page_handler;
|
||||
std::shared_ptr<ConfigMem::Handler> config_mem_handler;
|
||||
std::shared_ptr<SharedPage::Handler> shared_page_handler;
|
||||
|
||||
std::unique_ptr<IPCDebugger::Recorder> ipc_recorder;
|
||||
|
||||
u32 next_thread_id;
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version);
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -71,32 +71,32 @@ void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) {
|
||||
// the sizes specified in the memory_region_sizes table.
|
||||
VAddr base = 0;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
memory_regions[i].Reset(base, memory_region_sizes[mem_type][i]);
|
||||
memory_regions[i]->Reset(base, memory_region_sizes[mem_type][i]);
|
||||
|
||||
base += memory_regions[i].size;
|
||||
base += memory_regions[i]->size;
|
||||
}
|
||||
|
||||
// We must've allocated the entire FCRAM by the end
|
||||
ASSERT(base == (is_new_3ds ? Memory::FCRAM_N3DS_SIZE : Memory::FCRAM_SIZE));
|
||||
|
||||
config_mem_handler = std::make_unique<ConfigMem::Handler>();
|
||||
config_mem_handler = std::make_shared<ConfigMem::Handler>();
|
||||
auto& config_mem = config_mem_handler->GetConfigMem();
|
||||
config_mem.app_mem_type = reported_mem_type;
|
||||
config_mem.app_mem_alloc = memory_region_sizes[reported_mem_type][0];
|
||||
config_mem.sys_mem_alloc = memory_regions[1].size;
|
||||
config_mem.base_mem_alloc = memory_regions[2].size;
|
||||
config_mem.sys_mem_alloc = memory_regions[1]->size;
|
||||
config_mem.base_mem_alloc = memory_regions[2]->size;
|
||||
|
||||
shared_page_handler = std::make_unique<SharedPage::Handler>(timing);
|
||||
shared_page_handler = std::make_shared<SharedPage::Handler>(timing);
|
||||
}
|
||||
|
||||
MemoryRegionInfo* KernelSystem::GetMemoryRegion(MemoryRegion region) {
|
||||
std::shared_ptr<MemoryRegionInfo> KernelSystem::GetMemoryRegion(MemoryRegion region) {
|
||||
switch (region) {
|
||||
case MemoryRegion::APPLICATION:
|
||||
return &memory_regions[0];
|
||||
return memory_regions[0];
|
||||
case MemoryRegion::SYSTEM:
|
||||
return &memory_regions[1];
|
||||
return memory_regions[1];
|
||||
case MemoryRegion::BASE:
|
||||
return &memory_regions[2];
|
||||
return memory_regions[2];
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -147,7 +147,7 @@ void KernelSystem::HandleSpecialMapping(VMManager& address_space, const AddressM
|
||||
return;
|
||||
}
|
||||
|
||||
u8* target_pointer = memory.GetPhysicalPointer(area->paddr_base + offset_into_region);
|
||||
auto target_pointer = memory.GetPhysicalRef(area->paddr_base + offset_into_region);
|
||||
|
||||
// TODO(yuriks): This flag seems to have some other effect, but it's unknown what
|
||||
MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO;
|
||||
@ -160,20 +160,16 @@ void KernelSystem::HandleSpecialMapping(VMManager& address_space, const AddressM
|
||||
}
|
||||
|
||||
void KernelSystem::MapSharedPages(VMManager& address_space) {
|
||||
auto cfg_mem_vma =
|
||||
address_space
|
||||
.MapBackingMemory(Memory::CONFIG_MEMORY_VADDR,
|
||||
reinterpret_cast<u8*>(&config_mem_handler->GetConfigMem()),
|
||||
Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared)
|
||||
.Unwrap();
|
||||
auto cfg_mem_vma = address_space
|
||||
.MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, {config_mem_handler},
|
||||
Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared)
|
||||
.Unwrap();
|
||||
address_space.Reprotect(cfg_mem_vma, VMAPermission::Read);
|
||||
|
||||
auto shared_page_vma =
|
||||
address_space
|
||||
.MapBackingMemory(Memory::SHARED_PAGE_VADDR,
|
||||
reinterpret_cast<u8*>(&shared_page_handler->GetSharedPage()),
|
||||
Memory::SHARED_PAGE_SIZE, MemoryState::Shared)
|
||||
.Unwrap();
|
||||
auto shared_page_vma = address_space
|
||||
.MapBackingMemory(Memory::SHARED_PAGE_VADDR, {shared_page_handler},
|
||||
Memory::SHARED_PAGE_SIZE, MemoryState::Shared)
|
||||
.Unwrap();
|
||||
address_space.Reprotect(shared_page_vma, VMAPermission::Read);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
#include <optional>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <boost/serialization/set.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/serialization/boost_interval_set.hpp"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -60,6 +62,16 @@ struct MemoryRegionInfo {
|
||||
* @param size the size of the region to free.
|
||||
*/
|
||||
void Free(u32 offset, u32 size);
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& base;
|
||||
ar& size;
|
||||
ar& used;
|
||||
ar& free_blocks;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -4,14 +4,18 @@
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/global.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::Mutex)
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void ReleaseThreadMutexes(Thread* thread) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user