diff --git a/CMakeLists.txt b/CMakeLists.txt index 93a477adf4..e85534e8f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 2aeaa56880..7d687a4dc5 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -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) diff --git a/externals/boost b/externals/boost index 502437b2ae..36603a1e66 160000 --- a/externals/boost +++ b/externals/boost @@ -1 +1 @@ -Subproject commit 502437b2ae3f1da821aa7d5d5174ec356fa89269 +Subproject commit 36603a1e665e849d29b1735a12c0a51284a10dd0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 33d7400fae..1ada1b3ebc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 3cb9b538d6..63a5665967 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -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) diff --git a/src/audio_core/dsp_interface.h b/src/audio_core/dsp_interface.h index 5b83e684dc..41ab0c0dc5 100644 --- a/src/audio_core/dsp_interface.h +++ b/src/audio_core/dsp_interface.h @@ -6,6 +6,7 @@ #include #include +#include #include "audio_core/audio_types.h" #include "audio_core/time_stretch.h" #include "common/common_types.h" @@ -113,6 +114,10 @@ private: Common::RingBuffer fifo; std::array last_frame{}; TimeStretcher time_stretcher; + + template + void serialize(Archive& ar, const unsigned int) {} + friend class boost::serialization::access; }; } // namespace AudioCore diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index 1725fdf781..df52f461b3 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -2,6 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include +#include +#include #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 +void DspHle::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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, num_dsp_pipe> pipe_data; + std::array, num_dsp_pipe> pipe_data{}; HLE::DspMemory dsp_memory; std::array 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 decoder; + std::unique_ptr decoder{}; - std::weak_ptr dsp_dsp; + std::weak_ptr dsp_dsp{}; + + template + 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_) { diff --git a/src/audio_core/hle/hle.h b/src/audio_core/hle/hle.h index 4ab468331d..11ec2820a6 100644 --- a/src/audio_core/hle/hle.h +++ b/src/audio_core/hle/hle.h @@ -7,6 +7,7 @@ #include #include #include +#include #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; + + DspHle(); + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; } // namespace AudioCore + +BOOST_CLASS_EXPORT_KEY(AudioCore::DspHle) diff --git a/src/audio_core/hle/mixers.h b/src/audio_core/hle/mixers.h index c5bfd512f2..5043be38ce 100644 --- a/src/audio_core/hle/mixers.h +++ b/src/audio_core/hle/mixers.h @@ -5,6 +5,7 @@ #pragma once #include +#include #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 + 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 diff --git a/src/audio_core/hle/shared_memory.h b/src/audio_core/hle/shared_memory.h index 8ab9aa88e9..43bf1df694 100644 --- a/src/audio_core/hle/shared_memory.h +++ b/src/audio_core/hle/shared_memory.h @@ -8,6 +8,7 @@ #include #include #include +#include #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 + void serialize(Archive& ar, const unsigned int) { + ar& storage; + } + friend class boost::serialization::access; }; static_assert(std::is_trivially_copyable::value, "u32_dsp isn't trivially copyable"); diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h index b0db4a5d32..17c31672bc 100644 --- a/src/audio_core/hle/source.h +++ b/src/audio_core/hle/source.h @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include #include #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 + 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, BufferOrder> input_queue; + std::priority_queue, 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 + 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 + void serialize(Archive& ar, const unsigned int) { + ar& state; + } + friend class boost::serialization::access; }; } // namespace AudioCore::HLE diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index ddabad13b7..053d88f516 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -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(); f->glFinish(); diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 9830f90006..88be0330f3 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -57,7 +57,7 @@ const std::array, 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 default_hotkeys{ +const std::array 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 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}}, diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index 540ab6c411..32ee7a1dfe 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp @@ -216,7 +216,7 @@ std::vector> WaitTreeThread::GetChildren() const { std::vector> list(WaitTreeWaitObject::GetChildren()); const auto& thread = static_cast(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> WaitTreeThread::GetChildren() const { break; } + list.push_back(std::make_unique(tr("object id = %1").arg(thread.GetObjectId()))); list.push_back(std::make_unique(tr("processor = %1").arg(processor))); list.push_back(std::make_unique(tr("thread id = %1").arg(thread.GetThreadId()))); list.push_back( diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 1f2a712867..7389b8c23e 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -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::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(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(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(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 = diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 4bdbd622f2..234b9893a4 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -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 actions_load_state; + std::array actions_save_state; + + u32 oldest_slot; + u64 oldest_slot_time; + u32 newest_slot; + u64 newest_slot_time; QTranslator translator; diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 6011b0e7f9..2eff98083b 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -79,11 +79,28 @@ &Emulation + + + Save State + + + + + + + Load State + + + + + + + @@ -217,6 +234,22 @@ &Stop + + + false + + + Save + + + + + false + + + Load + + FAQ @@ -235,6 +268,16 @@ Single Window Mode + + + Save to Oldest Slot + + + + + Load from Newest Slot + + Configure... diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index af07ac2152..7ab54242d5 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -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) diff --git a/src/common/archives.h b/src/common/archives.h new file mode 100644 index 0000000000..b9f4330bde --- /dev/null +++ b/src/common/archives.h @@ -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 +#include +#include + +using iarchive = boost::archive::binary_iarchive; +using oarchive = boost::archive::binary_oarchive; + +#define SERIALIZE_IMPL(A) \ + template void A::serialize(iarchive & ar, const unsigned int file_version); \ + template void A::serialize(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) diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 13e71615e6..eec4dde9c6 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -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) diff --git a/src/common/construct.h b/src/common/construct.h new file mode 100644 index 0000000000..cb47bb46ea --- /dev/null +++ b/src/common/construct.h @@ -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 + +/// 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 + static void save_construct(Archive& ar, const T* t, const unsigned int file_version) { + t->save_construct(ar, file_version); + } + template + 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 \ + void save_construct_data(Archive& ar, const T* t, const unsigned int file_version) { \ + construct_access::save_construct(ar, t, file_version); \ + } \ + template \ + void load_construct_data(Archive& ar, T* t, const unsigned int file_version) { \ + construct_access::load_construct(ar, t, file_version); \ + } \ + } diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index ce41890f77..fd1a3fd309 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -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 diff --git a/src/common/file_util.h b/src/common/file_util.h index 2037db13e4..d5915b8b73 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #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 { + std::string& str; + + explicit Path(std::string& _str) : str(_str) {} + + static const Path make(std::string& str) { + return Path(str); + } + + template + void save(Archive& ar, const unsigned int) const { + auto s_path = SerializePath(str, true); + ar << s_path; + } + template + 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 children; + +private: + template + 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 @@ -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 + 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 diff --git a/src/common/memory_ref.cpp b/src/common/memory_ref.cpp new file mode 100644 index 0000000000..300f87d581 --- /dev/null +++ b/src/common/memory_ref.cpp @@ -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) diff --git a/src/common/memory_ref.h b/src/common/memory_ref.h new file mode 100644 index 0000000000..0a50c7be9c --- /dev/null +++ b/src/common/memory_ref.h @@ -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 +#include +#include +#include +#include +#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 + 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& Vector() { + return data; + } + + const std::vector& Vector() const { + return data; + } + +private: + std::vector data; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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 backing_mem_) + : backing_mem(std::move(backing_mem_)), offset(0) { + Init(); + } + MemoryRef(std::shared_ptr 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 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(backing_mem->GetSize() - offset); + } else { + cptr = nullptr; + csize = 0; + } + } + + template + void serialize(Archive& ar, const unsigned int) { + ar& backing_mem; + ar& offset; + Init(); + } + friend class boost::serialization::access; +}; diff --git a/src/common/serialization/atomic.h b/src/common/serialization/atomic.h new file mode 100644 index 0000000000..0cf0f20bc4 --- /dev/null +++ b/src/common/serialization/atomic.h @@ -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 +#include + +namespace boost::serialization { + +template +void serialize(Archive& ar, std::atomic& value, const unsigned int file_version) { + boost::serialization::split_free(ar, value, file_version); +} + +template +void save(Archive& ar, const std::atomic& value, const unsigned int file_version) { + ar << value.load(); +} + +template +void load(Archive& ar, std::atomic& value, const unsigned int file_version) { + T tmp; + ar >> tmp; + value.store(tmp); +} + +} // namespace boost::serialization diff --git a/src/common/serialization/boost_discrete_interval.hpp b/src/common/serialization/boost_discrete_interval.hpp new file mode 100644 index 0000000000..f04e3cabc6 --- /dev/null +++ b/src/common/serialization/boost_discrete_interval.hpp @@ -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 +#include "common/common_types.h" +#include "common/logging/log.h" + +namespace boost::serialization { + +template +void save(Archive& ar, const boost::icl::discrete_interval& obj, + const unsigned int file_version) { + ar << obj.lower(); + ar << obj.upper(); + ar << obj.bounds()._bits; +} + +template +void load(Archive& ar, boost::icl::discrete_interval& 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 +void serialize(Archive& ar, boost::icl::discrete_interval& obj, + const unsigned int file_version) { + boost::serialization::split_free(ar, obj, file_version); +} + +} // namespace boost::serialization diff --git a/src/common/serialization/boost_flat_set.h b/src/common/serialization/boost_flat_set.h new file mode 100644 index 0000000000..703bd28b3d --- /dev/null +++ b/src/common/serialization/boost_flat_set.h @@ -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 +#include +#include "common/common_types.h" + +namespace boost::serialization { + +template +void save(Archive& ar, const boost::container::flat_set& set, const unsigned int file_version) { + ar << static_cast(set.size()); + for (auto& v : set) { + ar << v; + } +} + +template +void load(Archive& ar, boost::container::flat_set& 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 +void serialize(Archive& ar, boost::container::flat_set& set, const unsigned int file_version) { + boost::serialization::split_free(ar, set, file_version); +} + +} // namespace boost::serialization diff --git a/src/common/serialization/boost_interval_set.hpp b/src/common/serialization/boost_interval_set.hpp new file mode 100644 index 0000000000..73a5603609 --- /dev/null +++ b/src/common/serialization/boost_interval_set.hpp @@ -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 +#include +#include "common/serialization/boost_discrete_interval.hpp" + +namespace boost::serialization { + +template +void save(Archive& ar, const boost::icl::interval_set& set, const unsigned int file_version) { + ar << static_cast(set.iterative_size()); + for (auto& v : set) { + ar << v; + } +} + +template +void load(Archive& ar, boost::icl::interval_set& set, const unsigned int file_version) { + u64 count{}; + ar >> count; + set.clear(); + for (u64 i = 0; i < count; i++) { + typename boost::icl::interval_set::interval_type value{}; + ar >> value; + set += value; + } +} + +template +void serialize(Archive& ar, boost::icl::interval_set& set, const unsigned int file_version) { + boost::serialization::split_free(ar, set, file_version); +} + +} // namespace boost::serialization diff --git a/src/common/serialization/boost_small_vector.hpp b/src/common/serialization/boost_small_vector.hpp new file mode 100644 index 0000000000..b4e07a8962 --- /dev/null +++ b/src/common/serialization/boost_small_vector.hpp @@ -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 + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// 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 +inline void save(Archive& ar, const boost::container::small_vector& t, + const unsigned int /* file_version */, mpl::false_) { + boost::serialization::stl::save_collection>(ar, + t); +} + +template +inline void load(Archive& ar, boost::container::small_vector& 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 +inline void save(Archive& ar, const boost::container::small_vector& 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(static_cast(&t[0]), + count); +} + +template +inline void load(Archive& ar, boost::container::small_vector& 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(static_cast(&t[0]), count); +} + +// dispatch to either default or optimized versions + +template +inline void save(Archive& ar, const boost::container::small_vector& t, + const unsigned int file_version) { + typedef typename boost::serialization::use_array_optimization::template apply< + typename remove_const::type>::type use_optimized; + save(ar, t, file_version, use_optimized()); +} + +template +inline void load(Archive& ar, boost::container::small_vector& 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()); + return; + } +#endif + typedef typename boost::serialization::use_array_optimization::template apply< + typename remove_const::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 +inline void serialize(Archive& ar, boost::container::small_vector& 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 +inline void serialize(Archive& ar, boost::container::small_vector& 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 diff --git a/src/common/serialization/boost_vector.hpp b/src/common/serialization/boost_vector.hpp new file mode 100644 index 0000000000..55a5b9eaee --- /dev/null +++ b/src/common/serialization/boost_vector.hpp @@ -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 + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// 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 +inline void save(Archive& ar, const boost::container::vector& t, + const unsigned int /* file_version */, mpl::false_) { + boost::serialization::stl::save_collection>(ar, + t); +} + +template +inline void load(Archive& ar, boost::container::vector& 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 +inline void save(Archive& ar, const boost::container::vector& 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(static_cast(&t[0]), + count); +} + +template +inline void load(Archive& ar, boost::container::vector& 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(static_cast(&t[0]), count); +} + +// dispatch to either default or optimized versions + +template +inline void save(Archive& ar, const boost::container::vector& t, + const unsigned int file_version) { + typedef typename boost::serialization::use_array_optimization::template apply< + typename remove_const::type>::type use_optimized; + save(ar, t, file_version, use_optimized()); +} + +template +inline void load(Archive& ar, boost::container::vector& 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()); + return; + } +#endif + typedef typename boost::serialization::use_array_optimization::template apply< + typename remove_const::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 +inline void serialize(Archive& ar, boost::container::vector& 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 +inline void serialize(Archive& ar, boost::container::vector& t, + const unsigned int file_version) { + boost::serialization::split_free(ar, t, file_version); +} + +} // namespace serialization +} // namespace boost + +#include + +BOOST_SERIALIZATION_COLLECTION_TRAITS(boost::container::vector) + +#endif // BOOST_SERIALIZATION_BOOST_VECTOR_HPP diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 62e48e2248..af40bf09be 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include "common/common_types.h" namespace Common { @@ -157,6 +160,52 @@ private: Queue* first; // The priority level queues of thread ids. std::array 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 + 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 + 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 diff --git a/src/common/vector_math.h b/src/common/vector_math.h index ba36744fc9..ba7bd1aa79 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -32,6 +32,7 @@ #include #include +#include namespace Common { @@ -44,6 +45,13 @@ class Vec4; template class Vec2 { + friend class boost::serialization::access; + template + 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::Normalize() { template class Vec3 { + friend class boost::serialization::access; + template + 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; template class Vec4 { + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& x; + ar& y; + ar& z; + ar& w; + } + public: T x; T y; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d70f218882..01f18e4454 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 6595b53c49..40bfc55433 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -6,10 +6,14 @@ #include #include +#include +#include +#include #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 + 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 + 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& 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 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 GetPageTable() const = 0; + std::shared_ptr timer; private: u32 id; + + friend class boost::serialization::access; + + template + 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(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(i); + u32 r = 0; + switch (reg) { + case CP15_THREAD_UPRW: + case CP15_THREAD_URO: + r = GetCP15Register(reg); + } + ar << r; + } + } + + template + void load(Archive& ar, const unsigned int file_version) { + PurgeState(); + ar >> timer; + ar >> id; + std::shared_ptr 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(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(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) diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index df5de7131b..039601a541 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -153,7 +153,7 @@ ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u std::shared_ptr timer) : ARM_Interface(id, timer), system(*system), memory(memory), cb(std::make_unique(*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 ARM_Dynarmic::GetPageTable() const { + return current_page_table; +} + +void ARM_Dynarmic::SetPageTable(const std::shared_ptr& 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 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(cp15_state); config.define_unpredictable_behaviour = true; return std::make_unique(config); } + +void ARM_Dynarmic::PurgeState() { + ClearInstructionCache(); +} diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 44f2ee3751..7575e9a2af 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -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 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& page_table) override; + void PurgeState() override; + +protected: + std::shared_ptr 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> jits; + std::shared_ptr current_page_table = nullptr; + std::map, std::unique_ptr> jits; }; diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index c2c291df01..099a5c06a3 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -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& page_table) { ClearInstructionCache(); } +std::shared_ptr 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]; } diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 38659eac0a..1452216c2c 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -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 NewContext() const override; void SaveContext(const std::unique_ptr& arg) override; void LoadContext(const std::unique_ptr& arg) override; + void SetPageTable(const std::shared_ptr& page_table) override; void PrepareReschedule() override; + void PurgeState() override; + +protected: + std::shared_ptr GetPageTable() const override; private: void ExecuteInstructions(u64 num_instructions); diff --git a/src/core/cheats/cheats.cpp b/src/core/cheats/cheats.cpp index 8b4a30ca63..9053b5ca68 100644 --- a/src/core/cheats/cheats.cpp +++ b/src/core/cheats/cheats.cpp @@ -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); }); diff --git a/src/core/cheats/cheats.h b/src/core/cheats/cheats.h index a8d3730383..1dfed7ab81 100644 --- a/src/core/cheats/cheats.h +++ b/src/core/cheats/cheats.h @@ -26,6 +26,7 @@ class CheatEngine { public: explicit CheatEngine(Core::System& system); ~CheatEngine(); + void Connect(); std::vector> GetCheats() const; void AddCheat(const std::shared_ptr& cheat); void RemoveCheat(int index); diff --git a/src/core/core.cpp b/src/core/core.cpp index 3f3478aa48..03b70d2a88 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include +#include #include +#include #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(init_result)); @@ -206,7 +288,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st } } cheat_engine = std::make_unique(*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(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(); timing = std::make_unique(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(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(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(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(); - service_manager = std::make_shared(*this); + service_manager = std::make_unique(*this); archive_manager = std::make_unique(*this); HW::Init(*memory); @@ -419,7 +498,7 @@ void System::RegisterImageInterface(std::shared_ptr 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 +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(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 diff --git a/src/core/core.h b/src/core/core.h index 4bc8e6a854..e7fbd8ee36 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -5,7 +5,9 @@ #pragma once #include +#include #include +#include #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(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 telemetry_session; /// Service manager - std::shared_ptr service_manager; + std::unique_ptr service_manager; /// Frontend applets std::shared_ptr 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 reset_requested; - std::atomic shutdown_requested; + std::mutex signal_mutex; + Signal current_signal; + u32 signal_param; + + friend class boost::serialization::access; + template + 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) diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 5dbd5f74cd..9639265965 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -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(100.0 / cpu_clock_percentage); + timers[i] = std::make_shared(); } + 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::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; diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 929f398656..bb34c79b02 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -23,9 +23,12 @@ #include #include #include +#include +#include #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 + void save(Archive& ar, const unsigned int) const { + ar& time; + ar& fifo_order; + ar& userdata; + std::string name = *(type->name); + ar << name; + } + + template + void load(Archive& ar, const unsigned int) { + ar& time; + ar& fifo_order; + ar& userdata; + std::string name; + ar >> name; + type = Global().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 + 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 + 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 diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index 2df4f98c4a..0c5b14dc52 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" @@ -64,6 +66,32 @@ private: std::vector binary; std::string string; std::u16string u16str; + + template + 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 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 delay_generator; + +private: + template + 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 GetFormatInfo(const Path& path, u64 program_id) const = 0; + + template + void serialize(Archive& ar, const unsigned int) {} + friend class boost::serialization::access; }; } // namespace FileSys diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index efcdb2d7f5..8326122f54 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -6,6 +6,7 @@ #include #include #include +#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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index 7dc345c847..f33dd1a895 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h @@ -6,6 +6,8 @@ #include #include +#include +#include #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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp index 06fe696224..789547f0e3 100644 --- a/src/core/file_sys/archive_ncch.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -8,6 +8,7 @@ #include #include #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 { diff --git a/src/core/file_sys/archive_ncch.h b/src/core/file_sys/archive_ncch.h index 28d9ff0446..95fe889ea5 100644 --- a/src/core/file_sys/archive_ncch.h +++ b/src/core/file_sys/archive_ncch.h @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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 file_buffer; + + NCCHFile() = default; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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 GetFormatInfo(const Path& path, u64 program_id) const override; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/archive_other_savedata.cpp b/src/core/file_sys/archive_other_savedata.cpp index 1c3b071ba8..d4f8debc4b 100644 --- a/src/core/file_sys/archive_other_savedata.cpp +++ b/src/core/file_sys/archive_other_savedata.cpp @@ -4,6 +4,7 @@ #include #include +#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 diff --git a/src/core/file_sys/archive_other_savedata.h b/src/core/file_sys/archive_other_savedata.h index e3e8f83c33..f002fec7f0 100644 --- a/src/core/file_sys/archive_other_savedata.h +++ b/src/core/file_sys/archive_other_savedata.h @@ -4,6 +4,9 @@ #pragma once +#include +#include +#include #include "core/file_sys/archive_source_sd_savedata.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -27,8 +30,15 @@ public: ResultVal GetFormatInfo(const Path& path, u64 program_id) const override; private: - std::string mount_point; std::shared_ptr sd_savedata_source; + + ArchiveFactory_OtherSaveDataPermitted() = default; + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& sd_savedata_source; + } + friend class boost::serialization::access; }; /// File system interface to the OtherSaveDataGeneral archive @@ -47,8 +57,18 @@ public: ResultVal GetFormatInfo(const Path& path, u64 program_id) const override; private: - std::string mount_point; std::shared_ptr sd_savedata_source; + + ArchiveFactory_OtherSaveDataGeneral() = default; + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index c1046e2f56..fc4bd34fa2 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#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( diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h index 591a3b900c..5bc4b8ecdc 100644 --- a/src/core/file_sys/archive_savedata.h +++ b/src/core/file_sys/archive_savedata.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include "core/file_sys/archive_source_sd_savedata.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -27,8 +29,17 @@ public: ResultVal GetFormatInfo(const Path& path, u64 program_id) const override; private: - std::string mount_point; std::shared_ptr sd_savedata_source; + + ArchiveFactory_SaveData() = default; + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& sd_savedata_source; + } + friend class boost::serialization::access; }; } // namespace FileSys + +BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SaveData) diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index d2269fe7cc..4c3ce6d69a 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -4,6 +4,7 @@ #include #include +#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> SDMCArchive::OpenFile(const Path& path, @@ -405,3 +411,5 @@ ResultVal ArchiveFactory_SDMC::GetFormatInfo(const Path& path return ResultCode(-1); } } // namespace FileSys + +SERIALIZE_EXPORT_IMPL(FileSys::SDMCDelayGenerator) diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 41d7b7c591..265dd7e932 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -6,6 +6,9 @@ #include #include +#include +#include +#include #include "core/file_sys/archive_backend.h" #include "core/hle/result.h" @@ -42,6 +45,14 @@ public: protected: ResultVal> OpenFileBase(const Path& path, const Mode& mode) const; std::string mount_point; + + SDMCArchive() = default; + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp index 74552d751e..241e93f0a9 100644 --- a/src/core/file_sys/archive_sdmcwriteonly.cpp +++ b/src/core/file_sys/archive_sdmcwriteonly.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#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> SDMCWriteOnlyArchive::OpenFile(const Path& path, @@ -96,3 +102,5 @@ ResultVal ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const P } } // namespace FileSys + +SERIALIZE_EXPORT_IMPL(FileSys::SDMCWriteOnlyDelayGenerator) diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h index 8191f053fb..f4149961c3 100644 --- a/src/core/file_sys/archive_sdmcwriteonly.h +++ b/src/core/file_sys/archive_sdmcwriteonly.h @@ -31,6 +31,14 @@ public: const Mode& mode) const override; ResultVal> OpenDirectory(const Path& path) const override; + +private: + SDMCWriteOnlyArchive() = default; + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp index 0f8dd80651..e866e9bc0a 100644 --- a/src/core/file_sys/archive_selfncch.cpp +++ b/src/core/file_sys/archive_selfncch.cpp @@ -4,6 +4,7 @@ #include #include +#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> data; + + ExeFSSectionFile() = default; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& ncch_data; + } + friend class boost::serialization::access; }; void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) { @@ -297,3 +318,6 @@ ResultVal ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&, } } // namespace FileSys + +SERIALIZE_EXPORT_IMPL(FileSys::ExeFSSectionFile) +SERIALIZE_EXPORT_IMPL(FileSys::SelfNCCHArchive) diff --git a/src/core/file_sys/archive_selfncch.h b/src/core/file_sys/archive_selfncch.h index 779e7e75ab..9f5b353b36 100644 --- a/src/core/file_sys/archive_selfncch.h +++ b/src/core/file_sys/archive_selfncch.h @@ -8,6 +8,10 @@ #include #include #include +#include +#include +#include +#include #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> banner; std::shared_ptr romfs_file; std::shared_ptr update_romfs_file; + +private: + template + 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 ncch_data; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp index 0b8072b96f..9afbfd73c3 100644 --- a/src/core/file_sys/archive_source_sd_savedata.cpp +++ b/src/core/file_sys/archive_source_sd_savedata.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#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 { diff --git a/src/core/file_sys/archive_source_sd_savedata.h b/src/core/file_sys/archive_source_sd_savedata.h index b5fe43cc16..4ac028a3d7 100644 --- a/src/core/file_sys/archive_source_sd_savedata.h +++ b/src/core/file_sys/archive_source_sd_savedata.h @@ -6,6 +6,8 @@ #include #include +#include +#include #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 + void serialize(Archive& ar, const unsigned int) { + ar& mount_point; + } + friend class boost::serialization::access; }; } // namespace FileSys + +BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveSource_SDSaveData) diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index cef42e5611..ecfb342199 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -7,6 +7,7 @@ #include #include #include +#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) { diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h index e72ecce3a1..d4f204a666 100644 --- a/src/core/file_sys/archive_systemsavedata.h +++ b/src/core/file_sys/archive_systemsavedata.h @@ -6,6 +6,8 @@ #include #include +#include +#include #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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/delay_generator.cpp b/src/core/file_sys/delay_generator.cpp index 04f877f833..137e63d696 100644 --- a/src/core/file_sys/delay_generator.cpp +++ b/src/core/file_sys/delay_generator.cpp @@ -3,8 +3,11 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "core/file_sys/delay_generator.h" +SERIALIZE_EXPORT_IMPL(FileSys::DefaultDelayGenerator) + namespace FileSys { DelayGenerator::~DelayGenerator() = default; diff --git a/src/core/file_sys/delay_generator.h b/src/core/file_sys/delay_generator.h index d530f2ee29..6513a730ef 100644 --- a/src/core/file_sys/delay_generator.h +++ b/src/core/file_sys/delay_generator.h @@ -5,8 +5,18 @@ #pragma once #include +#include +#include #include "common/common_types.h" +#define SERIALIZE_DELAY_GENERATOR \ +private: \ + template \ + void serialize(Archive& ar, const unsigned int) { \ + ar& boost::serialization::base_object(*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 + 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); diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory_backend.h index e9f124b024..4c9dbb4df3 100644 --- a/src/core/file_sys/directory_backend.h +++ b/src/core/file_sys/directory_backend.h @@ -53,6 +53,11 @@ public: * @return true if the directory closed correctly */ virtual bool Close() const = 0; + +private: + template + void serialize(Archive& ar, const unsigned int) {} + friend class boost::serialization::access; }; } // namespace FileSys diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index 0614605468..6ace314bf9 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -5,6 +5,7 @@ #include #include #include +#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 DiskFile::Read(const u64 offset, const std::size_t length, diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h index 481aa470a9..2f0e5691ae 100644 --- a/src/core/file_sys/disk_archive.h +++ b/src/core/file_sys/disk_archive.h @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #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 file; + +private: + DiskFile() = default; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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::iterator children_iterator; + +private: + DiskDirectory() = default; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index c865c98e8f..d56fc4c1ca 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "common/common_types.h" #include "core/hle/result.h" #include "delay_generator.h" @@ -90,6 +91,12 @@ public: protected: std::unique_ptr delay_generator; + + template + void serialize(Archive& ar, const unsigned int) { + ar& delay_generator; + } + friend class boost::serialization::access; }; } // namespace FileSys diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index ba16f8cd86..10d0851644 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -5,6 +5,7 @@ #include #include #include +#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 file, diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index 8168e04f4a..145fabb986 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #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 romfs_file; + + IVFCFile() = default; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& romfs_file; + } + friend class boost::serialization::access; }; class IVFCDirectory : public DirectoryBackend { @@ -159,6 +176,23 @@ private: std::vector romfs_file; u64 data_offset; u64 data_size; + + IVFCFileInMemory() = default; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/layered_fs.cpp b/src/core/file_sys/layered_fs.cpp index 9d5fbf7c21..a4ef57baf2 100644 --- a/src/core/file_sys/layered_fs.cpp +++ b/src/core/file_sys/layered_fs.cpp @@ -5,6 +5,7 @@ #include #include #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 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 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(&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(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(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(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(u16name.size() * 2); std::memcpy(file_metadata_table.data() + written, &metadata, sizeof(metadata)); written += sizeof(metadata); diff --git a/src/core/file_sys/layered_fs.h b/src/core/file_sys/layered_fs.h index 956eedcfa7..2a494bd8e2 100644 --- a/src/core/file_sys/layered_fs.h +++ b/src/core/file_sys/layered_fs.h @@ -9,6 +9,10 @@ #include #include #include +#include +#include +#include +#include #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 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 file_metadata_table; // rebuilt file metadata table u64 current_data_offset{}; // current assigned data offset + + LayeredFS(); + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/romfs_reader.cpp b/src/core/file_sys/romfs_reader.cpp index 64374684a1..4c83515b37 100644 --- a/src/core/file_sys/romfs_reader.cpp +++ b/src/core/file_sys/romfs_reader.cpp @@ -1,15 +1,18 @@ #include #include #include +#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(data_size) - offset); read_length = file.ReadBytes(buffer, read_length); if (is_encrypted) { CryptoPP::CTR_Mode::Decryption d(key.data(), key.size(), ctr.data()); diff --git a/src/core/file_sys/romfs_reader.h b/src/core/file_sys/romfs_reader.h index df0318c99a..26dcb98579 100644 --- a/src/core/file_sys/romfs_reader.h +++ b/src/core/file_sys/romfs_reader.h @@ -1,6 +1,9 @@ #pragma once #include +#include +#include +#include #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 + void serialize(Archive& ar, const unsigned int file_version) {} + friend class boost::serialization::access; }; /** @@ -45,9 +53,26 @@ private: FileUtil::IOFile file; std::array key; std::array 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 + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp index 8d7830468d..090dc6fb05 100644 --- a/src/core/file_sys/savedata_archive.cpp +++ b/src/core/file_sys/savedata_archive.cpp @@ -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> 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) diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h index 176d357106..38b9653d7d 100644 --- a/src/core/file_sys/savedata_archive.h +++ b/src/core/file_sys/savedata_archive.h @@ -38,6 +38,22 @@ public: protected: std::string mount_point; + SaveDataArchive() = default; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/global.h b/src/core/global.h new file mode 100644 index 0000000000..794d71f942 --- /dev/null +++ b/src/core/global.h @@ -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 +T& Global(); + +// Declare explicit specialisation to prevent automatic instantiation +class System; +template <> +System& Global(); + +class Timing; +template <> +Timing& Global(); + +} // namespace Core diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index e52c0f2720..5074e352fc 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -3,8 +3,10 @@ // Refer to the license.txt file included. #include +#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, VAddr wait_address) { @@ -76,16 +80,18 @@ std::shared_ptr KernelSystem::CreateAddressArbiter(std::string n return address_arbiter; } +void AddressArbiter::WakeUp(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr 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, ArbitrationType type, VAddr address, s32 value, u64 nanoseconds) { - auto timeout_callback = [this](ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr 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(shared_from_this()); switch (type) { diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index a4aff6a6ef..c7a263d9ef 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -6,8 +6,15 @@ #include #include +#include +#include +#include +#include +#include +#include #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, ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); + void WakeUp(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr object); + private: KernelSystem& kernel; @@ -67,6 +77,21 @@ private: /// Threads waiting for the address arbiter to be signaled. std::vector> waiting_threads; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + if (file_version > 0) { + ar& boost::serialization::base_object(*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) diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index d217dfb7c1..e7e8a80140 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -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) {} diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 423db01723..8d0f505201 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -6,6 +6,9 @@ #include #include +#include +#include +#include #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 + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_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) diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 3e76f1a4ef..a47e6411b1 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -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) {} diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index de2c7b0baa..1943db8e64 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include #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 parent; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& name; + ar& parent; + } }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::ClientSession) +CONSTRUCT_KERNEL_OBJECT(Kernel::ClientSession) diff --git a/src/core/hle/kernel/config_mem.cpp b/src/core/hle/kernel/config_mem.cpp index 58bef4110b..4b262b5013 100644 --- a/src/core/hle/kernel/config_mem.cpp +++ b/src/core/hle/kernel/config_mem.cpp @@ -3,10 +3,13 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "core/hle/kernel/config_mem.h" //////////////////////////////////////////////////////////////////////////////////////////////////// +SERIALIZE_EXPORT_IMPL(ConfigMem::Handler) + namespace ConfigMem { Handler::Handler() { diff --git a/src/core/hle/kernel/config_mem.h b/src/core/hle/kernel/config_mem.h index ecb97c6bd2..b592aff7aa 100644 --- a/src/core/hle/kernel/config_mem.h +++ b/src/core/hle/kernel/config_mem.h @@ -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 +#include #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(&config_mem); + } + + const u8* GetPtr() const override { + return reinterpret_cast(&config_mem); + } + + std::size_t GetSize() const override { + return sizeof(config_mem); + } + private: ConfigMemDef config_mem; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& boost::serialization::make_binary_object(&config_mem, sizeof(config_mem)); + } }; } // namespace ConfigMem + +BOOST_CLASS_EXPORT_KEY(ConfigMem::Handler) diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index f31162e35b..9f26847b03 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -5,11 +5,14 @@ #include #include #include +#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) {} diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index efc4a0c283..02eabd750b 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -4,6 +4,9 @@ #pragma once +#include +#include +#include #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 + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& reset_type; + ar& signaled; + ar& name; + } }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::Event) +CONSTRUCT_KERNEL_OBJECT(Kernel::Event) diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index bd5a5df6af..728ab5fa31 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #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 + void serialize(Archive& ar, const unsigned int file_version) { + ar& objects; + ar& generations; + ar& next_generation; + ar& next_free_slot; + } }; } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index ab4ecfd059..88bd6b71bb 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -16,6 +16,47 @@ namespace Kernel { +class HLERequestContext::ThreadCallback : public Kernel::WakeupCallback { + +public: + ThreadCallback(std::shared_ptr context_, + std::shared_ptr callback_) + : context(std::move(context_)), callback(std::move(callback_)) {} + void WakeUp(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr 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 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 callback{}; + std::shared_ptr context{}; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& callback; + ar& context; + } + friend class boost::serialization::access; +}; + SessionRequestHandler::SessionInfo::SessionInfo(std::shared_ptr session, std::unique_ptr data) : session(std::move(session)), data(std::move(data)) {} @@ -33,34 +74,16 @@ void SessionRequestHandler::ClientDisconnected(std::shared_ptr se connected_sessions.end()); } -std::shared_ptr HLERequestContext::SleepClientThread(const std::string& reason, - std::chrono::nanoseconds timeout, - WakeupCallback&& callback) { +std::shared_ptr HLERequestContext::SleepClientThread( + const std::string& reason, std::chrono::nanoseconds timeout, + std::shared_ptr 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, - std::shared_ptr 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 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(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 HLERequestContext::SleepClientThread(const std::string& r return event; } +HLERequestContext::HLERequestContext() : kernel(Core::Global()) {} + HLERequestContext::HLERequestContext(KernelSystem& kernel, std::shared_ptr session, - Thread* thread) + std::shared_ptr 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 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 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(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 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 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().Memory()) {} + +MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr 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) diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 26942fe6b1..47d98af2cd 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -11,7 +11,12 @@ #include #include #include +#include +#include +#include +#include #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 + void serialize(Archive& ar, const unsigned int file_version) {} + friend class boost::serialization::access; }; protected: @@ -90,15 +100,33 @@ protected: std::shared_ptr session; std::unique_ptr data; + + private: + SessionInfo() = default; + template + 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 connected_sessions; + +private: + template + 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, 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; + u32 size; IPC::MappedBufferPermissions perms; + + MappedBuffer(); + + template + 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 { public: - HLERequestContext(KernelSystem& kernel, std::shared_ptr session, Thread* thread); + HLERequestContext(KernelSystem& kernel, std::shared_ptr session, + std::shared_ptr thread); ~HLERequestContext(); /// Returns a pointer to the IPC command buffer for this request. @@ -174,8 +215,17 @@ public: return session; } - using WakeupCallback = std::function thread, HLERequestContext& context, ThreadWakeupReason reason)>; + class WakeupCallback { + public: + virtual ~WakeupCallback() = default; + virtual void WakeUp(std::shared_ptr thread, HLERequestContext& context, + ThreadWakeupReason reason) = 0; + + private: + template + 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 SleepClientThread(const std::string& reason, std::chrono::nanoseconds timeout, - WakeupCallback&& callback); + std::shared_ptr 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 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 cmd_buf; std::shared_ptr session; - Thread* thread; + std::shared_ptr thread; // TODO(yuriks): Check common usage of this and optimize size accordingly boost::container::small_vector, 8> request_handles; // The static buffers will be created when the IPC request is translated. std::array, IPC::MAX_STATIC_BUFFERS> static_buffers; // The mapped buffers will be created when the IPC request is translated boost::container::small_vector request_mapped_buffers; + + HLERequestContext(); + template + 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) diff --git a/src/core/hle/kernel/ipc.cpp b/src/core/hle/kernel/ipc.cpp index 3dacb98317..eb14888404 100644 --- a/src/core/hle/kernel/ipc.cpp +++ b/src/core/hle/kernel/ipc.cpp @@ -4,6 +4,7 @@ #include #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(Memory::PAGE_SIZE); + std::shared_ptr reserve_buffer = + std::make_shared(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(num_pages * Memory::PAGE_SIZE); - memory.ReadBlock(*src_process, source_address, buffer.get() + page_offset, size); + std::shared_ptr buffer = + std::make_shared(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), diff --git a/src/core/hle/kernel/ipc.h b/src/core/hle/kernel/ipc.h index b060799586..2a5fcb4b2e 100644 --- a/src/core/hle/kernel/ipc.h +++ b/src/core/hle/kernel/ipc.h @@ -6,6 +6,7 @@ #include #include +#include #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 buffer; - std::unique_ptr reserve_buffer; + std::shared_ptr buffer; + std::shared_ptr reserve_buffer; + +private: + template + 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. diff --git a/src/core/hle/kernel/ipc_debugger/recorder.cpp b/src/core/hle/kernel/ipc_debugger/recorder.cpp index 968815c5bd..f1e4a09f16 100644 --- a/src/core/hle/kernel/ipc_debugger/recorder.cpp +++ b/src/core/hle/kernel/ipc_debugger/recorder.cpp @@ -52,7 +52,7 @@ void Recorder::RegisterRequest(const std::shared_ptr& 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& 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; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8b2d89ead8..d36c8a4c7c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -2,6 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include +#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(); }); MemoryInit(system_mode, n3ds_mode); resource_limits = std::make_unique(*this); @@ -58,24 +65,23 @@ std::shared_ptr KernelSystem::GetCurrentProcess() const { void KernelSystem::SetCurrentProcess(std::shared_ptr process) { current_process = process; - SetCurrentMemoryPageTable(&process->vm_manager.page_table); + SetCurrentMemoryPageTable(process->vm_manager.page_table); } void KernelSystem::SetCurrentProcessForCPU(std::shared_ptr 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 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 +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 diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c275d7ce4c..f0a8368d91 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -132,7 +132,8 @@ public: */ ResultVal> 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 owner_process); /** * Creates a semaphore. @@ -213,7 +214,7 @@ public: void SetCurrentProcess(std::shared_ptr process); void SetCurrentProcessForCPU(std::shared_ptr process, u32 core_id); - void SetCurrentMemoryPageTable(Memory::PageTable* page_table); + void SetCurrentMemoryPageTable(std::shared_ptr page_table); void SetCPUs(std::vector> cpu); @@ -236,11 +237,11 @@ public: IPCDebugger::Recorder& GetIPCRecorder(); const IPCDebugger::Recorder& GetIPCRecorder() const; - MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); + std::shared_ptr GetMemoryRegion(MemoryRegion region); void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); - std::array memory_regions; + std::array, 3> memory_regions{}; /// Adds a port to the named port table void AddNamedPort(std::string name, std::shared_ptr port); @@ -291,12 +292,16 @@ private: std::vector> thread_managers; - std::unique_ptr config_mem_handler; - std::unique_ptr shared_page_handler; + std::shared_ptr config_mem_handler; + std::shared_ptr shared_page_handler; std::unique_ptr ipc_recorder; u32 next_thread_id; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); }; } // namespace Kernel diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 7b6425bbd6..a4e77e0b29 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -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(); + config_mem_handler = std::make_shared(); 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(timing); + shared_page_handler = std::make_shared(timing); } -MemoryRegionInfo* KernelSystem::GetMemoryRegion(MemoryRegion region) { +std::shared_ptr 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(&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(&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); } diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h index bb4e174f79..739369a9c6 100644 --- a/src/core/hle/kernel/memory.h +++ b/src/core/hle/kernel/memory.h @@ -6,7 +6,9 @@ #include #include +#include #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 + void serialize(Archive& ar, const unsigned int file_version) { + ar& base; + ar& size; + ar& used; + ar& free_blocks; + } }; } // namespace Kernel diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 30dd1eb554..77a5fe9036 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -4,14 +4,18 @@ #include #include +#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) { diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 1f6358909c..db98aa8c96 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include #include "common/common_types.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/wait_object.h" @@ -58,6 +62,16 @@ public: private: KernelSystem& kernel; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& lock_count; + ar& priority; + ar& name; + ar& holding_thread; + } }; /** @@ -67,3 +81,6 @@ private: void ReleaseThreadMutexes(Thread* thread); } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::Mutex) +CONSTRUCT_KERNEL_OBJECT(Kernel::Mutex) diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 9547a83ed2..b272366d74 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -7,7 +7,12 @@ #include #include #include +#include +#include +#include #include "common/common_types.h" +#include "common/serialization/atomic.h" +#include "core/global.h" #include "core/hle/kernel/kernel.h" namespace Kernel { @@ -64,6 +69,12 @@ public: private: std::atomic object_id; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& object_id; + } }; template @@ -87,3 +98,13 @@ inline std::shared_ptr DynamicObjectCast(std::shared_ptr object) { } } // namespace Kernel + +BOOST_SERIALIZATION_ASSUME_ABSTRACT(Kernel::Object) + +#define CONSTRUCT_KERNEL_OBJECT(T) \ + namespace boost::serialization { \ + template \ + void load_construct_data(Archive& ar, T* t, const unsigned int file_version) { \ + ::new (t) T(Core::Global()); \ + } \ + } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index c6aa2b8954..9b03f30f97 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -4,9 +4,14 @@ #include #include +#include +#include +#include +#include "common/archives.h" #include "common/assert.h" #include "common/common_funcs.h" #include "common/logging/log.h" +#include "common/serialization/boost_vector.hpp" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" @@ -15,8 +20,34 @@ #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" +SERIALIZE_EXPORT_IMPL(Kernel::Process) +SERIALIZE_EXPORT_IMPL(Kernel::CodeSet) + namespace Kernel { +template +void Process::serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& handle_table; + ar& codeset; // TODO: Replace with apploader reference + ar& resource_limit; + ar& svc_access_mask; + ar& handle_table_size; + ar&(boost::container::vector>&)address_mappings; + ar& flags.raw; + ar& kernel_version; + ar& ideal_processor; + ar& status; + ar& process_id; + ar& vm_manager; + ar& memory_used; + ar& memory_region; + ar& tls_slots; +} + +SERIALIZE_IMPL(Process) + std::shared_ptr KernelSystem::CreateCodeSet(std::string name, u64 program_id) { auto codeset{std::make_shared(*this)}; @@ -191,7 +222,7 @@ ResultVal Process::HeapAllocate(VAddr target, u32 size, VMAPermission per std::fill(kernel.memory.GetFCRAMPointer(interval.lower()), kernel.memory.GetFCRAMPointer(interval.upper()), 0); auto vma = vm_manager.MapBackingMemory(interval_target, - kernel.memory.GetFCRAMPointer(interval.lower()), + kernel.memory.GetFCRAMRef(interval.lower()), interval_size, memory_state); ASSERT(vma.Succeeded()); vm_manager.Reprotect(vma.Unwrap(), perms); @@ -219,7 +250,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) { // Free heaps block by block CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(target, size)); for (const auto [backing_memory, block_size] : backing_blocks) { - memory_region->Free(kernel.memory.GetFCRAMOffset(backing_memory), block_size); + memory_region->Free(kernel.memory.GetFCRAMOffset(backing_memory.GetPtr()), block_size); } ResultCode result = vm_manager.UnmapRange(target, size); @@ -263,9 +294,9 @@ ResultVal Process::LinearAllocate(VAddr target, u32 size, VMAPermission p } } - u8* backing_memory = kernel.memory.GetFCRAMPointer(physical_offset); + auto backing_memory = kernel.memory.GetFCRAMRef(physical_offset); - std::fill(backing_memory, backing_memory + size, 0); + std::fill(backing_memory.GetPtr(), backing_memory.GetPtr() + size, 0); auto vma = vm_manager.MapBackingMemory(target, backing_memory, size, MemoryState::Continuous); ASSERT(vma.Succeeded()); vm_manager.Reprotect(vma.Unwrap(), perms); @@ -403,8 +434,7 @@ ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission pe Kernel::Process::Process(KernelSystem& kernel) : Object(kernel), handle_table(kernel), vm_manager(kernel.memory), kernel(kernel) { - - kernel.memory.RegisterPageTable(&vm_manager.page_table); + kernel.memory.RegisterPageTable(vm_manager.page_table); } Kernel::Process::~Process() { // Release all objects this process owns first so that their potential destructor can do clean @@ -413,7 +443,7 @@ Kernel::Process::~Process() { // memory etc.) even if they are still referenced by other processes. handle_table.Clear(); - kernel.memory.UnregisterPageTable(&vm_manager.page_table); + kernel.memory.UnregisterPageTable(vm_manager.page_table); } std::shared_ptr KernelSystem::GetProcessById(u32 process_id) const { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index dc2c878b8c..4eca568d90 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -11,6 +11,10 @@ #include #include #include +#include +#include +#include +#include #include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/handle_table.h" @@ -25,6 +29,16 @@ struct AddressMapping { u32 size; bool read_only; bool unk_flag; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& address; + ar& size; + ar& read_only; + ar& unk_flag; + } }; union ProcessFlags { @@ -59,6 +73,15 @@ public: std::size_t offset = 0; VAddr addr = 0; u32 size = 0; + + private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& offset; + ar& addr; + ar& size; + } }; std::string GetTypeName() const override { @@ -106,6 +129,18 @@ public: std::string name; /// Title ID corresponding to the process u64 program_id; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& memory; + ar& segments; + ar& entrypoint; + ar& name; + ar& program_id; + } }; class Process final : public Object { @@ -167,7 +202,7 @@ public: u32 memory_used = 0; - MemoryRegionInfo* memory_region = nullptr; + std::shared_ptr memory_region = nullptr; /// The Thread Local Storage area is allocated as processes create threads, /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part @@ -195,5 +230,14 @@ public: private: KernelSystem& kernel; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::CodeSet) +BOOST_CLASS_EXPORT_KEY(Kernel::Process) +CONSTRUCT_KERNEL_OBJECT(Kernel::CodeSet) +CONSTRUCT_KERNEL_OBJECT(Kernel::Process) diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 8691c142ec..9affa685a0 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -3,10 +3,13 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/kernel/resource_limit.h" +SERIALIZE_EXPORT_IMPL(Kernel::ResourceLimit) + namespace Kernel { ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {} diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 99ae8f2cf4..cb1c8c78e4 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include #include "common/common_types.h" #include "core/hle/kernel/object.h" @@ -110,6 +114,35 @@ public: /// Current CPU time that the processes in this category are utilizing s32 current_cpu_time = 0; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + // NB most of these aren't used at all currently, but we're adding them here for forwards + // compatibility + ar& name; + ar& max_priority; + ar& max_commit; + ar& max_threads; + ar& max_events; + ar& max_mutexes; + ar& max_semaphores; + ar& max_timers; + ar& max_shared_mems; + ar& max_address_arbiters; + ar& max_cpu_time; + ar& current_commit; + ar& current_threads; + ar& current_events; + ar& current_mutexes; + ar& current_semaphores; + ar& current_timers; + ar& current_shared_mems; + ar& current_address_arbiters; + ar& current_cpu_time; + } }; class ResourceLimitList { @@ -126,6 +159,15 @@ public: private: std::array, 4> resource_limits; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& resource_limits; + } }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::ResourceLimit) +CONSTRUCT_KERNEL_OBJECT(Kernel::ResourceLimit) diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index bbc8a385f8..7aefc86054 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -2,12 +2,15 @@ // 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/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/thread.h" +SERIALIZE_EXPORT_IMPL(Kernel::Semaphore) + namespace Kernel { Semaphore::Semaphore(KernelSystem& kernel) : WaitObject(kernel) {} diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index 47b3eabf10..b9863a838a 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -5,6 +5,9 @@ #pragma once #include +#include +#include +#include #include #include "common/common_types.h" #include "core/hle/kernel/object.h" @@ -43,6 +46,19 @@ public: * @return The number of free slots the semaphore had before this call */ ResultVal Release(s32 release_count); + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& max_count; + ar& available_count; + ar& name; + } }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::Semaphore) +CONSTRUCT_KERNEL_OBJECT(Kernel::Semaphore) diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index a69b427780..006403925d 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -3,14 +3,22 @@ // Refer to the license.txt file included. #include +#include +#include +#include +#include +#include "common/archives.h" #include "common/assert.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/thread.h" +SERIALIZE_EXPORT_IMPL(Kernel::ServerPort) + namespace Kernel { ServerPort::ServerPort(KernelSystem& kernel) : WaitObject(kernel) {} @@ -48,4 +56,13 @@ KernelSystem::PortPair KernelSystem::CreatePortPair(u32 max_sessions, std::strin return std::make_pair(std::move(server_port), std::move(client_port)); } +template +void ServerPort::serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& name; + ar& pending_sessions; + ar& hle_handler; +} +SERIALIZE_IMPL(ServerPort) + } // namespace Kernel diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 9b0f134802..00eb101005 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -7,8 +7,10 @@ #include #include #include +#include #include "common/common_types.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" @@ -60,6 +62,14 @@ public: bool ShouldWait(const Thread* thread) const override; void Acquire(Thread* thread) override; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::ServerPort) +CONSTRUCT_KERNEL_OBJECT(Kernel::ServerPort) diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 5855d83a5d..61ade5b70a 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -3,7 +3,10 @@ // Refer to the license.txt file included. #include - +#include +#include +#include +#include "common/archives.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/hle_ipc.h" @@ -11,8 +14,22 @@ #include "core/hle/kernel/session.h" #include "core/hle/kernel/thread.h" +SERIALIZE_EXPORT_IMPL(Kernel::ServerSession) + namespace Kernel { +template +void ServerSession::serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& name; + ar& parent; + ar& hle_handler; + ar& pending_requesting_threads; + ar& currently_handling; + ar& mapped_buffer_context; +} +SERIALIZE_IMPL(ServerSession) + ServerSession::ServerSession(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel) {} ServerSession::~ServerSession() { // This destructor will be called automatically when the last ServerSession handle is closed by @@ -68,14 +85,15 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread) { // If this ServerSession has an associated HLE handler, forward the request to it. if (hle_handler != nullptr) { std::array cmd_buf; - Kernel::Process* current_process = thread->owner_process; + auto current_process = thread->owner_process; kernel.memory.ReadBlock(*current_process, thread->GetCommandBufferAddress(), cmd_buf.data(), cmd_buf.size() * sizeof(u32)); - Kernel::HLERequestContext context(kernel, SharedFrom(this), thread.get()); - context.PopulateFromIncomingCommandBuffer(cmd_buf.data(), *current_process); + auto context = + std::make_shared(kernel, SharedFrom(this), thread); + context->PopulateFromIncomingCommandBuffer(cmd_buf.data(), current_process); - hle_handler->HandleSyncRequest(context); + hle_handler->HandleSyncRequest(*context); ASSERT(thread->status == Kernel::ThreadStatus::Running || thread->status == Kernel::ThreadStatus::WaitHleEvent); @@ -83,7 +101,7 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread) { // put the thread to sleep then the writing of the command buffer will be deferred to the // wakeup callback. if (thread->status == Kernel::ThreadStatus::Running) { - context.WriteToOutgoingCommandBuffer(cmd_buf.data(), *current_process); + context->WriteToOutgoingCommandBuffer(cmd_buf.data(), *current_process); kernel.memory.WriteBlock(*current_process, thread->GetCommandBufferAddress(), cmd_buf.data(), cmd_buf.size() * sizeof(u32)); } diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 940f38f9b5..b91accce5b 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -6,10 +6,12 @@ #include #include +#include #include "common/assert.h" #include "common/common_types.h" #include "core/hle/kernel/ipc.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/session.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" #include "core/memory.h" @@ -103,6 +105,13 @@ private: friend class KernelSystem; KernelSystem& kernel; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::ServerSession) +CONSTRUCT_KERNEL_OBJECT(Kernel::ServerSession) diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 6429147442..8bb846c525 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -2,11 +2,22 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include "common/archives.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" -#include "core/hle/kernel/thread.h" + +SERIALIZE_IMPL(Kernel::Session) namespace Kernel { -Session::Session() {} -Session::~Session() {} +template +void Session::serialize(Archive& ar, const unsigned int file_version) { + ar& client; + ar& server; + ar& port; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 17bb4d6c67..eca1a9252b 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "core/hle/kernel/object.h" namespace Kernel { @@ -24,5 +25,10 @@ public: ClientSession* client = nullptr; ///< The client endpoint of the session. ServerSession* server = nullptr; ///< The server endpoint of the session. std::shared_ptr port; ///< The port that this session is associated with (optional). + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); }; } // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 4cd3055085..e8f792ee8c 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -3,12 +3,15 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/logging/log.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/shared_memory.h" #include "core/memory.h" +SERIALIZE_EXPORT_IMPL(Kernel::SharedMemory) + namespace Kernel { SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {} @@ -38,13 +41,13 @@ ResultVal> KernelSystem::CreateSharedMemory( if (address == 0) { // We need to allocate a block from the Linear Heap ourselves. // We'll manually allocate some memory from the linear heap in the specified region. - MemoryRegionInfo* memory_region = GetMemoryRegion(region); + auto memory_region = GetMemoryRegion(region); auto offset = memory_region->LinearAllocate(size); ASSERT_MSG(offset, "Not enough space in region to allocate shared memory!"); std::fill(memory.GetFCRAMPointer(*offset), memory.GetFCRAMPointer(*offset + size), 0); - shared_memory->backing_blocks = {{memory.GetFCRAMPointer(*offset), size}}; + shared_memory->backing_blocks = {{memory.GetFCRAMRef(*offset), size}}; shared_memory->holding_memory += MemoryRegionInfo::Interval(*offset, *offset + size); shared_memory->linear_heap_phys_offset = *offset; @@ -75,7 +78,7 @@ std::shared_ptr KernelSystem::CreateSharedMemoryForApplet( auto shared_memory{std::make_shared(*this)}; // Allocate memory in heap - MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::SYSTEM); + auto memory_region = GetMemoryRegion(MemoryRegion::SYSTEM); auto backing_blocks = memory_region->HeapAllocate(size); ASSERT_MSG(!backing_blocks.empty(), "Not enough space in region to allocate shared memory!"); shared_memory->holding_memory = backing_blocks; @@ -86,7 +89,7 @@ std::shared_ptr KernelSystem::CreateSharedMemoryForApplet( shared_memory->other_permissions = other_permissions; for (const auto& interval : backing_blocks) { shared_memory->backing_blocks.push_back( - {memory.GetFCRAMPointer(interval.lower()), interval.upper() - interval.lower()}); + {memory.GetFCRAMRef(interval.lower()), interval.upper() - interval.lower()}); std::fill(memory.GetFCRAMPointer(interval.lower()), memory.GetFCRAMPointer(interval.upper()), 0); } diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 0d781cfcc8..0e500a1ddf 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -6,7 +6,11 @@ #include #include +#include +#include +#include #include "common/common_types.h" +#include "common/memory_ref.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/result.h" @@ -86,7 +90,7 @@ private: /// during creation. PAddr linear_heap_phys_offset = 0; /// Backing memory for this shared memory block. - std::vector> backing_blocks; + std::vector> backing_blocks; /// Size of the memory block. Page-aligned. u32 size = 0; /// Permission restrictions applied to the process which created the block. @@ -104,6 +108,24 @@ private: friend class KernelSystem; KernelSystem& kernel; + + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& linear_heap_phys_offset; + ar& backing_blocks; + ar& size; + ar& permissions; + ar& other_permissions; + ar& owner_process; + ar& base_address; + ar& name; + ar& holding_memory; + } + friend class boost::serialization::access; }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::SharedMemory) +CONSTRUCT_KERNEL_OBJECT(Kernel::SharedMemory) diff --git a/src/core/hle/kernel/shared_page.cpp b/src/core/hle/kernel/shared_page.cpp index 30de0ca1f2..8960551420 100644 --- a/src/core/hle/kernel/shared_page.cpp +++ b/src/core/hle/kernel/shared_page.cpp @@ -4,6 +4,7 @@ #include #include +#include "common/archives.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/shared_page.h" @@ -13,6 +14,19 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// +SERIALIZE_EXPORT_IMPL(SharedPage::Handler) + +namespace boost::serialization { + +template +void load_construct_data(Archive& ar, SharedPage::Handler* t, const unsigned int) { + ::new (t) SharedPage::Handler(Core::System::GetInstance().CoreTiming()); +} +template void load_construct_data(iarchive& ar, SharedPage::Handler* t, + const unsigned int); + +} // namespace boost::serialization + namespace SharedPage { static std::chrono::seconds GetInitTime() { diff --git a/src/core/hle/kernel/shared_page.h b/src/core/hle/kernel/shared_page.h index 5092b48693..9cd579d523 100644 --- a/src/core/hle/kernel/shared_page.h +++ b/src/core/hle/kernel/shared_page.h @@ -13,9 +13,13 @@ #include #include #include +#include +#include +#include #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/memory_ref.h" #include "common/swap.h" #include "core/memory.h" @@ -82,7 +86,7 @@ struct SharedPageDef { static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page structure size is wrong"); -class Handler { +class Handler : public BackingMem { public: Handler(Core::Timing& timing); @@ -96,6 +100,18 @@ public: SharedPageDef& GetSharedPage(); + u8* GetPtr() override { + return reinterpret_cast(&shared_page); + } + + const u8* GetPtr() const override { + return reinterpret_cast(&shared_page); + } + + std::size_t GetSize() const override { + return sizeof(shared_page); + } + private: u64 GetSystemTime() const; void UpdateTimeCallback(u64 userdata, int cycles_late); @@ -104,6 +120,22 @@ private: std::chrono::seconds init_time; SharedPageDef shared_page; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& boost::serialization::make_binary_object(&shared_page, sizeof(shared_page)); + } + friend class boost::serialization::access; }; } // namespace SharedPage + +namespace boost::serialization { + +template +void load_construct_data(Archive& ar, SharedPage::Handler* t, const unsigned int); + +} // namespace boost::serialization + +BOOST_CLASS_EXPORT_KEY(SharedPage::Handler) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index d3a7b06266..25eb9bb4ec 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -282,7 +282,7 @@ void SVC::ExitProcess() { // Stop all the process threads that are currently waiting for objects. auto& thread_list = kernel.GetCurrentThreadManager().GetThreadList(); for (auto& thread : thread_list) { - if (thread->owner_process != current_process.get()) + if (thread->owner_process != current_process) continue; if (thread.get() == kernel.GetCurrentThreadManager().GetCurrentThread()) @@ -403,6 +403,76 @@ ResultCode SVC::CloseHandle(Handle handle) { return kernel.GetCurrentProcess()->handle_table.Close(handle); } +static ResultCode ReceiveIPCRequest(Kernel::KernelSystem& kernel, Memory::MemorySystem& memory, + std::shared_ptr server_session, + std::shared_ptr thread); + +class SVC_SyncCallback : public Kernel::WakeupCallback { +public: + explicit SVC_SyncCallback(bool do_output_) : do_output(do_output_) {} + void WakeUp(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr object) { + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); + return; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + + // The wait_all case does not update the output index. + if (do_output) { + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); + } + } + +private: + bool do_output; + + SVC_SyncCallback() = default; + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& do_output; + } + friend class boost::serialization::access; +}; + +class SVC_IPCCallback : public Kernel::WakeupCallback { +public: + explicit SVC_IPCCallback(Core::System& system_) : system(system_) {} + + void WakeUp(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr object) { + + ASSERT(thread->status == ThreadStatus::WaitSynchAny); + ASSERT(reason == ThreadWakeupReason::Signal); + + ResultCode result = RESULT_SUCCESS; + + if (object->GetHandleType() == HandleType::ServerSession) { + auto server_session = DynamicObjectCast(object); + result = ReceiveIPCRequest(system.Kernel(), system.Memory(), server_session, thread); + } + + thread->SetWaitSynchronizationResult(result); + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); + } + +private: + Core::System& system; + + SVC_IPCCallback() : system(Core::Global()) {} + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + } + friend class boost::serialization::access; +}; + /// Wait for a handle to synchronize, timeout after the specified nanoseconds ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) { auto object = kernel.GetCurrentProcess()->handle_table.Get(handle); @@ -426,21 +496,7 @@ ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) { // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = [](ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object) { - ASSERT(thread->status == ThreadStatus::WaitSynchAny); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - return; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - - // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we - // don't have to do anything else here. - }; + thread->wakeup_callback = std::make_shared(false); system.PrepareReschedule(); @@ -515,20 +571,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = [](ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object) { - ASSERT(thread->status == ThreadStatus::WaitSynchAll); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - return; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - // The wait_all case does not update the output index. - }; + thread->wakeup_callback = std::make_shared(false); system.PrepareReschedule(); @@ -575,20 +618,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = [](ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object) { - ASSERT(thread->status == ThreadStatus::WaitSynchAny); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); - return; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); - }; + thread->wakeup_callback = std::make_shared(true); system.PrepareReschedule(); @@ -730,22 +760,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co thread->wait_objects = std::move(objects); - thread->wakeup_callback = [& kernel = this->kernel, &memory = this->memory]( - ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object) { - ASSERT(thread->status == ThreadStatus::WaitSynchAny); - ASSERT(reason == ThreadWakeupReason::Signal); - - ResultCode result = RESULT_SUCCESS; - - if (object->GetHandleType() == HandleType::ServerSession) { - auto server_session = DynamicObjectCast(object); - result = ReceiveIPCRequest(kernel, memory, server_session, thread); - } - - thread->SetWaitSynchronizationResult(result); - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); - }; + thread->wakeup_callback = std::make_shared(system); system.PrepareReschedule(); @@ -916,7 +931,7 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr CASCADE_RESULT(std::shared_ptr thread, kernel.CreateThread(name, entry_point, priority, arg, processor_id, stack_top, - *current_process)); + current_process)); thread->context->SetFpscr(FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO); // 0x03C00000 @@ -1025,7 +1040,7 @@ ResultCode SVC::GetProcessIdOfThread(u32* process_id, Handle thread_handle) { if (thread == nullptr) return ERR_INVALID_HANDLE; - const std::shared_ptr process = SharedFrom(thread->owner_process); + const std::shared_ptr process = thread->owner_process; ASSERT_MSG(process != nullptr, "Invalid parent process for thread={:#010X}", thread_handle); @@ -1592,6 +1607,7 @@ void SVC::CallSVC(u32 immediate) { "Running threads from exiting processes is unimplemented"); const FunctionDef* info = GetSVCInfo(immediate); + LOG_TRACE(Kernel_SVC, "calling {}", info->name); if (info) { if (info->func) { (this->*(info->func))(); @@ -1619,3 +1635,6 @@ void SVCContext::CallSVC(u32 immediate) { } } // namespace Kernel + +SERIALIZE_EXPORT_IMPL(Kernel::SVC_SyncCallback) +SERIALIZE_EXPORT_IMPL(Kernel::SVC_IPCCallback) diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index efddff9e88..4cf2654001 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" namespace Core { @@ -25,4 +26,10 @@ private: std::unique_ptr impl; }; +class SVC_SyncCallback; +class SVC_IPCCallback; + } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::SVC_SyncCallback) +BOOST_CLASS_EXPORT_KEY(Kernel::SVC_IPCCallback) diff --git a/src/core/hle/kernel/svc_wrapper.h b/src/core/hle/kernel/svc_wrapper.h index b4fbbe0de6..77334152d0 100644 --- a/src/core/hle/kernel/svc_wrapper.h +++ b/src/core/hle/kernel/svc_wrapper.h @@ -280,6 +280,26 @@ private: } }; + template + struct WrapPass { + // Call function R(Context::svc)(Us...) and transfer the return value to registers + template + static void Call(Context& context, SVCT svc, Us... u) { + static_assert(std::is_same_v); + if constexpr (std::is_void_v) { + (context.*svc)(u...); + } else { + ResultCode r = (context.*svc)(u...); + if (r.IsError()) { + LOG_ERROR(Kernel_SVC, "level={} summary={} module={} description={}", + r.level.ExtractValue(r.raw), r.summary.ExtractValue(r.raw), + r.module.ExtractValue(r.raw), r.description.ExtractValue(r.raw)); + } + SetParam(context, r); + } + } + }; + template struct WrapHelper; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b2255c2265..60d2026315 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -6,10 +6,13 @@ #include #include #include +#include +#include "common/archives.h" #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" #include "common/math_util.h" +#include "common/serialization/boost_flat_set.h" #include "core/arm/arm_interface.h" #include "core/arm/skyeye_common/armstate.h" #include "core/core.h" @@ -23,8 +26,34 @@ #include "core/hle/result.h" #include "core/memory.h" +SERIALIZE_EXPORT_IMPL(Kernel::Thread) + namespace Kernel { +template +void Thread::serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar&* context.get(); + ar& thread_id; + ar& status; + ar& entry_point; + ar& stack_top; + ar& nominal_priority; + ar& current_priority; + ar& last_running_ticks; + ar& processor_id; + ar& tls_address; + ar& held_mutexes; + ar& pending_mutexes; + ar& owner_process; + ar& wait_objects; + ar& wait_address; + ar& name; + ar& wakeup_callback; +} + +SERIALIZE_IMPL(Thread) + bool Thread::ShouldWait(const Thread* thread) const { return status != ThreadStatus::Dead; } @@ -34,7 +63,7 @@ void Thread::Acquire(Thread* thread) { } Thread::Thread(KernelSystem& kernel, u32 core_id) - : WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()), + : WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()), core_id(core_id), thread_manager(kernel.GetThreadManager(core_id)) {} Thread::~Thread() {} @@ -75,7 +104,7 @@ void Thread::Stop() { void ThreadManager::SwitchContext(Thread* new_thread) { Thread* previous_thread = GetCurrentThread(); - Process* previous_process = nullptr; + std::shared_ptr previous_process = nullptr; Core::Timing& timing = kernel.timing; @@ -107,7 +136,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) { new_thread->status = ThreadStatus::Running; if (previous_process != current_thread->owner_process) { - kernel.SetCurrentProcessForCPU(SharedFrom(current_thread->owner_process), cpu->GetID()); + kernel.SetCurrentProcessForCPU(current_thread->owner_process, cpu->GetID()); } cpu->LoadContext(new_thread->context); @@ -164,7 +193,7 @@ void ThreadManager::ThreadWakeupCallback(u64 thread_id, s64 cycles_late) { // Invoke the wakeup callback before clearing the wait objects if (thread->wakeup_callback) - thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr); + thread->wakeup_callback->WakeUp(ThreadWakeupReason::Timeout, thread, nullptr); // Remove the thread from each of its waiting objects' waitlists for (auto& object : thread->wait_objects) @@ -282,10 +311,9 @@ static void ResetThreadContext(const std::unique_ptrSetCpsr(USER32MODE | ((entry_point & 1) << 5)); // Usermode and THUMB mode } -ResultVal> KernelSystem::CreateThread(std::string name, VAddr entry_point, - u32 priority, u32 arg, - s32 processor_id, VAddr stack_top, - Process& owner_process) { +ResultVal> KernelSystem::CreateThread( + std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top, + std::shared_ptr owner_process) { // Check if priority is in ranged. Lowest priority -> highest priority id. if (priority > ThreadPrioLowest) { LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); @@ -299,7 +327,7 @@ ResultVal> KernelSystem::CreateThread(std::string name, // TODO(yuriks): Other checks, returning 0xD9001BEA - if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { + if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:08x}", name, entry_point); // TODO: Verify error return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, @@ -322,17 +350,17 @@ ResultVal> KernelSystem::CreateThread(std::string name, thread->wait_address = 0; thread->name = std::move(name); thread_managers[processor_id]->wakeup_callback_table[thread->thread_id] = thread.get(); - thread->owner_process = &owner_process; + thread->owner_process = owner_process; // Find the next available TLS index, and mark it as used - auto& tls_slots = owner_process.tls_slots; + auto& tls_slots = owner_process->tls_slots; auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots); if (needs_allocation) { // There are no already-allocated pages with free slots, lets allocate a new one. // TLS pages are allocated from the BASE region in the linear heap. - MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE); + auto memory_region = GetMemoryRegion(MemoryRegion::BASE); // Allocate some memory from the end of the linear heap for this region. auto offset = memory_region->LinearAllocate(Memory::PAGE_SIZE); @@ -341,17 +369,17 @@ ResultVal> KernelSystem::CreateThread(std::string name, "Not enough space in region to allocate a new TLS page for thread"); return ERR_OUT_OF_MEMORY; } - owner_process.memory_used += Memory::PAGE_SIZE; + owner_process->memory_used += Memory::PAGE_SIZE; tls_slots.emplace_back(0); // The page is completely available at the start available_page = tls_slots.size() - 1; available_slot = 0; // Use the first slot in the new page - auto& vm_manager = owner_process.vm_manager; + auto& vm_manager = owner_process->vm_manager; // Map the page to the current process' address space. vm_manager.MapBackingMemory(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - memory.GetFCRAMPointer(*offset), Memory::PAGE_SIZE, + memory.GetFCRAMRef(*offset), Memory::PAGE_SIZE, MemoryState::Locked); } @@ -360,7 +388,7 @@ ResultVal> KernelSystem::CreateThread(std::string name, thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; - memory.ZeroBlock(owner_process, thread->tls_address, Memory::TLS_ENTRY_SIZE); + memory.ZeroBlock(*owner_process, thread->tls_address, Memory::TLS_ENTRY_SIZE); // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used // to initialize the context @@ -407,7 +435,7 @@ std::shared_ptr SetupMainThread(KernelSystem& kernel, u32 entry_point, u // Initialize new "main" thread auto thread_res = kernel.CreateThread("main", entry_point, priority, 0, owner_process->ideal_processor, - Memory::HEAP_VADDR_END, *owner_process); + Memory::HEAP_VADDR_END, owner_process); std::shared_ptr thread = std::move(thread_res).Unwrap(); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index cd47448a25..233381f67f 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -9,6 +9,10 @@ #include #include #include +#include +#include +#include +#include #include "common/common_types.h" #include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" @@ -57,6 +61,20 @@ enum class ThreadWakeupReason { Timeout // The thread was woken up due to a wait timeout. }; +class Thread; + +class WakeupCallback { +public: + virtual ~WakeupCallback() = default; + virtual void WakeUp(ThreadWakeupReason reason, std::shared_ptr thread, + std::shared_ptr object) = 0; + +private: + template + void serialize(Archive& ar, const unsigned int) {} + friend class boost::serialization::access; +}; + class ThreadManager { public: explicit ThreadManager(Kernel::KernelSystem& kernel, u32 core_id); @@ -140,6 +158,15 @@ private: friend class Thread; friend class KernelSystem; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& current_thread; + ar& ready_queue; + ar& wakeup_callback_table; + ar& thread_list; + } }; class Thread final : public WaitObject { @@ -276,30 +303,34 @@ public: VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread /// Mutexes currently held by this thread, which will be released when it exits. - boost::container::flat_set> held_mutexes; + boost::container::flat_set> held_mutexes{}; /// Mutexes that this thread is currently waiting for. - boost::container::flat_set> pending_mutexes; + boost::container::flat_set> pending_mutexes{}; - Process* owner_process; ///< Process that owns this thread + std::shared_ptr owner_process{}; ///< Process that owns this thread /// Objects that the thread is waiting on, in the same order as they were // passed to WaitSynchronization1/N. - std::vector> wait_objects; + std::vector> wait_objects{}; VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address - std::string name; + std::string name{}; - using WakeupCallback = void(ThreadWakeupReason reason, std::shared_ptr thread, - std::shared_ptr object); // Callback that will be invoked when the thread is resumed from a waiting state. If the thread // was waiting via WaitSynchronizationN then the object will be the last object that became // available. In case of a timeout, the object will be nullptr. - std::function wakeup_callback; + std::shared_ptr wakeup_callback{}; + + const u32 core_id; private: ThreadManager& thread_manager; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); }; /** @@ -314,3 +345,22 @@ std::shared_ptr SetupMainThread(KernelSystem& kernel, u32 entry_point, u std::shared_ptr owner_process); } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::Thread) + +namespace boost::serialization { + +template +inline void save_construct_data(Archive& ar, const Kernel::Thread* t, + const unsigned int file_version) { + ar << t->core_id; +} + +template +inline void load_construct_data(Archive& ar, Kernel::Thread* t, const unsigned int file_version) { + u32 core_id; + ar >> core_id; + ::new (t) Kernel::Thread(Core::Global(), core_id); +} + +} // namespace boost::serialization diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 9c3d0f7250..d2a12ade1d 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -4,6 +4,7 @@ #include #include +#include "common/archives.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" @@ -12,6 +13,8 @@ #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" +SERIALIZE_EXPORT_IMPL(Kernel::Timer) + namespace Kernel { Timer::Timer(KernelSystem& kernel) diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index 6865f52437..d5af5e654d 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include "common/common_types.h" #include "core/core_timing.h" #include "core/hle/kernel/object.h" @@ -33,6 +35,13 @@ private: friend class Timer; friend class KernelSystem; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& next_timer_callback_id; + ar& timer_callback_table; + } }; class Timer final : public WaitObject { @@ -103,6 +112,21 @@ private: TimerManager& timer_manager; friend class KernelSystem; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& reset_type; + ar& initial_delay; + ar& interval_delay; + ar& signaled; + ar& name; + ar& callback_id; + } }; } // namespace Kernel + +BOOST_CLASS_EXPORT_KEY(Kernel::Timer) +CONSTRUCT_KERNEL_OBJECT(Kernel::Timer) diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index ba2e2bd1a0..83541b2b27 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -27,7 +27,8 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { type != next.type) { return false; } - if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) { + if (type == VMAType::BackingMemory && + backing_memory.GetPtr() + size != next.backing_memory.GetPtr()) { return false; } if (type == VMAType::MMIO && paddr + size != next.paddr) { @@ -36,7 +37,8 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { return true; } -VMManager::VMManager(Memory::MemorySystem& memory) : memory(memory) { +VMManager::VMManager(Memory::MemorySystem& memory) + : memory(memory), page_table(std::make_shared()) { Reset(); } @@ -50,8 +52,7 @@ void VMManager::Reset() { initial_vma.size = MAX_ADDRESS; vma_map.emplace(initial_vma.base, initial_vma); - page_table.pointers.fill(nullptr); - page_table.attributes.fill(Memory::PageType::Unmapped); + page_table->Clear(); UpdatePageTableForVMA(initial_vma); } @@ -64,7 +65,7 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { } } -ResultVal VMManager::MapBackingMemoryToBase(VAddr base, u32 region_size, u8* memory, +ResultVal VMManager::MapBackingMemoryToBase(VAddr base, u32 region_size, MemoryRef memory, u32 size, MemoryState state) { // Find the first Free VMA. @@ -93,9 +94,9 @@ ResultVal VMManager::MapBackingMemoryToBase(VAddr base, u32 region_size, return MakeResult(target); } -ResultVal VMManager::MapBackingMemory(VAddr target, u8* memory, u32 size, - MemoryState state) { - ASSERT(memory != nullptr); +ResultVal VMManager::MapBackingMemory(VAddr target, MemoryRef memory, + u32 size, MemoryState state) { + ASSERT(memory.GetPtr() != nullptr); // This is the appropriately sized VMA that will turn into our allocation. CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); @@ -351,20 +352,20 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { switch (vma.type) { case VMAType::Free: - memory.UnmapRegion(page_table, vma.base, vma.size); + memory.UnmapRegion(*page_table, vma.base, vma.size); break; case VMAType::BackingMemory: - memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); + memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: - memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); + memory.MapIoRegion(*page_table, vma.base, vma.size, vma.mmio_handler); break; } } -ResultVal>> VMManager::GetBackingBlocksForRange(VAddr address, - u32 size) { - std::vector> backing_blocks; +ResultVal>> VMManager::GetBackingBlocksForRange(VAddr address, + u32 size) { + std::vector> backing_blocks; VAddr interval_target = address; while (interval_target != address + size) { auto vma = FindVMA(interval_target); @@ -375,7 +376,7 @@ ResultVal>> VMManager::GetBackingBlocksForRange( VAddr interval_end = std::min(address + size, vma->second.base + vma->second.size); u32 interval_size = interval_end - interval_target; - u8* backing_memory = vma->second.backing_memory + (interval_target - vma->second.base); + auto backing_memory = vma->second.backing_memory + (interval_target - vma->second.base); backing_blocks.push_back({backing_memory, interval_size}); interval_target += interval_size; diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index fbd9bf09b3..06fbb8672b 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -8,7 +8,11 @@ #include #include #include +#include +#include +#include #include "common/common_types.h" +#include "common/memory_ref.h" #include "core/hle/result.h" #include "core/memory.h" #include "core/mmio.h" @@ -71,7 +75,7 @@ struct VirtualMemoryArea { // Settings for type = BackingMemory /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. - u8* backing_memory = nullptr; + MemoryRef backing_memory{}; // Settings for type = MMIO /// Physical address of the register area this VMA maps to. @@ -80,6 +84,20 @@ struct VirtualMemoryArea { /// Tests if this area can be merged to the right with `next`. bool CanBeMergedWith(const VirtualMemoryArea& next) const; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& base; + ar& size; + ar& type; + ar& permissions; + ar& meminfo_state; + ar& backing_memory; + ar& paddr; + ar& mmio_handler; + } }; /** @@ -134,7 +152,7 @@ public: * @param state MemoryState tag to attach to the VMA. * @returns The address at which the memory was mapped. */ - ResultVal MapBackingMemoryToBase(VAddr base, u32 region_size, u8* memory, u32 size, + ResultVal MapBackingMemoryToBase(VAddr base, u32 region_size, MemoryRef memory, u32 size, MemoryState state); /** * Maps an unmanaged host memory pointer at a given address. @@ -144,7 +162,8 @@ public: * @param size Size of the mapping. * @param state MemoryState tag to attach to the VMA. */ - ResultVal MapBackingMemory(VAddr target, u8* memory, u32 size, MemoryState state); + ResultVal MapBackingMemory(VAddr target, MemoryRef memory, u32 size, + MemoryState state); /** * Maps a memory-mapped IO region at a given address. @@ -186,11 +205,12 @@ public: void LogLayout(Log::Level log_level) const; /// Gets a list of backing memory blocks for the specified range - ResultVal>> GetBackingBlocksForRange(VAddr address, u32 size); + ResultVal>> GetBackingBlocksForRange(VAddr address, + u32 size); /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. - Memory::PageTable page_table; + std::shared_ptr page_table; private: using VMAIter = decltype(vma_map)::iterator; @@ -229,5 +249,12 @@ private: void UpdatePageTableForVMA(const VirtualMemoryArea& vma); Memory::MemorySystem& memory; + + template + void serialize(Archive& ar, const unsigned int) { + ar& vma_map; + ar& page_table; + } + friend class boost::serialization::access; }; } // namespace Kernel diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 94ae632cde..2cd4331c5d 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -4,6 +4,7 @@ #include #include +#include "common/archives.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/kernel/errors.h" @@ -16,6 +17,15 @@ namespace Kernel { +template +void WaitObject::serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& waiting_threads; + // NB: hle_notifier *not* serialized since it's a callback! + // Fortunately it's only used in one place (DSP) so we can reconstruct it there +} +SERIALIZE_IMPL(WaitObject) + void WaitObject::AddWaitingThread(std::shared_ptr thread) { auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); if (itr == waiting_threads.end()) @@ -80,7 +90,7 @@ void WaitObject::WakeupAllWaitingThreads() { // Invoke the wakeup callback before clearing the wait objects if (thread->wakeup_callback) - thread->wakeup_callback(ThreadWakeupReason::Signal, thread, SharedFrom(this)); + thread->wakeup_callback->WakeUp(ThreadWakeupReason::Signal, thread, SharedFrom(this)); for (auto& object : thread->wait_objects) object->RemoveWaitingThread(thread.get()); diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 41e8035159..7f73eab9f5 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include "common/common_types.h" #include "core/hle/kernel/object.h" @@ -62,6 +65,11 @@ private: /// Function to call when this object becomes available std::function hle_notifier; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); }; // Specialization of DynamicObjectCast for WaitObjects diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 1543d7bd82..40a71e369c 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/assert.h" #include "common/bit_field.h" #include "common/common_funcs.h" @@ -225,6 +226,13 @@ union ResultCode { constexpr bool IsError() const { return is_error.ExtractValue(raw) == 1; } + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& raw; + } + friend class boost::serialization::access; }; constexpr bool operator==(const ResultCode& a, const ResultCode& b) { diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp index 9d40b9661b..d9f1085c81 100644 --- a/src/core/hle/service/ac/ac.cpp +++ b/src/core/hle/service/ac/ac.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" @@ -179,4 +180,15 @@ void InstallInterfaces(Core::System& system) { std::make_shared(ac)->InstallAsService(service_manager); } +template +void Module::serialize(Archive& ar, const unsigned int) { + ar& ac_connected; + ar& close_event; + ar& connect_event; + ar& disconnect_event; + // default_config is never written to +} + } // namespace Service::AC + +SERIALIZE_IMPL(Service::AC::Module) diff --git a/src/core/hle/service/ac/ac.h b/src/core/hle/service/ac/ac.h index f3554c876d..e4342bde93 100644 --- a/src/core/hle/service/ac/ac.h +++ b/src/core/hle/service/ac/ac.h @@ -153,6 +153,11 @@ protected: std::shared_ptr close_event; std::shared_ptr connect_event; std::shared_ptr disconnect_event; + +private: + template + void serialize(Archive& ar, const unsigned int file_version); + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); diff --git a/src/core/hle/service/ac/ac_i.cpp b/src/core/hle/service/ac/ac_i.cpp index 0dde7bf90b..ff4fb954cb 100644 --- a/src/core/hle/service/ac/ac_i.cpp +++ b/src/core/hle/service/ac/ac_i.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ac/ac_i.h" namespace Service::AC { @@ -33,3 +34,5 @@ AC_I::AC_I(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:i" } } // namespace Service::AC + +SERIALIZE_EXPORT_IMPL(Service::AC::AC_I) diff --git a/src/core/hle/service/ac/ac_i.h b/src/core/hle/service/ac/ac_i.h index bca91aabe3..8e852fb9b2 100644 --- a/src/core/hle/service/ac/ac_i.h +++ b/src/core/hle/service/ac/ac_i.h @@ -12,6 +12,12 @@ namespace Service::AC { class AC_I final : public Module::Interface { public: explicit AC_I(std::shared_ptr ac); + +private: + SERVICE_SERIALIZATION(AC_I, ac, Module) }; } // namespace Service::AC + +BOOST_CLASS_EXPORT_KEY(Service::AC::AC_I) +BOOST_SERIALIZATION_CONSTRUCT(Service::AC::AC_I) diff --git a/src/core/hle/service/ac/ac_u.cpp b/src/core/hle/service/ac/ac_u.cpp index d62d7ccb6c..e88cdd1640 100644 --- a/src/core/hle/service/ac/ac_u.cpp +++ b/src/core/hle/service/ac/ac_u.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ac/ac_u.h" namespace Service::AC { @@ -33,3 +34,5 @@ AC_U::AC_U(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:u" } } // namespace Service::AC + +SERIALIZE_EXPORT_IMPL(Service::AC::AC_U) diff --git a/src/core/hle/service/ac/ac_u.h b/src/core/hle/service/ac/ac_u.h index 18efcd1e68..117269f1f6 100644 --- a/src/core/hle/service/ac/ac_u.h +++ b/src/core/hle/service/ac/ac_u.h @@ -12,6 +12,12 @@ namespace Service::AC { class AC_U final : public Module::Interface { public: explicit AC_U(std::shared_ptr ac); + +private: + SERVICE_SERIALIZATION(AC_U, ac, Module) }; } // namespace Service::AC + +BOOST_CLASS_EXPORT_KEY(Service::AC::AC_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::AC::AC_U) diff --git a/src/core/hle/service/act/act.h b/src/core/hle/service/act/act.h index 8846788906..e5c2cf7aed 100644 --- a/src/core/hle/service/act/act.h +++ b/src/core/hle/service/act/act.h @@ -20,9 +20,14 @@ public: Interface(std::shared_ptr act, const char* name); ~Interface(); - private: + protected: std::shared_ptr act; }; + +private: + template + void serialize(Archive& ar, const unsigned int file_version) {} + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); diff --git a/src/core/hle/service/act/act_a.cpp b/src/core/hle/service/act/act_a.cpp index 7a33f9175d..b85a17183f 100644 --- a/src/core/hle/service/act/act_a.cpp +++ b/src/core/hle/service/act/act_a.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/act/act_a.h" namespace Service::ACT { @@ -24,3 +25,5 @@ ACT_A::ACT_A(std::shared_ptr act) : Module::Interface(std::move(act), "a } } // namespace Service::ACT + +SERIALIZE_EXPORT_IMPL(Service::ACT::ACT_A) diff --git a/src/core/hle/service/act/act_a.h b/src/core/hle/service/act/act_a.h index 48a79aab7e..1454441d5f 100644 --- a/src/core/hle/service/act/act_a.h +++ b/src/core/hle/service/act/act_a.h @@ -11,6 +11,12 @@ namespace Service::ACT { class ACT_A final : public Module::Interface { public: explicit ACT_A(std::shared_ptr act); + +private: + SERVICE_SERIALIZATION(ACT_A, act, Module) }; } // namespace Service::ACT + +BOOST_CLASS_EXPORT_KEY(Service::ACT::ACT_A) +BOOST_SERIALIZATION_CONSTRUCT(Service::ACT::ACT_A) diff --git a/src/core/hle/service/act/act_u.cpp b/src/core/hle/service/act/act_u.cpp index 99978d9cab..a0058d5734 100644 --- a/src/core/hle/service/act/act_u.cpp +++ b/src/core/hle/service/act/act_u.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/act/act_u.h" namespace Service::ACT { @@ -20,3 +21,5 @@ ACT_U::ACT_U(std::shared_ptr act) : Module::Interface(std::move(act), "a } } // namespace Service::ACT + +SERIALIZE_EXPORT_IMPL(Service::ACT::ACT_U) diff --git a/src/core/hle/service/act/act_u.h b/src/core/hle/service/act/act_u.h index 3aca428e63..bcd2d653e9 100644 --- a/src/core/hle/service/act/act_u.h +++ b/src/core/hle/service/act/act_u.h @@ -11,6 +11,12 @@ namespace Service::ACT { class ACT_U final : public Module::Interface { public: explicit ACT_U(std::shared_ptr act); + +private: + SERVICE_SERIALIZATION(ACT_U, act, Module) }; } // namespace Service::ACT + +BOOST_CLASS_EXPORT_KEY(Service::ACT::ACT_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::ACT::ACT_U) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 31df828e30..240762d25a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1053,7 +1053,7 @@ void Module::Interface::BeginImportProgram(Kernel::HLERequestContext& ctx) { // Citra will store contents out to sdmc/nand const FileSys::Path cia_path = {}; auto file = std::make_shared( - am->system, std::make_unique(media_type), cia_path); + am->kernel, std::make_unique(media_type), cia_path); am->cia_installing = true; @@ -1080,7 +1080,7 @@ void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext& // contents out to sdmc/nand const FileSys::Path cia_path = {}; auto file = std::make_shared( - am->system, std::make_unique(FS::MediaType::NAND), cia_path); + am->kernel, std::make_unique(FS::MediaType::NAND), cia_path); am->cia_installing = true; @@ -1482,11 +1482,13 @@ void Module::Interface::GetMetaDataFromCia(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(output_buffer); } -Module::Module(Core::System& system) : system(system) { +Module::Module(Core::System& system) : kernel(system.Kernel()) { ScanForAllTitles(); system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex"); } +Module::Module(Kernel::KernelSystem& kernel) : kernel(kernel) {} + Module::~Module() = default; void InstallInterfaces(Core::System& system) { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 0912dde409..25bd582650 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -9,9 +9,14 @@ #include #include #include +#include +#include +#include #include "common/common_types.h" +#include "common/construct.h" #include "core/file_sys/cia_container.h" #include "core/file_sys/file_backend.h" +#include "core/global.h" #include "core/hle/kernel/mutex.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -557,11 +562,13 @@ public: */ void GetMetaDataFromCia(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr am; }; private: + explicit Module(Kernel::KernelSystem& kernel); + /** * Scans the for titles in a storage medium for listing. * @param media_type the storage medium to scan @@ -573,12 +580,32 @@ private: */ void ScanForAllTitles(); - Core::System& system; + Kernel::KernelSystem& kernel; bool cia_installing = false; std::array, 3> am_title_list; std::shared_ptr system_updater_mutex; + + template + void serialize(Archive& ar, const unsigned int) { + ar& cia_installing; + ar& am_title_list; + ar& system_updater_mutex; + } + + template + static void load_construct(Archive& ar, Module* t, const unsigned int file_version) { + ::new (t) Module(Core::Global()); + } + + template + void save_construct(Archive& ar, const unsigned int file_version) const {} + + friend class ::construct_access; + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::AM + +BOOST_SERIALIZATION_CONSTRUCT(Service::AM::Module); diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp index cee1aa81b6..788ee090c2 100644 --- a/src/core/hle/service/am/am_app.cpp +++ b/src/core/hle/service/am/am_app.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/am/am_app.h" namespace Service::AM { @@ -26,3 +27,5 @@ AM_APP::AM_APP(std::shared_ptr am) : Module::Interface(std::move(am), "a } } // namespace Service::AM + +SERIALIZE_EXPORT_IMPL(Service::AM::AM_APP) diff --git a/src/core/hle/service/am/am_app.h b/src/core/hle/service/am/am_app.h index 67cf8ba2e6..b8e858cb48 100644 --- a/src/core/hle/service/am/am_app.h +++ b/src/core/hle/service/am/am_app.h @@ -11,6 +11,12 @@ namespace Service::AM { class AM_APP final : public Module::Interface { public: explicit AM_APP(std::shared_ptr am); + +private: + SERVICE_SERIALIZATION(AM_APP, am, Module) }; } // namespace Service::AM + +BOOST_CLASS_EXPORT_KEY(Service::AM::AM_APP) +BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_APP) diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp index 120ee53e78..cd88965afc 100644 --- a/src/core/hle/service/am/am_net.cpp +++ b/src/core/hle/service/am/am_net.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/am/am_net.h" namespace Service::AM { @@ -123,3 +124,5 @@ AM_NET::AM_NET(std::shared_ptr am) : Module::Interface(std::move(am), "a } } // namespace Service::AM + +SERIALIZE_EXPORT_IMPL(Service::AM::AM_NET) diff --git a/src/core/hle/service/am/am_net.h b/src/core/hle/service/am/am_net.h index a5adbd7e57..b73610df9e 100644 --- a/src/core/hle/service/am/am_net.h +++ b/src/core/hle/service/am/am_net.h @@ -11,6 +11,12 @@ namespace Service::AM { class AM_NET final : public Module::Interface { public: explicit AM_NET(std::shared_ptr am); + +private: + SERVICE_SERIALIZATION(AM_NET, am, Module) }; } // namespace Service::AM + +BOOST_CLASS_EXPORT_KEY(Service::AM::AM_NET) +BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_NET) diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp index ca4affbc22..6c7e992350 100644 --- a/src/core/hle/service/am/am_sys.cpp +++ b/src/core/hle/service/am/am_sys.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/am/am_sys.h" namespace Service::AM { @@ -71,3 +72,5 @@ AM_SYS::AM_SYS(std::shared_ptr am) : Module::Interface(std::move(am), "a } } // namespace Service::AM + +SERIALIZE_EXPORT_IMPL(Service::AM::AM_SYS) diff --git a/src/core/hle/service/am/am_sys.h b/src/core/hle/service/am/am_sys.h index b142916ca9..fdee63b134 100644 --- a/src/core/hle/service/am/am_sys.h +++ b/src/core/hle/service/am/am_sys.h @@ -11,6 +11,12 @@ namespace Service::AM { class AM_SYS final : public Module::Interface { public: explicit AM_SYS(std::shared_ptr am); + +private: + SERVICE_SERIALIZATION(AM_SYS, am, Module) }; } // namespace Service::AM + +BOOST_CLASS_EXPORT_KEY(Service::AM::AM_SYS) +BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_SYS) diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp index 840860ec0b..cffb883886 100644 --- a/src/core/hle/service/am/am_u.cpp +++ b/src/core/hle/service/am/am_u.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/am/am_u.h" namespace Service::AM { @@ -83,3 +84,5 @@ AM_U::AM_U(std::shared_ptr am) : Module::Interface(std::move(am), "am:u" } } // namespace Service::AM + +SERIALIZE_EXPORT_IMPL(Service::AM::AM_U) diff --git a/src/core/hle/service/am/am_u.h b/src/core/hle/service/am/am_u.h index 1d732c90ae..d71bf722c0 100644 --- a/src/core/hle/service/am/am_u.h +++ b/src/core/hle/service/am/am_u.h @@ -11,6 +11,12 @@ namespace Service::AM { class AM_U final : public Module::Interface { public: explicit AM_U(std::shared_ptr am); + +private: + SERVICE_SERIALIZATION(AM_U, am, Module) }; } // namespace Service::AM + +BOOST_CLASS_EXPORT_KEY(Service::AM::AM_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::AM::AM_U) diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 8a24595ff2..6612a720e4 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -10,6 +10,8 @@ #include "core/hle/service/apt/ns.h" #include "core/hle/service/cfg/cfg.h" +SERVICE_CONSTRUCT_IMPL(Service::APT::AppletManager) + namespace Service::APT { enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 }; diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index 6bc880bd46..0f1a46a8b9 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -6,8 +6,12 @@ #include #include -#include #include +#include +#include +#include +#include +#include "core/global.h" #include "core/hle/kernel/event.h" #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" @@ -84,6 +88,17 @@ struct MessageParameter { SignalType signal = SignalType::None; std::shared_ptr object = nullptr; std::vector buffer; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& sender_id; + ar& destination_id; + ar& signal; + ar& object; + ar& buffer; + } + friend class boost::serialization::access; }; /// Holds information about the parameters used in StartLibraryApplet @@ -161,6 +176,16 @@ public: u64 current_title_id; FS::MediaType current_media_type; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& next_title_id; + ar& next_media_type; + ar& current_title_id; + ar& current_media_type; + } + friend class boost::serialization::access; }; ApplicationJumpParameters GetApplicationJumpParameters() const { @@ -169,7 +194,8 @@ public: private: /// Parameter data to be returned in the next call to Glance/ReceiveParameter. - std::optional next_parameter; + // NOTE: A bug in gcc prevents serializing std::optional + boost::optional next_parameter; static constexpr std::size_t NumAppletSlot = 4; @@ -199,6 +225,20 @@ private: title_id = 0; attributes.raw = 0; } + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& applet_id; + ar& slot; + ar& title_id; + ar& registered; + ar& loaded; + ar& attributes.raw; + ar& notification_event; + ar& parameter_event; + } + friend class boost::serialization::access; }; ApplicationJumpParameters app_jump_parameters{}; @@ -216,6 +256,18 @@ private: SignalType library_applet_closing_command; Core::System& system; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& next_parameter; + ar& app_jump_parameters; + ar& applet_slots; + ar& library_applet_closing_command; + } + friend class boost::serialization::access; }; } // namespace Service::APT + +SERVICE_CONSTRUCT(Service::APT::AppletManager) diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index f7fb0660fe..d93b4bcb25 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include "common/archives.h" #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -26,8 +29,25 @@ #include "core/hw/aes/ccm.h" #include "core/hw/aes/key.h" +SERVICE_CONSTRUCT_IMPL(Service::APT::Module) + namespace Service::APT { +template +void Module::serialize(Archive& ar, const unsigned int) { + ar& shared_font_mem; + ar& shared_font_loaded; + ar& shared_font_relocated; + ar& lock; + ar& cpu_percent; + ar& unknown_ns_state_field; + ar& screen_capture_buffer; + ar& screen_capture_post_permission; + ar& applet_manager; +} + +SERIALIZE_IMPL(Module) + Module::NSInterface::NSInterface(std::shared_ptr apt, const char* name, u32 max_session) : ServiceFramework(name, max_session), apt(std::move(apt)) {} diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 2e2c219cd4..8b01d3b8eb 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -6,9 +6,11 @@ #include #include +#include "common/archives.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/global.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/service.h" @@ -65,7 +67,7 @@ public: NSInterface(std::shared_ptr apt, const char* name, u32 max_session); ~NSInterface(); - private: + protected: std::shared_ptr apt; }; @@ -601,9 +603,16 @@ public: */ void CheckNew3DS(Kernel::HLERequestContext& ctx); - private: + protected: bool application_reset_prepared{}; std::shared_ptr apt; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& application_reset_prepared; + } + friend class boost::serialization::access; }; private: @@ -630,8 +639,14 @@ private: ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value std::shared_ptr applet_manager; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::APT + +SERVICE_CONSTRUCT(Service::APT::Module) diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 7d5c96139e..0d6e64d301 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/apt/apt_a.h" namespace Service::APT { @@ -105,3 +106,5 @@ APT_A::APT_A(std::shared_ptr apt) } } // namespace Service::APT + +SERIALIZE_EXPORT_IMPL(Service::APT::APT_A) diff --git a/src/core/hle/service/apt/apt_a.h b/src/core/hle/service/apt/apt_a.h index f481fa1c90..e17e2f3234 100644 --- a/src/core/hle/service/apt/apt_a.h +++ b/src/core/hle/service/apt/apt_a.h @@ -11,6 +11,12 @@ namespace Service::APT { class APT_A final : public Module::APTInterface { public: explicit APT_A(std::shared_ptr apt); + +private: + SERVICE_SERIALIZATION(APT_A, apt, Module) }; } // namespace Service::APT + +BOOST_CLASS_EXPORT_KEY(Service::APT::APT_A) +BOOST_SERIALIZATION_CONSTRUCT(Service::APT::APT_A) diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 1843bdd9aa..d0e5f55266 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/apt/apt_s.h" namespace Service::APT { @@ -105,3 +106,5 @@ APT_S::APT_S(std::shared_ptr apt) } } // namespace Service::APT + +SERIALIZE_EXPORT_IMPL(Service::APT::APT_S) diff --git a/src/core/hle/service/apt/apt_s.h b/src/core/hle/service/apt/apt_s.h index 7e041cbda2..d1dd27ff14 100644 --- a/src/core/hle/service/apt/apt_s.h +++ b/src/core/hle/service/apt/apt_s.h @@ -18,6 +18,12 @@ namespace Service::APT { class APT_S final : public Module::APTInterface { public: explicit APT_S(std::shared_ptr apt); + +private: + SERVICE_SERIALIZATION(APT_S, apt, Module) }; } // namespace Service::APT + +BOOST_CLASS_EXPORT_KEY(Service::APT::APT_S) +BOOST_SERIALIZATION_CONSTRUCT(Service::APT::APT_S) diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 0ecbd65da4..05f531a833 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/apt/apt_u.h" namespace Service::APT { @@ -102,3 +103,5 @@ APT_U::APT_U(std::shared_ptr apt) } } // namespace Service::APT + +SERIALIZE_EXPORT_IMPL(Service::APT::APT_U) diff --git a/src/core/hle/service/apt/apt_u.h b/src/core/hle/service/apt/apt_u.h index 3b342ed555..b8a8fe205a 100644 --- a/src/core/hle/service/apt/apt_u.h +++ b/src/core/hle/service/apt/apt_u.h @@ -18,6 +18,12 @@ namespace Service::APT { class APT_U final : public Module::APTInterface { public: explicit APT_U(std::shared_ptr apt); + +private: + SERVICE_SERIALIZATION(APT_U, apt, Module) }; } // namespace Service::APT + +BOOST_CLASS_EXPORT_KEY(Service::APT::APT_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::APT::APT_U) diff --git a/src/core/hle/service/apt/ns_s.cpp b/src/core/hle/service/apt/ns_s.cpp index e9eb871744..1f86b9a61c 100644 --- a/src/core/hle/service/apt/ns_s.cpp +++ b/src/core/hle/service/apt/ns_s.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/apt/ns_s.h" namespace Service::NS { @@ -29,3 +30,5 @@ NS_S::NS_S(std::shared_ptr apt) } } // namespace Service::NS + +SERIALIZE_EXPORT_IMPL(Service::NS::NS_S) diff --git a/src/core/hle/service/apt/ns_s.h b/src/core/hle/service/apt/ns_s.h index 5a5b311b06..eaa0ad9d37 100644 --- a/src/core/hle/service/apt/ns_s.h +++ b/src/core/hle/service/apt/ns_s.h @@ -14,6 +14,12 @@ namespace Service::NS { class NS_S final : public Service::APT::Module::NSInterface { public: explicit NS_S(std::shared_ptr apt); + +private: + SERVICE_SERIALIZATION(NS_S, apt, Service::APT::Module) }; } // namespace Service::NS + +BOOST_CLASS_EXPORT_KEY(Service::NS::NS_S) +BOOST_SERIALIZATION_CONSTRUCT(Service::NS::NS_S) diff --git a/src/core/hle/service/boss/boss.h b/src/core/hle/service/boss/boss.h index e0cb39b372..e4000e8515 100644 --- a/src/core/hle/service/boss/boss.h +++ b/src/core/hle/service/boss/boss.h @@ -5,6 +5,8 @@ #pragma once #include +#include +#include "core/global.h" #include "core/hle/kernel/event.h" #include "core/hle/service/service.h" @@ -952,19 +954,42 @@ public: */ void GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr boss; + private: u8 new_arrival_flag; u8 ns_data_new_flag; u8 ns_data_new_flag_privileged; u8 output_flag; + + template + void serialize(Archive& ar, const unsigned int) { + ar& new_arrival_flag; + ar& ns_data_new_flag; + ar& ns_data_new_flag_privileged; + ar& output_flag; + } + friend class boost::serialization::access; }; private: std::shared_ptr task_finish_event; + + template + void serialize(Archive& ar, const unsigned int) { + ar& task_finish_event; + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::BOSS + +namespace boost::serialization { +template +void load_construct_data(Archive& ar, Service::BOSS::Module* t, const unsigned int) { + ::new (t) Service::BOSS::Module(Core::Global()); +} +} // namespace boost::serialization diff --git a/src/core/hle/service/boss/boss_p.cpp b/src/core/hle/service/boss/boss_p.cpp index cdc8f10364..802f2643a4 100644 --- a/src/core/hle/service/boss/boss_p.cpp +++ b/src/core/hle/service/boss/boss_p.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/boss/boss_p.h" namespace Service::BOSS { @@ -84,3 +85,5 @@ BOSS_P::BOSS_P(std::shared_ptr boss) } } // namespace Service::BOSS + +SERIALIZE_EXPORT_IMPL(Service::BOSS::BOSS_P) diff --git a/src/core/hle/service/boss/boss_p.h b/src/core/hle/service/boss/boss_p.h index 9c84a1e9d2..56f0cd4fcf 100644 --- a/src/core/hle/service/boss/boss_p.h +++ b/src/core/hle/service/boss/boss_p.h @@ -11,6 +11,12 @@ namespace Service::BOSS { class BOSS_P final : public Module::Interface { public: explicit BOSS_P(std::shared_ptr boss); + +private: + SERVICE_SERIALIZATION(BOSS_P, boss, Module) }; } // namespace Service::BOSS + +BOOST_CLASS_EXPORT_KEY(Service::BOSS::BOSS_P) +BOOST_SERIALIZATION_CONSTRUCT(Service::BOSS::BOSS_P) diff --git a/src/core/hle/service/boss/boss_u.cpp b/src/core/hle/service/boss/boss_u.cpp index c6aaba8888..68a30510d4 100644 --- a/src/core/hle/service/boss/boss_u.cpp +++ b/src/core/hle/service/boss/boss_u.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/boss/boss_u.h" namespace Service::BOSS { @@ -72,3 +73,5 @@ BOSS_U::BOSS_U(std::shared_ptr boss) } } // namespace Service::BOSS + +SERIALIZE_EXPORT_IMPL(Service::BOSS::BOSS_U) diff --git a/src/core/hle/service/boss/boss_u.h b/src/core/hle/service/boss/boss_u.h index a93b4e5024..195783b406 100644 --- a/src/core/hle/service/boss/boss_u.h +++ b/src/core/hle/service/boss/boss_u.h @@ -11,6 +11,12 @@ namespace Service::BOSS { class BOSS_U final : public Module::Interface { public: explicit BOSS_U(std::shared_ptr boss); + +private: + SERVICE_SERIALIZATION(BOSS_U, boss, Module) }; } // namespace Service::BOSS + +BOOST_CLASS_EXPORT_KEY(Service::BOSS::BOSS_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::BOSS::BOSS_U) diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index c5d24c3144..c093ed7e55 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/bit_set.h" #include "common/logging/log.h" #include "core/core.h" @@ -20,8 +21,32 @@ #include "core/memory.h" #include "core/settings.h" +SERVICE_CONSTRUCT_IMPL(Service::CAM::Module) + namespace Service::CAM { +template +void Module::serialize(Archive& ar, const unsigned int) { + ar& cameras; + ar& ports; + ar& is_camera_reload_pending; + if (Archive::is_loading::value) { + for (int i = 0; i < NumCameras; i++) { + LoadCameraImplementation(cameras[i], i); + } + for (std::size_t i = 0; i < ports.size(); i++) { + if (ports[i].is_busy) { + cameras[ports[i].camera_id].impl->StartCapture(); + } + if (ports[i].is_receiving) { + StartReceiving(static_cast(i)); + } + } + } +} + +SERIALIZE_IMPL(Module) + // built-in resolution parameters constexpr std::array PRESET_RESOLUTION{{ {640, 480, 0, 0, 639, 479}, // VGA diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index 989a3a6a74..7c36e02c5a 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -9,8 +9,14 @@ #include #include #include +#include +#include +#include +#include +#include #include "common/common_types.h" #include "common/swap.h" +#include "core/global.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -180,6 +186,18 @@ struct Resolution { u16 crop_y0; u16 crop_x1; u16 crop_y1; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& width; + ar& height; + ar& crop_x0; + ar& crop_y0; + ar& crop_x1; + ar& crop_y1; + } + friend class boost::serialization::access; }; struct PackageParameterWithoutContext { @@ -726,7 +744,7 @@ public: */ void DriverFinalize(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr cam; }; @@ -755,6 +773,16 @@ private: Effect effect{Effect::None}; OutputFormat format{OutputFormat::YUV422}; Resolution resolution = {0, 0, 0, 0, 0, 0}; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& flip; + ar& effect; + ar& format; + ar& resolution; + } + friend class boost::serialization::access; }; struct CameraConfig { @@ -762,6 +790,20 @@ private: std::array contexts; int current_context{0}; FrameRate frame_rate{FrameRate::Rate_15}; + + private: + template + void serialize(Archive& ar, const unsigned int file_version) { + // For compatibility: put a nullptr here + if (file_version == 0) { + std::unique_ptr x; + ar& x; + } + ar& contexts; + ar& current_context; + ar& frame_rate; + } + friend class boost::serialization::access; }; struct PortConfig { @@ -798,6 +840,31 @@ private: u32 dest_size{0}; // the destination size of the receiving process void Clear(); + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& camera_id; + ar& is_active; + ar& is_pending_receiving; + ar& is_busy; + ar& is_receiving; + ar& is_trimming; + ar& x0; + ar& y0; + ar& x1; + ar& y1; + ar& transfer_bytes; + ar& completion_event; + ar& buffer_error_interrupt_event; + ar& vsync_interrupt_event; + ar& vsync_timings; + // Ignore capture_result. In-progress captures might be affected but this is OK. + ar& dest_process; + ar& dest; + ar& dest_size; + } + friend class boost::serialization::access; }; void LoadCameraImplementation(CameraConfig& camera, int camera_id); @@ -808,6 +875,10 @@ private: Core::TimingEventType* completion_event_callback; Core::TimingEventType* vsync_interrupt_event_callback; std::atomic is_camera_reload_pending{false}; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; std::shared_ptr GetModule(Core::System& system); @@ -815,3 +886,6 @@ std::shared_ptr GetModule(Core::System& system); void InstallInterfaces(Core::System& system); } // namespace Service::CAM + +SERVICE_CONSTRUCT(Service::CAM::Module) +BOOST_CLASS_VERSION(Service::CAM::Module::CameraConfig, 1) diff --git a/src/core/hle/service/cam/cam_c.cpp b/src/core/hle/service/cam/cam_c.cpp index e5aca5361a..10d04168e3 100644 --- a/src/core/hle/service/cam/cam_c.cpp +++ b/src/core/hle/service/cam/cam_c.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_c.h" @@ -79,3 +80,5 @@ CAM_C::CAM_C(std::shared_ptr cam) : Module::Interface(std::move(cam), "c } } // namespace Service::CAM + +SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_C) diff --git a/src/core/hle/service/cam/cam_c.h b/src/core/hle/service/cam/cam_c.h index d6dfcd6c58..c1ed355ac4 100644 --- a/src/core/hle/service/cam/cam_c.h +++ b/src/core/hle/service/cam/cam_c.h @@ -11,6 +11,12 @@ namespace Service::CAM { class CAM_C final : public Module::Interface { public: explicit CAM_C(std::shared_ptr cam); + +private: + SERVICE_SERIALIZATION(CAM_C, cam, Module) }; } // namespace Service::CAM + +BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_C) +BOOST_SERIALIZATION_CONSTRUCT(Service::CAM::CAM_C) diff --git a/src/core/hle/service/cam/cam_q.cpp b/src/core/hle/service/cam/cam_q.cpp index 71fc127d22..6f0c04598a 100644 --- a/src/core/hle/service/cam/cam_q.cpp +++ b/src/core/hle/service/cam/cam_q.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cam/cam_q.h" namespace Service::CAM { @@ -13,3 +14,5 @@ CAM_Q::CAM_Q() : ServiceFramework("cam:q", 1 /*TODO: find the true value*/) { } } // namespace Service::CAM + +SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_Q) diff --git a/src/core/hle/service/cam/cam_q.h b/src/core/hle/service/cam/cam_q.h index d1124493b3..992b383905 100644 --- a/src/core/hle/service/cam/cam_q.h +++ b/src/core/hle/service/cam/cam_q.h @@ -11,6 +11,11 @@ namespace Service::CAM { class CAM_Q : public ServiceFramework { public: CAM_Q(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::CAM + +BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_Q) diff --git a/src/core/hle/service/cam/cam_s.cpp b/src/core/hle/service/cam/cam_s.cpp index 606c8d3af7..771f0714c2 100644 --- a/src/core/hle/service/cam/cam_s.cpp +++ b/src/core/hle/service/cam/cam_s.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_s.h" @@ -79,3 +80,5 @@ CAM_S::CAM_S(std::shared_ptr cam) : Module::Interface(std::move(cam), "c } } // namespace Service::CAM + +SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_S) diff --git a/src/core/hle/service/cam/cam_s.h b/src/core/hle/service/cam/cam_s.h index 0c9d266447..cceb99b87f 100644 --- a/src/core/hle/service/cam/cam_s.h +++ b/src/core/hle/service/cam/cam_s.h @@ -11,6 +11,12 @@ namespace Service::CAM { class CAM_S final : public Module::Interface { public: explicit CAM_S(std::shared_ptr cam); + +private: + SERVICE_SERIALIZATION(CAM_S, cam, Module) }; } // namespace Service::CAM + +BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_S) +BOOST_SERIALIZATION_CONSTRUCT(Service::CAM::CAM_S) diff --git a/src/core/hle/service/cam/cam_u.cpp b/src/core/hle/service/cam/cam_u.cpp index 83cace5a70..30e64e0f84 100644 --- a/src/core/hle/service/cam/cam_u.cpp +++ b/src/core/hle/service/cam/cam_u.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_u.h" @@ -79,3 +80,5 @@ CAM_U::CAM_U(std::shared_ptr cam) : Module::Interface(std::move(cam), "c } } // namespace Service::CAM + +SERIALIZE_EXPORT_IMPL(Service::CAM::CAM_U) diff --git a/src/core/hle/service/cam/cam_u.h b/src/core/hle/service/cam/cam_u.h index 85b12559af..2b775a0352 100644 --- a/src/core/hle/service/cam/cam_u.h +++ b/src/core/hle/service/cam/cam_u.h @@ -11,6 +11,12 @@ namespace Service::CAM { class CAM_U final : public Module::Interface { public: explicit CAM_U(std::shared_ptr cam); + +private: + SERVICE_SERIALIZATION(CAM_U, cam, Module) }; } // namespace Service::CAM + +BOOST_CLASS_EXPORT_KEY(Service::CAM::CAM_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::CAM::CAM_U) diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index 281f480262..977a478421 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include #include #include #include +#include "common/archives.h" #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -24,8 +27,20 @@ #include "core/hle/service/cfg/cfg.h" #include "fmt/format.h" +SERVICE_CONSTRUCT_IMPL(Service::CECD::Module) +SERIALIZE_EXPORT_IMPL(Service::CECD::Module) +SERIALIZE_EXPORT_IMPL(Service::CECD::Module::SessionData) + namespace Service::CECD { +template +void Module::serialize(Archive& ar, const unsigned int) { + ar& cecd_system_save_data_archive; + ar& cecinfo_event; + ar& change_state_event; +} +SERIALIZE_IMPL(Module) + using CecDataPathType = Module::CecDataPathType; using CecOpenMode = Module::CecOpenMode; using CecSystemInfoType = Module::CecSystemInfoType; @@ -93,7 +108,7 @@ void Module::Interface::Open(Kernel::HLERequestContext& ctx) { } else { session_data->file = std::move(file_result).Unwrap(); rb.Push(RESULT_SUCCESS); - rb.Push(session_data->file->GetSize()); // Return file size + rb.Push(static_cast(session_data->file->GetSize())); // Return file size } if (path_type == CecDataPathType::MboxProgramId) { @@ -141,8 +156,8 @@ void Module::Interface::Read(Kernel::HLERequestContext& ctx) { break; default: // If not directory, then it is a file std::vector buffer(write_buffer_size); - const u32 bytes_read = - session_data->file->Read(0, write_buffer_size, buffer.data()).Unwrap(); + const u32 bytes_read = static_cast( + session_data->file->Read(0, write_buffer_size, buffer.data()).Unwrap()); write_buffer.Write(buffer.data(), 0, write_buffer_size); session_data->file->Close(); @@ -184,7 +199,8 @@ void Module::Interface::ReadMessage(Kernel::HLERequestContext& ctx) { auto message = std::move(message_result).Unwrap(); std::vector buffer(buffer_size); - const u32 bytes_read = message->Read(0, buffer_size, buffer.data()).Unwrap(); + const u32 bytes_read = + static_cast(message->Read(0, buffer_size, buffer.data()).Unwrap()); write_buffer.Write(buffer.data(), 0, buffer_size); message->Close(); @@ -253,7 +269,8 @@ void Module::Interface::ReadMessageWithHMAC(Kernel::HLERequestContext& ctx) { auto message = std::move(message_result).Unwrap(); std::vector buffer(buffer_size); - const u32 bytes_read = message->Read(0, buffer_size, buffer.data()).Unwrap(); + const u32 bytes_read = + static_cast(message->Read(0, buffer_size, buffer.data()).Unwrap()); write_buffer.Write(buffer.data(), 0, buffer_size); message->Close(); @@ -354,8 +371,8 @@ void Module::Interface::Write(Kernel::HLERequestContext& ctx) { buffer); } - const u32 bytes_written = - session_data->file->Write(0, buffer.size(), true, buffer.data()).Unwrap(); + const u32 bytes_written = static_cast( + session_data->file->Write(0, buffer.size(), true, buffer.data()).Unwrap()); session_data->file->Close(); rb.Push(RESULT_SUCCESS); @@ -416,7 +433,8 @@ void Module::Interface::WriteMessage(Kernel::HLERequestContext& ctx) { msg_header.sender_id, msg_header.sender_id2, msg_header.send_count, msg_header.forward_count, msg_header.user_data); - const u32 bytes_written = message->Write(0, buffer_size, true, buffer.data()).Unwrap(); + const u32 bytes_written = + static_cast(message->Write(0, buffer_size, true, buffer.data()).Unwrap()); message->Close(); rb.Push(RESULT_SUCCESS); @@ -502,7 +520,8 @@ void Module::Interface::WriteMessageWithHMAC(Kernel::HLERequestContext& ctx) { hmac.CalculateDigest(hmac_digest.data(), message_body.data(), msg_header.body_size); std::memcpy(buffer.data() + hmac_offset, hmac_digest.data(), hmac_size); - const u32 bytes_written = message->Write(0, buffer_size, true, buffer.data()).Unwrap(); + const u32 bytes_written = + static_cast(message->Write(0, buffer_size, true, buffer.data()).Unwrap()); message->Close(); rb.Push(RESULT_SUCCESS); @@ -744,7 +763,8 @@ void Module::Interface::OpenAndWrite(Kernel::HLERequestContext& ctx) { cecd->CheckAndUpdateFile(path_type, ncch_program_id, buffer); } - const u32 bytes_written = file->Write(0, buffer.size(), true, buffer.data()).Unwrap(); + const u32 bytes_written = + static_cast(file->Write(0, buffer.size(), true, buffer.data()).Unwrap()); file->Close(); rb.Push(RESULT_SUCCESS); @@ -793,7 +813,8 @@ void Module::Interface::OpenAndRead(Kernel::HLERequestContext& ctx) { auto file = std::move(file_result).Unwrap(); std::vector buffer(buffer_size); - const u32 bytes_read = file->Read(0, buffer_size, buffer.data()).Unwrap(); + const u32 bytes_read = + static_cast(file->Read(0, buffer_size, buffer.data()).Unwrap()); write_buffer.Write(buffer.data(), 0, buffer_size); file->Close(); @@ -924,7 +945,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ constexpr u32 max_num_boxes = 24; constexpr u32 name_size = 16; // fixed size 16 characters long constexpr u32 valid_name_size = 8; // 8 characters are valid, the rest are null - const u32 file_size = file_buffer.size(); + const u32 file_size = static_cast(file_buffer.size()); switch (path_type) { case CecDataPathType::MboxList: { @@ -1008,7 +1029,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ std::u16string u16_filename; // Loop through entries but don't add mboxlist____ to itself. - for (auto i = 0; i < entry_count; i++) { + for (u32 i = 0; i < entry_count; i++) { u16_filename = std::u16string(entries[i].filename); file_name = Common::UTF16ToUTF8(u16_filename); @@ -1199,7 +1220,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ std::string file_name; std::u16string u16_filename; - for (auto i = 0; i < entry_count; i++) { + for (u32 i = 0; i < entry_count; i++) { u16_filename = std::u16string(entries[i].filename); file_name = Common::UTF16ToUTF8(u16_filename); @@ -1217,7 +1238,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ auto message_result = cecd_system_save_data_archive->OpenFile(message_path, mode); auto message = std::move(message_result).Unwrap(); - const u32 message_size = message->GetSize(); + const u32 message_size = static_cast(message->GetSize()); std::vector buffer(message_size); message->Read(0, message_size, buffer.data()).Unwrap(); @@ -1291,7 +1312,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ std::string file_name; std::u16string u16_filename; - for (auto i = 0; i < entry_count; i++) { + for (u32 i = 0; i < entry_count; i++) { u16_filename = std::u16string(entries[i].filename); file_name = Common::UTF16ToUTF8(u16_filename); @@ -1307,7 +1328,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_ auto message_result = cecd_system_save_data_archive->OpenFile(message_path, mode); auto message = std::move(message_result).Unwrap(); - const u32 message_size = message->GetSize(); + const u32 message_size = static_cast(message->GetSize()); std::vector buffer(message_size); message->Read(0, message_size, buffer.data()).Unwrap(); diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h index 004ef33e70..0870f31bf9 100644 --- a/src/core/hle/service/cecd/cecd.h +++ b/src/core/hle/service/cecd/cecd.h @@ -248,6 +248,19 @@ public: FileSys::Path path; std::unique_ptr file; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object( + *this); + ar& ncch_program_id; + ar& data_path_type; + ar& open_mode.raw; + ar& path; + ar& file; + } + friend class boost::serialization::access; }; class Interface : public ServiceFramework { @@ -584,7 +597,7 @@ public: */ void GetCecInfoEventHandleSys(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr cecd; }; @@ -613,9 +626,17 @@ private: std::shared_ptr change_state_event; Core::System& system; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; /// Initialize CECD service(s) void InstallInterfaces(Core::System& system); } // namespace Service::CECD + +SERVICE_CONSTRUCT(Service::CECD::Module) +BOOST_CLASS_EXPORT_KEY(Service::CECD::Module) +BOOST_CLASS_EXPORT_KEY(Service::CECD::Module::SessionData) diff --git a/src/core/hle/service/cecd/cecd_ndm.cpp b/src/core/hle/service/cecd/cecd_ndm.cpp index e4366e9c62..4b571283de 100644 --- a/src/core/hle/service/cecd/cecd_ndm.cpp +++ b/src/core/hle/service/cecd/cecd_ndm.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cecd/cecd_ndm.h" +SERIALIZE_EXPORT_IMPL(Service::CECD::CECD_NDM) + namespace Service::CECD { CECD_NDM::CECD_NDM(std::shared_ptr cecd) diff --git a/src/core/hle/service/cecd/cecd_ndm.h b/src/core/hle/service/cecd/cecd_ndm.h index a9fabb1a11..9fd282585e 100644 --- a/src/core/hle/service/cecd/cecd_ndm.h +++ b/src/core/hle/service/cecd/cecd_ndm.h @@ -11,6 +11,12 @@ namespace Service::CECD { class CECD_NDM final : public Module::Interface { public: explicit CECD_NDM(std::shared_ptr cecd); + +private: + SERVICE_SERIALIZATION(CECD_NDM, cecd, Module) }; } // namespace Service::CECD + +BOOST_CLASS_EXPORT_KEY(Service::CECD::CECD_NDM) +BOOST_SERIALIZATION_CONSTRUCT(Service::CECD::CECD_NDM) diff --git a/src/core/hle/service/cecd/cecd_s.cpp b/src/core/hle/service/cecd/cecd_s.cpp index 3395c405d2..fa838d2af4 100644 --- a/src/core/hle/service/cecd/cecd_s.cpp +++ b/src/core/hle/service/cecd/cecd_s.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cecd/cecd_s.h" +SERIALIZE_EXPORT_IMPL(Service::CECD::CECD_S) + namespace Service::CECD { CECD_S::CECD_S(std::shared_ptr cecd) diff --git a/src/core/hle/service/cecd/cecd_s.h b/src/core/hle/service/cecd/cecd_s.h index 6c50b13ba9..9c6a7afec9 100644 --- a/src/core/hle/service/cecd/cecd_s.h +++ b/src/core/hle/service/cecd/cecd_s.h @@ -11,6 +11,12 @@ namespace Service::CECD { class CECD_S final : public Module::Interface { public: explicit CECD_S(std::shared_ptr cecd); + +private: + SERVICE_SERIALIZATION(CECD_S, cecd, Module) }; } // namespace Service::CECD + +BOOST_CLASS_EXPORT_KEY(Service::CECD::CECD_S) +BOOST_SERIALIZATION_CONSTRUCT(Service::CECD::CECD_S) diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp index 81fcd1019b..e8cd7d1c9a 100644 --- a/src/core/hle/service/cecd/cecd_u.cpp +++ b/src/core/hle/service/cecd/cecd_u.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cecd/cecd_u.h" +SERIALIZE_EXPORT_IMPL(Service::CECD::CECD_U) + namespace Service::CECD { CECD_U::CECD_U(std::shared_ptr cecd) diff --git a/src/core/hle/service/cecd/cecd_u.h b/src/core/hle/service/cecd/cecd_u.h index 49ddadb690..31e7a2367d 100644 --- a/src/core/hle/service/cecd/cecd_u.h +++ b/src/core/hle/service/cecd/cecd_u.h @@ -11,6 +11,12 @@ namespace Service::CECD { class CECD_U final : public Module::Interface { public: explicit CECD_U(std::shared_ptr cecd); + +private: + SERVICE_SERIALIZATION(CECD_U, cecd, Module) }; } // namespace Service::CECD + +BOOST_CLASS_EXPORT_KEY(Service::CECD::CECD_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::CECD::CECD_U) diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index b9e9a66353..fc285f6625 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -4,8 +4,11 @@ #include #include +#include +#include #include #include +#include "common/archives.h" #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -24,8 +27,18 @@ #include "core/hle/service/cfg/cfg_u.h" #include "core/settings.h" +SERIALIZE_EXPORT_IMPL(Service::CFG::Module) + namespace Service::CFG { +template +void Module::serialize(Archive& ar, const unsigned int) { + ar& cfg_config_file_buffer; + ar& cfg_system_save_data_archive; + ar& preferred_region_code; +} +SERIALIZE_IMPL(Module) + /// The maximum number of block entries that can exist in the config file static const u32 CONFIG_FILE_MAX_BLOCK_ENTRIES = 1479; diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 6886d513b3..58e9145834 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -244,7 +244,7 @@ public: (this->*function)(ctx, id); } - private: + protected: std::shared_ptr cfg; }; @@ -426,6 +426,10 @@ private: std::array cfg_config_file_buffer; std::unique_ptr cfg_system_save_data_archive; u32 preferred_region_code = 0; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; std::shared_ptr GetModule(Core::System& system); @@ -436,3 +440,5 @@ void InstallInterfaces(Core::System& system); std::string GetConsoleIdHash(Core::System& system); } // namespace Service::CFG + +BOOST_CLASS_EXPORT_KEY(Service::CFG::Module) diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 6242990762..4d3d298a20 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cfg/cfg_i.h" +SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_I) + namespace Service::CFG { CFG_I::CFG_I(std::shared_ptr cfg) : Module::Interface(std::move(cfg), "cfg:i", 23) { diff --git a/src/core/hle/service/cfg/cfg_i.h b/src/core/hle/service/cfg/cfg_i.h index 704eb4a71a..ac8ffb9906 100644 --- a/src/core/hle/service/cfg/cfg_i.h +++ b/src/core/hle/service/cfg/cfg_i.h @@ -11,6 +11,12 @@ namespace Service::CFG { class CFG_I final : public Module::Interface { public: explicit CFG_I(std::shared_ptr cfg); + +private: + SERVICE_SERIALIZATION(CFG_I, cfg, Module) }; } // namespace Service::CFG + +BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_I) +BOOST_SERIALIZATION_CONSTRUCT(Service::CFG::CFG_I) diff --git a/src/core/hle/service/cfg/cfg_nor.cpp b/src/core/hle/service/cfg/cfg_nor.cpp index 4135483133..0dd21076d5 100644 --- a/src/core/hle/service/cfg/cfg_nor.cpp +++ b/src/core/hle/service/cfg/cfg_nor.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cfg/cfg_nor.h" +SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_NOR) + namespace Service::CFG { CFG_NOR::CFG_NOR() : ServiceFramework("cfg:nor", 23) { diff --git a/src/core/hle/service/cfg/cfg_nor.h b/src/core/hle/service/cfg/cfg_nor.h index 3dace92bd9..7e0a1a2b82 100644 --- a/src/core/hle/service/cfg/cfg_nor.h +++ b/src/core/hle/service/cfg/cfg_nor.h @@ -11,6 +11,11 @@ namespace Service::CFG { class CFG_NOR final : public ServiceFramework { public: CFG_NOR(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::CFG + +BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_NOR) diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp index a211dae05b..773a412f7f 100644 --- a/src/core/hle/service/cfg/cfg_s.cpp +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cfg/cfg_s.h" +SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_S) + namespace Service::CFG { CFG_S::CFG_S(std::shared_ptr cfg) : Module::Interface(std::move(cfg), "cfg:s", 23) { diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h index 7f135b3577..d0cbc7a187 100644 --- a/src/core/hle/service/cfg/cfg_s.h +++ b/src/core/hle/service/cfg/cfg_s.h @@ -11,6 +11,12 @@ namespace Service::CFG { class CFG_S final : public Module::Interface { public: explicit CFG_S(std::shared_ptr cfg); + +private: + SERVICE_SERIALIZATION(CFG_S, cfg, Module) }; } // namespace Service::CFG + +BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_S) +BOOST_SERIALIZATION_CONSTRUCT(Service::CFG::CFG_S) diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 6aa862f82f..bcc590bf87 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/cfg/cfg_u.h" +SERIALIZE_EXPORT_IMPL(Service::CFG::CFG_U) + namespace Service::CFG { CFG_U::CFG_U(std::shared_ptr cfg) : Module::Interface(std::move(cfg), "cfg:u", 23) { diff --git a/src/core/hle/service/cfg/cfg_u.h b/src/core/hle/service/cfg/cfg_u.h index 8b48e963ea..9063771244 100644 --- a/src/core/hle/service/cfg/cfg_u.h +++ b/src/core/hle/service/cfg/cfg_u.h @@ -11,6 +11,12 @@ namespace Service::CFG { class CFG_U final : public Module::Interface { public: explicit CFG_U(std::shared_ptr cfg); + +private: + SERVICE_SERIALIZATION(CFG_U, cfg, Module) }; } // namespace Service::CFG + +BOOST_CLASS_EXPORT_KEY(Service::CFG::CFG_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::CFG::CFG_U) diff --git a/src/core/hle/service/csnd/csnd_snd.cpp b/src/core/hle/service/csnd/csnd_snd.cpp index 5c9fa13e94..1a7a009645 100644 --- a/src/core/hle/service/csnd/csnd_snd.cpp +++ b/src/core/hle/service/csnd/csnd_snd.cpp @@ -3,11 +3,15 @@ // Refer to the license.txt file included. #include "common/alignment.h" +#include "common/archives.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/result.h" #include "core/hle/service/csnd/csnd_snd.h" +SERVICE_CONSTRUCT_IMPL(Service::CSND::CSND_SND) +SERIALIZE_EXPORT_IMPL(Service::CSND::CSND_SND) + namespace Service::CSND { enum class CommandId : u16 { diff --git a/src/core/hle/service/csnd/csnd_snd.h b/src/core/hle/service/csnd/csnd_snd.h index afdc3b1a8b..0b4cd4331d 100644 --- a/src/core/hle/service/csnd/csnd_snd.h +++ b/src/core/hle/service/csnd/csnd_snd.h @@ -5,6 +5,8 @@ #pragma once #include +#include +#include #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/service.h" @@ -33,6 +35,14 @@ enum class LoopMode : u8 { struct AdpcmState { s16 predictor = 0; u8 step_index = 0; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& predictor; + ar& step_index; + } + friend class boost::serialization::access; }; struct Channel { @@ -52,6 +62,28 @@ struct Channel { LoopMode loop_mode = LoopMode::Manual; Encoding encoding = Encoding::Pcm8; u8 psg_duty = 0; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& block1_address; + ar& block2_address; + ar& block1_size; + ar& block2_size; + ar& block1_adpcm_state; + ar& block2_adpcm_state; + ar& block2_adpcm_reload; + ar& left_channel_volume; + ar& right_channel_volume; + ar& left_capture_volume; + ar& right_capture_volume; + ar& sample_rate; + ar& linear_interpolation; + ar& loop_mode; + ar& encoding; + ar& psg_duty; + } + friend class boost::serialization::access; }; class CSND_SND final : public ServiceFramework { @@ -222,9 +254,27 @@ private: u32 type1_command_offset = 0; u32 acquired_channel_mask = 0; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& mutex; + ar& shared_memory; + ar& capture_units; + ar& channels; + ar& master_state_offset; + ar& channel_state_offset; + ar& capture_state_offset; + ar& type1_command_offset; + ar& acquired_channel_mask; + } + friend class boost::serialization::access; }; /// Initializes the CSND_SND Service void InstallInterfaces(Core::System& system); } // namespace Service::CSND + +BOOST_CLASS_EXPORT_KEY(Service::CSND::CSND_SND) +SERVICE_CONSTRUCT(Service::CSND::CSND_SND) diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp index 63308f57e2..f8b315c00b 100644 --- a/src/core/hle/service/dlp/dlp_clnt.cpp +++ b/src/core/hle/service/dlp/dlp_clnt.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/dlp/dlp_clnt.h" +SERIALIZE_EXPORT_IMPL(Service::DLP::DLP_CLNT) + namespace Service::DLP { DLP_CLNT::DLP_CLNT() : ServiceFramework("dlp:CLNT", 1) { diff --git a/src/core/hle/service/dlp/dlp_clnt.h b/src/core/hle/service/dlp/dlp_clnt.h index db506b985e..ac6933e7ea 100644 --- a/src/core/hle/service/dlp/dlp_clnt.h +++ b/src/core/hle/service/dlp/dlp_clnt.h @@ -12,6 +12,11 @@ class DLP_CLNT final : public ServiceFramework { public: DLP_CLNT(); ~DLP_CLNT() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::DLP + +BOOST_CLASS_EXPORT_KEY(Service::DLP::DLP_CLNT) diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp index 30a98c4bf1..948b25b673 100644 --- a/src/core/hle/service/dlp/dlp_fkcl.cpp +++ b/src/core/hle/service/dlp/dlp_fkcl.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/dlp/dlp_fkcl.h" +SERIALIZE_EXPORT_IMPL(Service::DLP::DLP_FKCL) + namespace Service::DLP { DLP_FKCL::DLP_FKCL() : ServiceFramework("dlp:FKCL", 1) { diff --git a/src/core/hle/service/dlp/dlp_fkcl.h b/src/core/hle/service/dlp/dlp_fkcl.h index a3b2ac86de..c05a77b495 100644 --- a/src/core/hle/service/dlp/dlp_fkcl.h +++ b/src/core/hle/service/dlp/dlp_fkcl.h @@ -12,6 +12,11 @@ class DLP_FKCL final : public ServiceFramework { public: DLP_FKCL(); ~DLP_FKCL() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::DLP + +BOOST_CLASS_EXPORT_KEY(Service::DLP::DLP_FKCL) diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp index 9d74059416..87733b71f0 100644 --- a/src/core/hle/service/dlp/dlp_srvr.cpp +++ b/src/core/hle/service/dlp/dlp_srvr.cpp @@ -2,12 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/result.h" #include "core/hle/service/dlp/dlp_srvr.h" +SERIALIZE_EXPORT_IMPL(Service::DLP::DLP_SRVR) + namespace Service::DLP { void DLP_SRVR::IsChild(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/dlp/dlp_srvr.h b/src/core/hle/service/dlp/dlp_srvr.h index 50d8d92b25..625740d2f2 100644 --- a/src/core/hle/service/dlp/dlp_srvr.h +++ b/src/core/hle/service/dlp/dlp_srvr.h @@ -15,6 +15,10 @@ public: private: void IsChild(Kernel::HLERequestContext& ctx); + + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::DLP + +BOOST_CLASS_EXPORT_KEY(Service::DLP::DLP_SRVR) diff --git a/src/core/hle/service/dsp/dsp_dsp.cpp b/src/core/hle/service/dsp/dsp_dsp.cpp index 37682edcc3..9b0ef89866 100644 --- a/src/core/hle/service/dsp/dsp_dsp.cpp +++ b/src/core/hle/service/dsp/dsp_dsp.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "audio_core/audio_types.h" +#include "common/archives.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" @@ -13,6 +14,9 @@ using DspPipe = AudioCore::DspPipe; using InterruptType = Service::DSP::DSP_DSP::InterruptType; +SERIALIZE_EXPORT_IMPL(Service::DSP::DSP_DSP) +SERVICE_CONSTRUCT_IMPL(Service::DSP::DSP_DSP) + namespace AudioCore { enum class DspPipe; } diff --git a/src/core/hle/service/dsp/dsp_dsp.h b/src/core/hle/service/dsp/dsp_dsp.h index ef1f0b76df..d580b3d00d 100644 --- a/src/core/hle/service/dsp/dsp_dsp.h +++ b/src/core/hle/service/dsp/dsp_dsp.h @@ -5,6 +5,9 @@ #pragma once #include +#include +#include +#include #include "audio_core/dsp_interface.h" #include "core/hle/kernel/event.h" #include "core/hle/result.h" @@ -264,8 +267,22 @@ private: /// Each DSP pipe has an associated interrupt std::array, AudioCore::num_dsp_pipe> pipes = {{}}; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& semaphore_event; + ar& preset_semaphore; + ar& interrupt_zero; + ar& interrupt_one; + ar& pipes; + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::DSP + +BOOST_CLASS_EXPORT_KEY(Service::DSP::DSP_DSP) +SERVICE_CONSTRUCT(Service::DSP::DSP_DSP) diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 13b2c7e464..85759f9d20 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "common/archives.h" #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -14,6 +15,20 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/result.h" #include "core/hle/service/err_f.h" +#undef exception_info // We use 'exception_info' as a plain identifier, but MSVC defines this in one + // of its many headers. + +SERIALIZE_EXPORT_IMPL(Service::ERR::ERR_F) + +namespace boost::serialization { +template +void load_construct_data(Archive& ar, Service::ERR::ERR_F* t, const unsigned int) { + ::new (t) Service::ERR::ERR_F(Core::Global()); +} + +template void load_construct_data(iarchive& ar, Service::ERR::ERR_F* t, + const unsigned int); +} // namespace boost::serialization namespace Service::ERR { diff --git a/src/core/hle/service/err_f.h b/src/core/hle/service/err_f.h index 4a1684cafc..1b9fad4526 100644 --- a/src/core/hle/service/err_f.h +++ b/src/core/hle/service/err_f.h @@ -34,8 +34,17 @@ private: void ThrowFatalError(Kernel::HLERequestContext& ctx); Core::System& system; + + SERVICE_SERIALIZATION_SIMPLE }; void InstallInterfaces(Core::System& system); } // namespace Service::ERR + +BOOST_CLASS_EXPORT_KEY(Service::ERR::ERR_F) + +namespace boost::serialization { +template +void load_construct_data(Archive& ar, Service::ERR::ERR_F* t, const unsigned int); +} diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h index 200b51ebe4..1f34e61fa6 100644 --- a/src/core/hle/service/frd/frd.h +++ b/src/core/hle/service/frd/frd.h @@ -18,10 +18,26 @@ struct FriendKey { u32 friend_id; u32 unknown; u64 friend_code; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& friend_id; + ar& unknown; + ar& friend_code; + } + friend class boost::serialization::access; }; struct MyPresence { u8 unknown[0x12C]; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& unknown; + } + friend class boost::serialization::access; }; struct Profile { @@ -130,13 +146,20 @@ public: */ void SetClientSdkVersion(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr frd; }; private: FriendKey my_friend_key = {0, 0, 0ull}; MyPresence my_presence = {}; + + template + void serialize(Archive& ar, const unsigned int) { + ar& my_friend_key; + ar& my_presence; + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); diff --git a/src/core/hle/service/frd/frd_a.cpp b/src/core/hle/service/frd/frd_a.cpp index c68689cc00..23a83a55e2 100644 --- a/src/core/hle/service/frd/frd_a.cpp +++ b/src/core/hle/service/frd/frd_a.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/frd/frd_a.h" +SERIALIZE_EXPORT_IMPL(Service::FRD::FRD_A) + namespace Service::FRD { FRD_A::FRD_A(std::shared_ptr frd) : Module::Interface(std::move(frd), "frd:a", 8) { diff --git a/src/core/hle/service/frd/frd_a.h b/src/core/hle/service/frd/frd_a.h index 97657a072f..0bdd875252 100644 --- a/src/core/hle/service/frd/frd_a.h +++ b/src/core/hle/service/frd/frd_a.h @@ -11,6 +11,12 @@ namespace Service::FRD { class FRD_A final : public Module::Interface { public: explicit FRD_A(std::shared_ptr frd); + +private: + SERVICE_SERIALIZATION(FRD_A, frd, Module) }; } // namespace Service::FRD + +BOOST_CLASS_EXPORT_KEY(Service::FRD::FRD_A) +BOOST_SERIALIZATION_CONSTRUCT(Service::FRD::FRD_A) diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp index 2cbc64243e..d83c8ac8c4 100644 --- a/src/core/hle/service/frd/frd_u.cpp +++ b/src/core/hle/service/frd/frd_u.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/frd/frd_u.h" +SERIALIZE_EXPORT_IMPL(Service::FRD::FRD_U) + namespace Service::FRD { FRD_U::FRD_U(std::shared_ptr frd) : Module::Interface(std::move(frd), "frd:u", 8) { diff --git a/src/core/hle/service/frd/frd_u.h b/src/core/hle/service/frd/frd_u.h index 5704d5e11a..e6adc37fbb 100644 --- a/src/core/hle/service/frd/frd_u.h +++ b/src/core/hle/service/frd/frd_u.h @@ -11,6 +11,12 @@ namespace Service::FRD { class FRD_U final : public Module::Interface { public: explicit FRD_U(std::shared_ptr frd); + +private: + SERVICE_SERIALIZATION(FRD_U, frd, Module) }; } // namespace Service::FRD + +BOOST_CLASS_EXPORT_KEY(Service::FRD::FRD_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::FRD::FRD_U) diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 6e179978bc..767faf1679 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -13,6 +13,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_ncch.h" @@ -90,7 +91,7 @@ ArchiveManager::OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys: if (backend.Failed()) return std::make_tuple(backend.Code(), open_timeout_ns); - auto file = std::shared_ptr(new File(system, std::move(backend).Unwrap(), path)); + auto file = std::shared_ptr(new File(system.Kernel(), std::move(backend).Unwrap(), path)); return std::make_tuple(MakeResult>(std::move(file)), open_timeout_ns); } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 862e749806..aba06ac6fe 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -8,7 +8,8 @@ #include #include #include -#include +#include +#include #include "common/common_types.h" #include "core/file_sys/archive_backend.h" #include "core/hle/result.h" @@ -253,13 +254,21 @@ private: * Map of registered archives, identified by id code. Once an archive is registered here, it is * never removed until UnregisterArchiveTypes is called. */ - boost::container::flat_map> id_code_map; + std::unordered_map> id_code_map; /** * Map of active archive handles to archive objects */ std::unordered_map> handle_map; ArchiveHandle next_handle = 1; + + template + void serialize(Archive& ar, const unsigned int) { + ar& id_code_map; + ar& handle_map; + ar& next_handle; + } + friend class boost::serialization::access; }; } // namespace Service::FS diff --git a/src/core/hle/service/fs/directory.cpp b/src/core/hle/service/fs/directory.cpp index a72a9307ad..655c5602ea 100644 --- a/src/core/hle/service/fs/directory.cpp +++ b/src/core/hle/service/fs/directory.cpp @@ -2,16 +2,33 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include "common/archives.h" #include "common/logging/log.h" #include "core/file_sys/directory_backend.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/fs/directory.h" +SERIALIZE_EXPORT_IMPL(Service::FS::Directory) + namespace Service::FS { +template +void Directory::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& path; + ar& backend; +} + Directory::Directory(std::unique_ptr&& backend, const FileSys::Path& path) - : ServiceFramework("", 1), path(path), backend(std::move(backend)) { + : Directory() { + this->backend = std::move(backend); + this->path = path; +} + +Directory::Directory() : ServiceFramework("", 1), path(""), backend(nullptr) { static const FunctionInfo functions[] = { // clang-format off {0x08010042, &Directory::Read, "Read"}, diff --git a/src/core/hle/service/fs/directory.h b/src/core/hle/service/fs/directory.h index 890b266488..77956b1661 100644 --- a/src/core/hle/service/fs/directory.h +++ b/src/core/hle/service/fs/directory.h @@ -25,6 +25,15 @@ public: protected: void Read(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); + +private: + Directory(); + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; } // namespace Service::FS + +BOOST_CLASS_EXPORT_KEY(Service::FS::Directory) diff --git a/src/core/hle/service/fs/file.cpp b/src/core/hle/service/fs/file.cpp index f91cbee99b..3d9eee5949 100644 --- a/src/core/hle/service/fs/file.cpp +++ b/src/core/hle/service/fs/file.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include "common/archives.h" #include "common/logging/log.h" #include "core/core.h" #include "core/file_sys/errors.h" @@ -13,11 +15,29 @@ #include "core/hle/kernel/server_session.h" #include "core/hle/service/fs/file.h" +SERIALIZE_EXPORT_IMPL(Service::FS::File) +SERIALIZE_EXPORT_IMPL(Service::FS::FileSessionSlot) + namespace Service::FS { -File::File(Core::System& system, std::unique_ptr&& backend, +template +void File::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& path; + ar& backend; +} + +File::File() : File(Core::Global()) {} + +File::File(Kernel::KernelSystem& kernel, std::unique_ptr&& backend, const FileSys::Path& path) - : ServiceFramework("", 1), path(path), backend(std::move(backend)), system(system) { + : File(kernel) { + this->backend = std::move(backend); + this->path = path; +} + +File::File(Kernel::KernelSystem& kernel) + : ServiceFramework("", 1), path(""), backend(nullptr), kernel(kernel) { static const FunctionInfo functions[] = { {0x08010100, &File::OpenSubFile, "OpenSubFile"}, {0x080200C2, &File::Read, "Read"}, @@ -71,12 +91,7 @@ void File::Read(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(buffer); std::chrono::nanoseconds read_timeout_ns{backend->GetReadDelayNs(length)}; - ctx.SleepClientThread("file::read", read_timeout_ns, - [](std::shared_ptr /*thread*/, - Kernel::HLERequestContext& /*ctx*/, - Kernel::ThreadWakeupReason /*reason*/) { - // Nothing to do here - }); + ctx.SleepClientThread("file::read", read_timeout_ns, nullptr); } void File::Write(Kernel::HLERequestContext& ctx) { @@ -201,7 +216,7 @@ void File::OpenLinkFile(Kernel::HLERequestContext& ctx) { using Kernel::ServerSession; IPC::RequestParser rp(ctx, 0x080C, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - auto [server, client] = system.Kernel().CreateSessionPair(GetName()); + auto [server, client] = kernel.CreateSessionPair(GetName()); ClientConnected(server); FileSessionSlot* slot = GetSessionData(server); @@ -247,7 +262,7 @@ void File::OpenSubFile(Kernel::HLERequestContext& ctx) { using Kernel::ClientSession; using Kernel::ServerSession; - auto [server, client] = system.Kernel().CreateSessionPair(GetName()); + auto [server, client] = kernel.CreateSessionPair(GetName()); ClientConnected(server); FileSessionSlot* slot = GetSessionData(server); @@ -261,7 +276,7 @@ void File::OpenSubFile(Kernel::HLERequestContext& ctx) { } std::shared_ptr File::Connect() { - auto [server, client] = system.Kernel().CreateSessionPair(GetName()); + auto [server, client] = kernel.CreateSessionPair(GetName()); ClientConnected(server); FileSessionSlot* slot = GetSessionData(server); diff --git a/src/core/hle/service/fs/file.h b/src/core/hle/service/fs/file.h index 062fcd5e71..a6ef693042 100644 --- a/src/core/hle/service/fs/file.h +++ b/src/core/hle/service/fs/file.h @@ -5,7 +5,9 @@ #pragma once #include +#include #include "core/file_sys/archive_backend.h" +#include "core/global.h" #include "core/hle/service/service.h" namespace Core { @@ -19,13 +21,25 @@ struct FileSessionSlot : public Kernel::SessionRequestHandler::SessionDataBase { u64 offset; ///< Offset that this session will start reading from. u64 size; ///< Max size of the file that this session is allowed to access bool subfile; ///< Whether this file was opened via OpenSubFile or not. + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object( + *this); + ar& priority; + ar& offset; + ar& size; + ar& subfile; + } + friend class boost::serialization::access; }; // TODO: File is not a real service, but it can still utilize ServiceFramework::RegisterHandlers. // Consider splitting ServiceFramework interface. class File final : public ServiceFramework { public: - File(Core::System& system, std::unique_ptr&& backend, + File(Kernel::KernelSystem& kernel, std::unique_ptr&& backend, const FileSys::Path& path); ~File() = default; @@ -59,7 +73,17 @@ private: void OpenLinkFile(Kernel::HLERequestContext& ctx); void OpenSubFile(Kernel::HLERequestContext& ctx); - Core::System& system; + Kernel::KernelSystem& kernel; + + File(Kernel::KernelSystem& kernel); + File(); + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; } // namespace Service::FS + +BOOST_CLASS_EXPORT_KEY(Service::FS::FileSessionSlot) +BOOST_CLASS_EXPORT_KEY(Service::FS::File) diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index bca8e77e53..010600ee2d 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/assert.h" #include "common/common_types.h" #include "common/file_util.h" @@ -25,6 +26,10 @@ #include "core/hle/service/fs/fs_user.h" #include "core/settings.h" +SERVICE_CONSTRUCT_IMPL(Service::FS::FS_USER) +SERIALIZE_EXPORT_IMPL(Service::FS::FS_USER) +SERIALIZE_EXPORT_IMPL(Service::FS::ClientSlot) + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace FS_User @@ -71,12 +76,7 @@ void FS_USER::OpenFile(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_FS, "failed to get a handle for file {}", file_path.DebugStr()); } - ctx.SleepClientThread("fs_user::open", open_timeout_ns, - [](std::shared_ptr /*thread*/, - Kernel::HLERequestContext& /*ctx*/, - Kernel::ThreadWakeupReason /*reason*/) { - // Nothing to do here - }); + ctx.SleepClientThread("fs_user::open", open_timeout_ns, nullptr); } void FS_USER::OpenFileDirectly(Kernel::HLERequestContext& ctx) { @@ -129,12 +129,7 @@ void FS_USER::OpenFileDirectly(Kernel::HLERequestContext& ctx) { file_path.DebugStr(), mode.hex, attributes); } - ctx.SleepClientThread("fs_user::open_directly", open_timeout_ns, - [](std::shared_ptr /*thread*/, - Kernel::HLERequestContext& /*ctx*/, - Kernel::ThreadWakeupReason /*reason*/) { - // Nothing to do here - }); + ctx.SleepClientThread("fs_user::open_directly", open_timeout_ns, nullptr); } void FS_USER::DeleteFile(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h index 97b45714b2..e972d0dae6 100644 --- a/src/core/hle/service/fs/fs_user.h +++ b/src/core/hle/service/fs/fs_user.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/common_types.h" #include "core/hle/service/service.h" @@ -22,6 +23,15 @@ struct ClientSlot : public Kernel::SessionRequestHandler::SessionDataBase { // behaviour is modified. Since we don't emulate fs:REG mechanism, we assume the program ID is // the same as codeset ID and fetch from there directly. u64 program_id = 0; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object( + *this); + ar& program_id; + } + friend class boost::serialization::access; }; class FS_USER final : public ServiceFramework { @@ -545,8 +555,19 @@ private: Core::System& system; ArchiveManager& archives; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& priority; + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::FS + +SERVICE_CONSTRUCT(Service::FS::FS_USER) +BOOST_CLASS_EXPORT_KEY(Service::FS::FS_USER) +BOOST_CLASS_EXPORT_KEY(Service::FS::ClientSlot) diff --git a/src/core/hle/service/gsp/gsp.cpp b/src/core/hle/service/gsp/gsp.cpp index 4291b99710..c360d895c3 100644 --- a/src/core/hle/service/gsp/gsp.cpp +++ b/src/core/hle/service/gsp/gsp.cpp @@ -27,4 +27,8 @@ void InstallInterfaces(Core::System& system) { std::make_shared()->InstallAsService(service_manager); } +void SetGlobalModule(Core::System& system) { + gsp_gpu = system.ServiceManager().GetService("gsp::Gpu"); +} + } // namespace Service::GSP diff --git a/src/core/hle/service/gsp/gsp.h b/src/core/hle/service/gsp/gsp.h index 4cde19e93a..a4dd84f277 100644 --- a/src/core/hle/service/gsp/gsp.h +++ b/src/core/hle/service/gsp/gsp.h @@ -23,4 +23,6 @@ namespace Service::GSP { void SignalInterrupt(InterruptId interrupt_id); void InstallInterfaces(Core::System& system); + +void SetGlobalModule(Core::System& system); } // namespace Service::GSP diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 33558fe10f..5a41f5ca0c 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/bit_field.h" #include "common/microprofile.h" #include "common/swap.h" @@ -21,6 +22,10 @@ #include "video_core/debug_utils/debug_utils.h" #include "video_core/gpu_debugger.h" +SERIALIZE_EXPORT_IMPL(Service::GSP::SessionData) +SERIALIZE_EXPORT_IMPL(Service::GSP::GSP_GPU) +SERVICE_CONSTRUCT_IMPL(Service::GSP::GSP_GPU) + // Main graphics debugger object - TODO: Here is probably not the best place for this GraphicsDebugger g_debugger; @@ -819,10 +824,6 @@ std::unique_ptr GSP_GPU::MakeSes return std::make_unique(this); } -SessionData::SessionData() { - UNREACHABLE(); -} - SessionData::SessionData(GSP_GPU* gsp) : gsp(gsp) { // Assign a new thread id to this session when it connects. Note: In the real GSP service this // is done through a real thread (svcCreateThread) but we have to simulate it since our HLE diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 8b43b9cea3..c8914ef72f 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/event.h" @@ -187,7 +189,7 @@ class GSP_GPU; class SessionData : public Kernel::SessionRequestHandler::SessionDataBase { public: - SessionData(); + SessionData() = default; SessionData(GSP_GPU* gsp); ~SessionData(); @@ -199,6 +201,18 @@ public: u32 thread_id; /// Whether RegisterInterruptRelayQueue was called for this session bool registered = false; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object( + *this); + ar& gsp; + ar& interrupt_event; + ar& thread_id; + ar& registered; + } + friend class boost::serialization::access; }; class GSP_GPU final : public ServiceFramework { @@ -431,8 +445,23 @@ private: std::array used_thread_ids = {false, false, false, false}; friend class SessionData; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& shared_memory; + ar& active_thread_id; + ar& first_initialization; + ar& used_thread_ids; + } + + friend class boost::serialization::access; }; ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info); } // namespace Service::GSP + +BOOST_CLASS_EXPORT_KEY(Service::GSP::SessionData) +BOOST_CLASS_EXPORT_KEY(Service::GSP::GSP_GPU) +SERVICE_CONSTRUCT(Service::GSP::GSP_GPU) diff --git a/src/core/hle/service/gsp/gsp_lcd.cpp b/src/core/hle/service/gsp/gsp_lcd.cpp index d795b87161..67a2e76286 100644 --- a/src/core/hle/service/gsp/gsp_lcd.cpp +++ b/src/core/hle/service/gsp/gsp_lcd.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/gsp/gsp_lcd.h" +SERIALIZE_EXPORT_IMPL(Service::GSP::GSP_LCD) + namespace Service::GSP { GSP_LCD::GSP_LCD() : ServiceFramework("gsp::Lcd") { diff --git a/src/core/hle/service/gsp/gsp_lcd.h b/src/core/hle/service/gsp/gsp_lcd.h index 24e57fb42f..31d17f5400 100644 --- a/src/core/hle/service/gsp/gsp_lcd.h +++ b/src/core/hle/service/gsp/gsp_lcd.h @@ -12,6 +12,11 @@ class GSP_LCD final : public ServiceFramework { public: GSP_LCD(); ~GSP_LCD() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::GSP + +BOOST_CLASS_EXPORT_KEY(Service::GSP::GSP_LCD) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index ded1809b1a..4c87182766 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -4,6 +4,10 @@ #include #include +#include +#include +#include +#include "common/archives.h" #include "common/logging/log.h" #include "core/3ds.h" #include "core/core.h" @@ -20,8 +24,36 @@ #include "core/movie.h" #include "video_core/video_core.h" +SERVICE_CONSTRUCT_IMPL(Service::HID::Module) +SERIALIZE_EXPORT_IMPL(Service::HID::Module) + namespace Service::HID { +template +void Module::serialize(Archive& ar, const unsigned int file_version) { + ar& shared_mem; + ar& event_pad_or_touch_1; + ar& event_pad_or_touch_2; + ar& event_accelerometer; + ar& event_gyroscope; + ar& event_debug_pad; + ar& next_pad_index; + ar& next_touch_index; + ar& next_accelerometer_index; + ar& next_gyroscope_index; + ar& enable_accelerometer_count; + ar& enable_gyroscope_count; + if (Archive::is_loading::value) { + LoadInputDevices(); + } + if (file_version >= 1) { + ar& state.hex; + } + // Update events are set in the constructor + // Devices are set from the implementation (and are stateless afaik) +} +SERIALIZE_IMPL(Module) + // Updating period for each HID device. These empirical values are measured from a 11.2 3DS. constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 7e8a8c5278..58a3c68a59 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -287,7 +288,7 @@ public: */ void GetGyroscopeLowCalibrateParam(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr hid; }; @@ -335,9 +336,17 @@ private: std::unique_ptr circle_pad; std::unique_ptr motion_device; std::unique_ptr touch_device; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; std::shared_ptr GetModule(Core::System& system); void InstallInterfaces(Core::System& system); } // namespace Service::HID + +SERVICE_CONSTRUCT(Service::HID::Module) +BOOST_CLASS_EXPORT_KEY(Service::HID::Module) +BOOST_CLASS_VERSION(Service::HID::Module, 1) diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp index 8371a61698..87791f0738 100644 --- a/src/core/hle/service/hid/hid_spvr.cpp +++ b/src/core/hle/service/hid/hid_spvr.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/hid/hid_spvr.h" +SERIALIZE_EXPORT_IMPL(Service::HID::Spvr) + namespace Service::HID { Spvr::Spvr(std::shared_ptr hid) : Module::Interface(std::move(hid), "hid:SPVR", 6) { diff --git a/src/core/hle/service/hid/hid_spvr.h b/src/core/hle/service/hid/hid_spvr.h index e2346dda57..749bd36b8b 100644 --- a/src/core/hle/service/hid/hid_spvr.h +++ b/src/core/hle/service/hid/hid_spvr.h @@ -11,6 +11,12 @@ namespace Service::HID { class Spvr final : public Module::Interface { public: explicit Spvr(std::shared_ptr hid); + +private: + SERVICE_SERIALIZATION(Spvr, hid, Module) }; } // namespace Service::HID + +BOOST_CLASS_EXPORT_KEY(Service::HID::Spvr) +BOOST_SERIALIZATION_CONSTRUCT(Service::HID::Spvr) diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp index 129b3fd02d..97ce8f111f 100644 --- a/src/core/hle/service/hid/hid_user.cpp +++ b/src/core/hle/service/hid/hid_user.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/hid/hid_user.h" +SERIALIZE_EXPORT_IMPL(Service::HID::User) + namespace Service::HID { User::User(std::shared_ptr hid) : Module::Interface(std::move(hid), "hid:USER", 6) { diff --git a/src/core/hle/service/hid/hid_user.h b/src/core/hle/service/hid/hid_user.h index 813f095047..8a6763ea65 100644 --- a/src/core/hle/service/hid/hid_user.h +++ b/src/core/hle/service/hid/hid_user.h @@ -14,6 +14,12 @@ namespace Service::HID { class User final : public Module::Interface { public: explicit User(std::shared_ptr hid); + +private: + SERVICE_SERIALIZATION(User, hid, Module) }; } // namespace Service::HID + +BOOST_CLASS_EXPORT_KEY(Service::HID::User) +BOOST_SERIALIZATION_CONSTRUCT(Service::HID::User) diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp index f0e805e510..4879ab3716 100644 --- a/src/core/hle/service/http_c.cpp +++ b/src/core/hle/service/http_c.cpp @@ -8,6 +8,7 @@ #endif #include #include +#include "common/archives.h" #include "common/assert.h" #include "core/core.h" #include "core/file_sys/archive_ncch.h" @@ -19,6 +20,9 @@ #include "core/hle/service/http_c.h" #include "core/hw/aes/key.h" +SERIALIZE_EXPORT_IMPL(Service::HTTP::HTTP_C) +SERIALIZE_EXPORT_IMPL(Service::HTTP::SessionData) + namespace Service::HTTP { namespace ErrCodes { @@ -79,10 +83,10 @@ void Context::MakeRequest() { client = std::move(ssl_client); if (auto client_cert = ssl_config.client_cert_ctx.lock()) { - SSL_CTX_use_certificate_ASN1(ctx, client_cert->certificate.size(), + SSL_CTX_use_certificate_ASN1(ctx, static_cast(client_cert->certificate.size()), client_cert->certificate.data()); SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, ctx, client_cert->private_key.data(), - client_cert->private_key.size()); + static_cast(client_cert->private_key.size())); } // TODO(B3N30): Check for SSLOptions-Bits and set the verify method accordingly diff --git a/src/core/hle/service/http_c.h b/src/core/hle/service/http_c.h index 0742de8a7f..490e066481 100644 --- a/src/core/hle/service/http_c.h +++ b/src/core/hle/service/http_c.h @@ -10,6 +10,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #ifdef ENABLE_WEB_SERVICE #if defined(__ANDROID__) #include @@ -57,6 +64,17 @@ struct ClientCertContext { u8 cert_id; std::vector certificate; std::vector private_key; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& handle; + ar& session_id; + ar& cert_id; + ar& certificate; + ar& private_key; + } + friend class boost::serialization::access; }; /// Represents a root certificate chain, it contains a list of DER-encoded certificates for @@ -68,12 +86,30 @@ struct RootCertChain { Handle handle; u32 session_id; std::vector certificate; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& handle; + ar& session_id; + ar& certificate; + } + friend class boost::serialization::access; }; using Handle = u32; Handle handle; u32 session_id; std::vector certificates; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& handle; + ar& session_id; + ar& certificates; + } + friend class boost::serialization::access; }; /// Represents an HTTP context. @@ -92,30 +128,74 @@ public: std::string username; std::string password; u16 port; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& url; + ar& username; + ar& password; + ar& port; + } + friend class boost::serialization::access; }; struct BasicAuth { std::string username; std::string password; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& username; + ar& password; + } + friend class boost::serialization::access; }; struct RequestHeader { RequestHeader(std::string name, std::string value) : name(name), value(value){}; std::string name; std::string value; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& name; + ar& value; + } + friend class boost::serialization::access; }; struct PostData { // TODO(Subv): Support Binary and Raw POST elements. PostData(std::string name, std::string value) : name(name), value(value){}; + PostData() = default; std::string name; std::string value; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& name; + ar& value; + } + friend class boost::serialization::access; }; struct SSLConfig { u32 options; std::weak_ptr client_cert_ctx; std::weak_ptr root_ca_chain; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& options; + ar& client_cert_ctx; + ar& root_ca_chain; + } + friend class boost::serialization::access; }; Handle handle; @@ -141,7 +221,7 @@ public: struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { /// The HTTP context that is currently bound to this session, this can be empty if no context /// has been bound. Certain commands can only be called on a session with a bound context. - std::optional current_http_context; + boost::optional current_http_context; u32 session_id; @@ -153,6 +233,19 @@ struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { /// Whether this session has been initialized in some way, be it via Initialize or /// InitializeConnectionSession. bool initialized = false; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object( + *this); + ar& current_http_context; + ar& session_id; + ar& num_http_contexts; + ar& num_client_certs; + ar& initialized; + } + friend class boost::serialization::access; }; class HTTP_C final : public ServiceFramework { @@ -339,8 +432,29 @@ private: std::vector private_key; bool init = false; } ClCertA; + +private: + template + void serialize(Archive& ar, const unsigned int) { + // NOTE: Serialization of the HTTP service is on a 'best effort' basis. + // There is a very good chance that saving/loading during a network connection will break, + // regardless! + ar& boost::serialization::base_object(*this); + ar& ClCertA.certificate; + ar& ClCertA.private_key; + ar& ClCertA.init; + ar& context_counter; + ar& client_certs_counter; + ar& client_certs; + // NOTE: `contexts` is not serialized because it contains non-serializable data. (i.e. + // handles to ongoing HTTP requests.) Serializing across HTTP contexts will break. + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::HTTP + +BOOST_CLASS_EXPORT_KEY(Service::HTTP::HTTP_C) +BOOST_CLASS_EXPORT_KEY(Service::HTTP::SessionData) diff --git a/src/core/hle/service/ir/extra_hid.h b/src/core/hle/service/ir/extra_hid.h index d21cb393fe..d498c471f1 100644 --- a/src/core/hle/service/ir/extra_hid.h +++ b/src/core/hle/service/ir/extra_hid.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/bit_field.h" #include "common/swap.h" #include "core/frontend/input.h" @@ -65,6 +66,18 @@ private: std::unique_ptr zr; std::unique_ptr c_stick; std::atomic is_device_reload_pending; + + template + void serialize(Archive& ar, const unsigned int) { + ar& hid_period; + ar& calibration_data; // This isn't writeable for now, but might be in future + if (Archive::is_loading::value) { + LoadInputDevices(); // zl, zr, c_stick are loaded here + } + } + friend class boost::serialization::access; }; } // namespace Service::IR + +BOOST_CLASS_EXPORT_KEY(Service::IR::ExtraHID) diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 71d16a1ce7..bd6c64af37 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include "common/archives.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" @@ -12,8 +15,23 @@ #include "core/movie.h" #include "core/settings.h" +SERIALIZE_EXPORT_IMPL(Service::IR::IR_RST) +SERVICE_CONSTRUCT_IMPL(Service::IR::IR_RST) + namespace Service::IR { +template +void IR_RST::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& update_event; + ar& shared_memory; + ar& next_pad_index; + ar& raw_c_stick; + ar& update_period; + // update_callback_id and input devices are set separately + ReloadInputDevices(); +} + struct PadDataEntry { PadState current_state; PadState delta_additions; diff --git a/src/core/hle/service/ir/ir_rst.h b/src/core/hle/service/ir/ir_rst.h index 84ad70dfcb..8e17381ad1 100644 --- a/src/core/hle/service/ir/ir_rst.h +++ b/src/core/hle/service/ir/ir_rst.h @@ -87,6 +87,13 @@ private: std::atomic is_device_reload_pending{false}; bool raw_c_stick{false}; int update_period{0}; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; } // namespace Service::IR + +BOOST_CLASS_EXPORT_KEY(Service::IR::IR_RST) +SERVICE_CONSTRUCT(Service::IR::IR_RST) diff --git a/src/core/hle/service/ir/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp index d76323e914..61618869a7 100644 --- a/src/core/hle/service/ir/ir_u.cpp +++ b/src/core/hle/service/ir/ir_u.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ir/ir_u.h" +SERIALIZE_EXPORT_IMPL(Service::IR::IR_U) + namespace Service::IR { IR_U::IR_U() : ServiceFramework("ir:u", 1) { diff --git a/src/core/hle/service/ir/ir_u.h b/src/core/hle/service/ir/ir_u.h index ea150b0825..ecaf1be285 100644 --- a/src/core/hle/service/ir/ir_u.h +++ b/src/core/hle/service/ir/ir_u.h @@ -12,6 +12,11 @@ namespace Service::IR { class IR_U final : public ServiceFramework { public: IR_U(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::IR + +BOOST_CLASS_EXPORT_KEY(Service::IR::IR_U) diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index d392c1983d..0cc77c928f 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -4,6 +4,9 @@ #include #include +#include +#include +#include #include "common/string_util.h" #include "common/swap.h" #include "core/core.h" @@ -13,8 +16,23 @@ #include "core/hle/service/ir/extra_hid.h" #include "core/hle/service/ir/ir_user.h" +SERIALIZE_EXPORT_IMPL(Service::IR::IR_USER) +SERVICE_CONSTRUCT_IMPL(Service::IR::IR_USER) + namespace Service::IR { +template +void IR_USER::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& conn_status_event; + ar& send_event; + ar& receive_event; + ar& shared_memory; + ar& connected_device; + ar& receive_buffer; + ar&* extra_hid.get(); +} + // This is a header that will present in the ir:USER shared memory if it is initialized with // InitializeIrNopShared service function. Otherwise the shared memory doesn't have this header if // it is initialized with InitializeIrNop service function. @@ -139,6 +157,16 @@ private: u32_le end_index; u32_le packet_count; u32_le unknown; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& begin_index; + ar& end_index; + ar& packet_count; + ar& unknown; + } + friend class boost::serialization::access; }; static_assert(sizeof(BufferInfo) == 16, "BufferInfo has wrong size!"); @@ -179,6 +207,20 @@ private: u32 buffer_offset; u32 max_packet_count; u32 max_data_size; + +private: + BufferManager() = default; + + template + void serialize(Archive& ar, const unsigned int) { + ar& info; + ar& shared_memory; + ar& info_offset; + ar& buffer_offset; + ar& max_packet_count; + ar& max_data_size; + } + friend class boost::serialization::access; }; /// Wraps the payload into packet and puts it to the receive buffer @@ -270,8 +312,8 @@ void IR_USER::RequireConnection(Kernel::HLERequestContext& ctx) { shared_memory_ptr[offsetof(SharedMemoryHeader, connection_role)] = 2; shared_memory_ptr[offsetof(SharedMemoryHeader, connected)] = 1; - connected_device = extra_hid.get(); - connected_device->OnConnect(); + connected_device = true; + extra_hid->OnConnect(); conn_status_event->Signal(); } else { LOG_WARNING(Service_IR, "unknown device id {}. Won't connect.", device_id); @@ -305,8 +347,8 @@ void IR_USER::GetSendEvent(Kernel::HLERequestContext& ctx) { void IR_USER::Disconnect(Kernel::HLERequestContext& ctx) { if (connected_device) { - connected_device->OnDisconnect(); - connected_device = nullptr; + extra_hid->OnDisconnect(); + connected_device = false; conn_status_event->Signal(); } @@ -331,8 +373,8 @@ void IR_USER::GetConnectionStatusEvent(Kernel::HLERequestContext& ctx) { void IR_USER::FinalizeIrNop(Kernel::HLERequestContext& ctx) { if (connected_device) { - connected_device->OnDisconnect(); - connected_device = nullptr; + extra_hid->OnDisconnect(); + connected_device = false; } shared_memory = nullptr; @@ -352,7 +394,7 @@ void IR_USER::SendIrNop(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (connected_device) { - connected_device->OnReceive(buffer); + extra_hid->OnReceive(buffer); send_event->Signal(); rb.Push(RESULT_SUCCESS); } else { @@ -414,6 +456,7 @@ IR_USER::IR_USER(Core::System& system) : ServiceFramework("ir:USER", 1) { using namespace Kernel; + connected_device = false; conn_status_event = system.Kernel().CreateEvent(ResetType::OneShot, "IR:ConnectionStatusEvent"); send_event = system.Kernel().CreateEvent(ResetType::OneShot, "IR:SendEvent"); receive_event = system.Kernel().CreateEvent(ResetType::OneShot, "IR:ReceiveEvent"); @@ -424,7 +467,7 @@ IR_USER::IR_USER(Core::System& system) : ServiceFramework("ir:USER", 1) { IR_USER::~IR_USER() { if (connected_device) { - connected_device->OnDisconnect(); + extra_hid->OnDisconnect(); } } diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h index 54a4f3e087..afb9be4f7b 100644 --- a/src/core/hle/service/ir/ir_user.h +++ b/src/core/hle/service/ir/ir_user.h @@ -45,6 +45,7 @@ protected: void Send(const std::vector& data); private: + // NOTE: This value is *not* serialized because it's always passed in the constructor const SendFunc send_func; }; @@ -164,9 +165,17 @@ private: std::shared_ptr conn_status_event, send_event, receive_event; std::shared_ptr shared_memory; - IRDevice* connected_device{nullptr}; + bool connected_device; std::unique_ptr receive_buffer; std::unique_ptr extra_hid; + +private: + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; } // namespace Service::IR + +BOOST_CLASS_EXPORT_KEY(Service::IR::IR_USER) +SERVICE_CONSTRUCT(Service::IR::IR_USER) diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index a435d5e58f..a2f8379b1b 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/alignment.h" +#include "common/archives.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" @@ -12,6 +13,10 @@ #include "core/hle/service/ldr_ro/cro_helper.h" #include "core/hle/service/ldr_ro/ldr_ro.h" +SERVICE_CONSTRUCT_IMPL(Service::LDR::RO) +SERIALIZE_EXPORT_IMPL(Service::LDR::RO) +SERIALIZE_EXPORT_IMPL(Service::LDR::ClientSlot) + namespace Service::LDR { static const ResultCode ERROR_ALREADY_INITIALIZED = // 0xD9612FF9 diff --git a/src/core/hle/service/ldr_ro/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h index f90005d13e..7581884f61 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.h +++ b/src/core/hle/service/ldr_ro/ldr_ro.h @@ -14,6 +14,15 @@ namespace Service::LDR { struct ClientSlot : public Kernel::SessionRequestHandler::SessionDataBase { VAddr loaded_crs = 0; ///< the virtual address of the static module + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object( + *this); + ar& loaded_crs; + } + friend class boost::serialization::access; }; class RO final : public ServiceFramework { @@ -151,8 +160,19 @@ private: void Shutdown(Kernel::HLERequestContext& self); Core::System& system; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::LDR + +SERVICE_CONSTRUCT(Service::LDR::RO) +BOOST_CLASS_EXPORT_KEY(Service::LDR::RO) +BOOST_CLASS_EXPORT_KEY(Service::LDR::ClientSlot) diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index ba3e978072..f0017ac4ca 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -2,9 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #ifdef HAVE_CUBEB #include "audio_core/cubeb_input.h" #endif +#include "common/archives.h" #include "common/logging/log.h" #include "core/core.h" #include "core/frontend/mic.h" @@ -17,8 +19,17 @@ #include "core/hle/service/mic_u.h" #include "core/settings.h" +SERVICE_CONSTRUCT_IMPL(Service::MIC::MIC_U) +SERIALIZE_EXPORT_IMPL(Service::MIC::MIC_U) + namespace Service::MIC { +template +void MIC_U::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar&* impl.get(); +} + /// Microphone audio encodings. enum class Encoding : u8 { PCM8 = 0, ///< Unsigned 8-bit PCM. @@ -59,6 +70,7 @@ constexpr u64 GetBufferUpdateRate(SampleRate sample_rate) { // Variables holding the current mic buffer writing state struct State { + std::weak_ptr memory_ref{}; u8* sharedmem_buffer = nullptr; u32 sharedmem_size = 0; std::size_t size = 0; @@ -95,6 +107,23 @@ struct State { std::memcpy(sharedmem_buffer + (sharedmem_size - sizeof(u32)), reinterpret_cast(&off), sizeof(u32)); } + +private: + template + void serialize(Archive& ar, const unsigned int) { + std::shared_ptr _memory_ref = memory_ref.lock(); + ar& _memory_ref; + memory_ref = _memory_ref; + ar& sharedmem_size; + ar& size; + ar& offset; + ar& initial_offset; + ar& looped_buffer; + ar& sample_size; + ar& sample_rate; + sharedmem_buffer = _memory_ref ? _memory_ref->GetPointer() : nullptr; + } + friend class boost::serialization::access; }; struct MIC_U::Impl { @@ -114,6 +143,7 @@ struct MIC_U::Impl { if (shared_memory) { shared_memory->SetName("MIC_U:shared_memory"); + state.memory_ref = shared_memory; state.sharedmem_buffer = shared_memory->GetPointer(); state.sharedmem_size = size; } @@ -363,6 +393,21 @@ struct MIC_U::Impl { std::unique_ptr mic; Core::Timing& timing; State state{}; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& change_mic_impl_requested; + ar& buffer_full_event; + // buffer_write_event set in constructor + ar& shared_memory; + ar& client_version; + ar& allow_shell_closed; + ar& clamp; + // mic interface set in constructor + ar& state; + } + friend class boost::serialization::access; }; void MIC_U::MapSharedMem(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index 2e40ed4047..2ca95e924f 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h @@ -190,6 +190,10 @@ private: struct Impl; std::unique_ptr impl; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; void ReloadMic(Core::System& system); @@ -197,3 +201,6 @@ void ReloadMic(Core::System& system); void InstallInterfaces(Core::System& system); } // namespace Service::MIC + +SERVICE_CONSTRUCT(Service::MIC::MIC_U) +BOOST_CLASS_EXPORT_KEY(Service::MIC::MIC_U) diff --git a/src/core/hle/service/mvd/mvd_std.cpp b/src/core/hle/service/mvd/mvd_std.cpp index 2c397f2dd1..43dbde5840 100644 --- a/src/core/hle/service/mvd/mvd_std.cpp +++ b/src/core/hle/service/mvd/mvd_std.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/mvd/mvd_std.h" +SERIALIZE_EXPORT_IMPL(Service::MVD::MVD_STD) + namespace Service::MVD { MVD_STD::MVD_STD() : ServiceFramework("mvd:std", 1) { diff --git a/src/core/hle/service/mvd/mvd_std.h b/src/core/hle/service/mvd/mvd_std.h index 6764f6ba89..fed41e6f04 100644 --- a/src/core/hle/service/mvd/mvd_std.h +++ b/src/core/hle/service/mvd/mvd_std.h @@ -12,6 +12,11 @@ class MVD_STD final : public ServiceFramework { public: MVD_STD(); ~MVD_STD() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::MVD + +BOOST_CLASS_EXPORT_KEY(Service::MVD::MVD_STD) diff --git a/src/core/hle/service/ndm/ndm_u.cpp b/src/core/hle/service/ndm/ndm_u.cpp index 057e68ede7..936d537996 100644 --- a/src/core/hle/service/ndm/ndm_u.cpp +++ b/src/core/hle/service/ndm/ndm_u.cpp @@ -2,10 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/ndm/ndm_u.h" +SERIALIZE_EXPORT_IMPL(Service::NDM::NDM_U) + namespace Service::NDM { void NDM_U::EnterExclusiveState(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/ndm/ndm_u.h b/src/core/hle/service/ndm/ndm_u.h index 5f48a31824..aebc6fa8b8 100644 --- a/src/core/hle/service/ndm/ndm_u.h +++ b/src/core/hle/service/ndm/ndm_u.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "core/hle/service/service.h" namespace Core { @@ -270,8 +271,23 @@ private: u32 scan_interval = DEFAULT_SCAN_INTERVAL; u32 retry_interval = DEFAULT_RETRY_INTERVAL; bool daemon_lock_enabled = false; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& daemon_bit_mask; + ar& default_daemon_bit_mask; + ar& daemon_status; + ar& exclusive_state; + ar& scan_interval; + ar& retry_interval; + ar& daemon_lock_enabled; + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::NDM + +BOOST_CLASS_EXPORT_KEY(Service::NDM::NDM_U) diff --git a/src/core/hle/service/news/news_s.cpp b/src/core/hle/service/news/news_s.cpp index 17eaa5ea8d..94cf68f4c0 100644 --- a/src/core/hle/service/news/news_s.cpp +++ b/src/core/hle/service/news/news_s.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/news/news_s.h" +SERIALIZE_EXPORT_IMPL(Service::NEWS::NEWS_S) + namespace Service::NEWS { void NEWS_S::GetTotalNotifications(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/news/news_s.h b/src/core/hle/service/news/news_s.h index e4673d22cb..9d1ce829f7 100644 --- a/src/core/hle/service/news/news_s.h +++ b/src/core/hle/service/news/news_s.h @@ -24,6 +24,10 @@ private: * 2 : Number of notifications */ void GetTotalNotifications(Kernel::HLERequestContext& ctx); + + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NEWS + +BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_S) diff --git a/src/core/hle/service/news/news_u.cpp b/src/core/hle/service/news/news_u.cpp index 3d6e87f910..d91b594b8e 100644 --- a/src/core/hle/service/news/news_u.cpp +++ b/src/core/hle/service/news/news_u.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/news/news_u.h" +SERIALIZE_EXPORT_IMPL(Service::NEWS::NEWS_U) + namespace Service::NEWS { NEWS_U::NEWS_U() : ServiceFramework("news:u", 1) { diff --git a/src/core/hle/service/news/news_u.h b/src/core/hle/service/news/news_u.h index cb06bad39f..8e672256d4 100644 --- a/src/core/hle/service/news/news_u.h +++ b/src/core/hle/service/news/news_u.h @@ -12,6 +12,11 @@ namespace Service::NEWS { class NEWS_U final : public ServiceFramework { public: NEWS_U(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NEWS + +BOOST_CLASS_EXPORT_KEY(Service::NEWS::NEWS_U) diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 3610b5416a..ed9cbb0a32 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" @@ -10,8 +11,22 @@ #include "core/hle/service/nfc/nfc_m.h" #include "core/hle/service/nfc/nfc_u.h" +SERVICE_CONSTRUCT_IMPL(Service::NFC::Module) +SERIALIZE_EXPORT_IMPL(Service::NFC::Module) + namespace Service::NFC { +template +void Module::serialize(Archive& ar, const unsigned int) { + ar& tag_in_range_event; + ar& tag_out_of_range_event; + ar& nfc_tag_state; + ar& nfc_status; + ar& amiibo_data; + ar& amiibo_in_range; +} +SERIALIZE_IMPL(Module) + struct TagInfo { u16_le id_offset_size; u8 unk1; diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h index 64bd239acf..51fe20b76a 100644 --- a/src/core/hle/service/nfc/nfc.h +++ b/src/core/hle/service/nfc/nfc.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/common_types.h" #include "core/hle/service/service.h" @@ -35,6 +36,13 @@ struct AmiiboData { u16_be model_number; u8 series; INSERT_PADDING_BYTES(0x1C1); + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::make_binary_object(this, sizeof(AmiiboData)); + } + friend class boost::serialization::access; }; static_assert(sizeof(AmiiboData) == 0x21C, "AmiiboData is an invalid size"); @@ -226,7 +234,7 @@ public: */ void GetIdentificationBlock(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr nfc; }; @@ -241,8 +249,15 @@ private: AmiiboData amiibo_data{}; bool amiibo_in_range = false; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::NFC + +SERVICE_CONSTRUCT(Service::NFC::Module) +BOOST_CLASS_EXPORT_KEY(Service::NFC::Module) diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp index 310490b8f1..cd591b9c1b 100644 --- a/src/core/hle/service/nfc/nfc_m.cpp +++ b/src/core/hle/service/nfc/nfc_m.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nfc/nfc_m.h" +SERIALIZE_EXPORT_IMPL(Service::NFC::NFC_M) + namespace Service::NFC { NFC_M::NFC_M(std::shared_ptr nfc) : Module::Interface(std::move(nfc), "nfc:m", 1) { diff --git a/src/core/hle/service/nfc/nfc_m.h b/src/core/hle/service/nfc/nfc_m.h index c9fe9b1309..48a9e241b2 100644 --- a/src/core/hle/service/nfc/nfc_m.h +++ b/src/core/hle/service/nfc/nfc_m.h @@ -11,6 +11,12 @@ namespace Service::NFC { class NFC_M final : public Module::Interface { public: explicit NFC_M(std::shared_ptr nfc); + +private: + SERVICE_SERIALIZATION(NFC_M, nfc, Module) }; } // namespace Service::NFC + +BOOST_CLASS_EXPORT_KEY(Service::NFC::NFC_M) +BOOST_SERIALIZATION_CONSTRUCT(Service::NFC::NFC_M) diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp index a6e99ace24..58d1843e42 100644 --- a/src/core/hle/service/nfc/nfc_u.cpp +++ b/src/core/hle/service/nfc/nfc_u.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nfc/nfc_u.h" +SERIALIZE_EXPORT_IMPL(Service::NFC::NFC_U) + namespace Service::NFC { NFC_U::NFC_U(std::shared_ptr nfc) : Module::Interface(std::move(nfc), "nfc:u", 1) { diff --git a/src/core/hle/service/nfc/nfc_u.h b/src/core/hle/service/nfc/nfc_u.h index aab408269c..2ed6030e67 100644 --- a/src/core/hle/service/nfc/nfc_u.h +++ b/src/core/hle/service/nfc/nfc_u.h @@ -11,6 +11,12 @@ namespace Service::NFC { class NFC_U final : public Module::Interface { public: explicit NFC_U(std::shared_ptr nfc); + +private: + SERVICE_SERIALIZATION(NFC_U, nfc, Module) }; } // namespace Service::NFC + +BOOST_CLASS_EXPORT_KEY(Service::NFC::NFC_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::NFC::NFC_U) diff --git a/src/core/hle/service/nim/nim_aoc.cpp b/src/core/hle/service/nim/nim_aoc.cpp index ddd8d5e036..f20c96f69e 100644 --- a/src/core/hle/service/nim/nim_aoc.cpp +++ b/src/core/hle/service/nim/nim_aoc.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nim/nim_aoc.h" +SERIALIZE_EXPORT_IMPL(Service::NIM::NIM_AOC) + namespace Service::NIM { NIM_AOC::NIM_AOC() : ServiceFramework("nim:aoc", 2) { diff --git a/src/core/hle/service/nim/nim_aoc.h b/src/core/hle/service/nim/nim_aoc.h index 5a1f518ecf..003b3fd856 100644 --- a/src/core/hle/service/nim/nim_aoc.h +++ b/src/core/hle/service/nim/nim_aoc.h @@ -12,6 +12,11 @@ class NIM_AOC final : public ServiceFramework { public: NIM_AOC(); ~NIM_AOC(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NIM + +BOOST_CLASS_EXPORT_KEY(Service::NIM::NIM_AOC) diff --git a/src/core/hle/service/nim/nim_s.cpp b/src/core/hle/service/nim/nim_s.cpp index d7236249fb..27118e406a 100644 --- a/src/core/hle/service/nim/nim_s.cpp +++ b/src/core/hle/service/nim/nim_s.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nim/nim_s.h" +SERIALIZE_EXPORT_IMPL(Service::NIM::NIM_S) + namespace Service::NIM { NIM_S::NIM_S() : ServiceFramework("nim:s", 1) { diff --git a/src/core/hle/service/nim/nim_s.h b/src/core/hle/service/nim/nim_s.h index 6485cde180..10b041456a 100644 --- a/src/core/hle/service/nim/nim_s.h +++ b/src/core/hle/service/nim/nim_s.h @@ -12,6 +12,11 @@ class NIM_S final : public ServiceFramework { public: NIM_S(); ~NIM_S(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NIM + +BOOST_CLASS_EXPORT_KEY(Service::NIM::NIM_S) diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index dd0e4d31c8..b44ef35399 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp @@ -2,11 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/service/nim/nim_u.h" +SERVICE_CONSTRUCT_IMPL(Service::NIM::NIM_U) +SERIALIZE_EXPORT_IMPL(Service::NIM::NIM_U) + namespace Service::NIM { NIM_U::NIM_U(Core::System& system) : ServiceFramework("nim:u", 2) { diff --git a/src/core/hle/service/nim/nim_u.h b/src/core/hle/service/nim/nim_u.h index 367ee6ea85..98fec69b24 100644 --- a/src/core/hle/service/nim/nim_u.h +++ b/src/core/hle/service/nim/nim_u.h @@ -41,6 +41,16 @@ private: void CheckSysUpdateAvailable(Kernel::HLERequestContext& ctx); std::shared_ptr nim_system_update_event; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& nim_system_update_event; + } + friend class boost::serialization::access; }; } // namespace Service::NIM + +SERVICE_CONSTRUCT(Service::NIM::NIM_U) +BOOST_CLASS_EXPORT_KEY(Service::NIM::NIM_U) diff --git a/src/core/hle/service/nwm/nwm_cec.cpp b/src/core/hle/service/nwm/nwm_cec.cpp index 7c47c88c70..ecd4f16e19 100644 --- a/src/core/hle/service/nwm/nwm_cec.cpp +++ b/src/core/hle/service/nwm/nwm_cec.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nwm/nwm_cec.h" +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_CEC) + namespace Service::NWM { NWM_CEC::NWM_CEC() : ServiceFramework("nwm::CEC") { diff --git a/src/core/hle/service/nwm/nwm_cec.h b/src/core/hle/service/nwm/nwm_cec.h index afdf984776..674c98caec 100644 --- a/src/core/hle/service/nwm/nwm_cec.h +++ b/src/core/hle/service/nwm/nwm_cec.h @@ -11,6 +11,11 @@ namespace Service::NWM { class NWM_CEC final : public ServiceFramework { public: NWM_CEC(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NWM + +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_CEC) diff --git a/src/core/hle/service/nwm/nwm_ext.cpp b/src/core/hle/service/nwm/nwm_ext.cpp index 4bbac391fc..d69da94ffb 100644 --- a/src/core/hle/service/nwm/nwm_ext.cpp +++ b/src/core/hle/service/nwm/nwm_ext.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nwm/nwm_ext.h" +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_EXT) + namespace Service::NWM { NWM_EXT::NWM_EXT() : ServiceFramework("nwm::EXT") { diff --git a/src/core/hle/service/nwm/nwm_ext.h b/src/core/hle/service/nwm/nwm_ext.h index 1711db65ae..1e8bcfde3c 100644 --- a/src/core/hle/service/nwm/nwm_ext.h +++ b/src/core/hle/service/nwm/nwm_ext.h @@ -11,6 +11,11 @@ namespace Service::NWM { class NWM_EXT final : public ServiceFramework { public: NWM_EXT(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NWM + +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_EXT) diff --git a/src/core/hle/service/nwm/nwm_inf.cpp b/src/core/hle/service/nwm/nwm_inf.cpp index 71cf118910..eaabf66673 100644 --- a/src/core/hle/service/nwm/nwm_inf.cpp +++ b/src/core/hle/service/nwm/nwm_inf.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nwm/nwm_inf.h" +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_INF) + namespace Service::NWM { NWM_INF::NWM_INF() : ServiceFramework("nwm::INF") { diff --git a/src/core/hle/service/nwm/nwm_inf.h b/src/core/hle/service/nwm/nwm_inf.h index 2c69cfb1e3..9f8c65a2b0 100644 --- a/src/core/hle/service/nwm/nwm_inf.h +++ b/src/core/hle/service/nwm/nwm_inf.h @@ -11,6 +11,11 @@ namespace Service::NWM { class NWM_INF final : public ServiceFramework { public: NWM_INF(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NWM + +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_INF) diff --git a/src/core/hle/service/nwm/nwm_sap.cpp b/src/core/hle/service/nwm/nwm_sap.cpp index 2ef196ab40..2cedf93718 100644 --- a/src/core/hle/service/nwm/nwm_sap.cpp +++ b/src/core/hle/service/nwm/nwm_sap.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nwm/nwm_sap.h" +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_SAP) + namespace Service::NWM { NWM_SAP::NWM_SAP() : ServiceFramework("nwm::SAP") { diff --git a/src/core/hle/service/nwm/nwm_sap.h b/src/core/hle/service/nwm/nwm_sap.h index b6700b8edf..0422dc658f 100644 --- a/src/core/hle/service/nwm/nwm_sap.h +++ b/src/core/hle/service/nwm/nwm_sap.h @@ -11,6 +11,11 @@ namespace Service::NWM { class NWM_SAP final : public ServiceFramework { public: NWM_SAP(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NWM + +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_SAP) diff --git a/src/core/hle/service/nwm/nwm_soc.cpp b/src/core/hle/service/nwm/nwm_soc.cpp index 443baaf390..d6ca365aba 100644 --- a/src/core/hle/service/nwm/nwm_soc.cpp +++ b/src/core/hle/service/nwm/nwm_soc.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nwm/nwm_soc.h" +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_SOC) + namespace Service::NWM { NWM_SOC::NWM_SOC() : ServiceFramework("nwm::SOC") { diff --git a/src/core/hle/service/nwm/nwm_soc.h b/src/core/hle/service/nwm/nwm_soc.h index 8e1b922bc6..f13490e835 100644 --- a/src/core/hle/service/nwm/nwm_soc.h +++ b/src/core/hle/service/nwm/nwm_soc.h @@ -11,6 +11,11 @@ namespace Service::NWM { class NWM_SOC final : public ServiceFramework { public: NWM_SOC(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NWM + +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_SOC) diff --git a/src/core/hle/service/nwm/nwm_tst.cpp b/src/core/hle/service/nwm/nwm_tst.cpp index 3be65200b7..65ffabfd9e 100644 --- a/src/core/hle/service/nwm/nwm_tst.cpp +++ b/src/core/hle/service/nwm/nwm_tst.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/nwm/nwm_tst.h" +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_TST) + namespace Service::NWM { NWM_TST::NWM_TST() : ServiceFramework("nwm::TST") { diff --git a/src/core/hle/service/nwm/nwm_tst.h b/src/core/hle/service/nwm/nwm_tst.h index 8214e0d1d5..576d4d1244 100644 --- a/src/core/hle/service/nwm/nwm_tst.h +++ b/src/core/hle/service/nwm/nwm_tst.h @@ -11,6 +11,11 @@ namespace Service::NWM { class NWM_TST final : public ServiceFramework { public: NWM_TST(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::NWM + +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_TST) diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index fe8f7635f9..cba699f25c 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -4,7 +4,10 @@ #include #include +#include +#include #include +#include "common/archives.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" @@ -21,8 +24,20 @@ #include "core/hle/service/nwm/uds_data.h" #include "core/memory.h" +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_UDS) +SERVICE_CONSTRUCT_IMPL(Service::NWM::NWM_UDS) + namespace Service::NWM { +template +void NWM_UDS::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& node_map; + ar& connection_event; + ar& received_beacons; + // wifi_packet_received set in constructor +} + namespace ErrCodes { enum { NotInitialized = 2, @@ -489,6 +504,9 @@ void NWM_UDS::HandleDataFrame(const Network::WifiPacket& packet) { /// Callback to parse and handle a received wifi packet. void NWM_UDS::OnWifiPacketReceived(const Network::WifiPacket& packet) { + if (!initialized) { + return; + } switch (packet.type) { case Network::WifiPacket::PacketType::Beacon: HandleBeaconFrame(packet); @@ -534,8 +552,7 @@ boost::optional NWM_UDS::GetNodeMacAddress(u16 dest_node_id void NWM_UDS::Shutdown(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x03, 0, 0); - if (auto room_member = Network::GetRoomMember().lock()) - room_member->Unbind(wifi_packet_received); + initialized = false; for (auto bind_node : channel_data) { bind_node.second.event->Signal(); @@ -625,13 +642,6 @@ ResultVal> NWM_UDS::Initialize( recv_buffer_memory = std::move(sharedmem); ASSERT_MSG(recv_buffer_memory->GetSize() == sharedmem_size, "Invalid shared memory size."); - if (auto room_member = Network::GetRoomMember().lock()) { - wifi_packet_received = room_member->BindOnWifiPacketReceived( - [this](const Network::WifiPacket& packet) { OnWifiPacketReceived(packet); }); - } else { - LOG_ERROR(Service_NWM, "Network isn't initalized"); - } - { std::lock_guard lock(connection_status_mutex); @@ -1165,6 +1175,30 @@ void NWM_UDS::GetChannel(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NWM, "called"); } +class NWM_UDS::ThreadCallback : public Kernel::HLERequestContext::WakeupCallback { +public: + explicit ThreadCallback(u16 command_id_) : command_id(command_id_) {} + + void WakeUp(std::shared_ptr thread, Kernel::HLERequestContext& ctx, + Kernel::ThreadWakeupReason reason) { + // TODO(B3N30): Add error handling for host full and timeout + IPC::RequestBuilder rb(ctx, command_id, 1, 0); + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_NWM, "connection sequence finished"); + } + +private: + ThreadCallback() = default; + u16 command_id; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& command_id; + } + friend class boost::serialization::access; +}; + void NWM_UDS::ConnectToNetwork(Kernel::HLERequestContext& ctx, u16 command_id, const u8* network_info_buffer, std::size_t network_info_size, u8 connection_type, std::vector passphrase) { @@ -1178,15 +1212,8 @@ void NWM_UDS::ConnectToNetwork(Kernel::HLERequestContext& ctx, u16 command_id, // Since this timing is handled by core_timing it could differ from the 'real world' time static constexpr std::chrono::nanoseconds UDSConnectionTimeout{300000000}; - connection_event = ctx.SleepClientThread( - "uds::ConnectToNetwork", UDSConnectionTimeout, - [command_id](std::shared_ptr thread, Kernel::HLERequestContext& ctx, - Kernel::ThreadWakeupReason reason) { - // TODO(B3N30): Add error handling for host full and timeout - IPC::RequestBuilder rb(ctx, command_id, 1, 0); - rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_NWM, "connection sequence finished"); - }); + connection_event = ctx.SleepClientThread("uds::ConnectToNetwork", UDSConnectionTimeout, + std::make_shared(command_id)); } void NWM_UDS::ConnectToNetwork(Kernel::HLERequestContext& ctx) { @@ -1396,6 +1423,13 @@ NWM_UDS::NWM_UDS(Core::System& system) : ServiceFramework("nwm::UDS"), system(sy system.Kernel().GetSharedPageHandler().SetMacAddress(mac); system.Kernel().GetSharedPageHandler().SetWifiLinkLevel(SharedPage::WifiLinkLevel::BEST); + + if (auto room_member = Network::GetRoomMember().lock()) { + wifi_packet_received = room_member->BindOnWifiPacketReceived( + [this](const Network::WifiPacket& packet) { OnWifiPacketReceived(packet); }); + } else { + LOG_ERROR(Service_NWM, "Network isn't initalized"); + } } NWM_UDS::~NWM_UDS() { @@ -1406,3 +1440,5 @@ NWM_UDS::~NWM_UDS() { } } // namespace Service::NWM + +SERIALIZE_EXPORT_IMPL(Service::NWM::NWM_UDS::ThreadCallback) diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 52ca386635..7ae471a538 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/service.h" @@ -127,6 +128,8 @@ public: explicit NWM_UDS(Core::System& system); ~NWM_UDS(); + class ThreadCallback; + private: Core::System& system; @@ -521,6 +524,14 @@ private: struct Node { bool connected; u16 node_id; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& connected; + ar& node_id; + } + friend class boost::serialization::access; }; std::map node_map; @@ -543,6 +554,14 @@ private: // List of the last beacons received from the network. std::list received_beacons; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; } // namespace Service::NWM + +SERVICE_CONSTRUCT(Service::NWM::NWM_UDS) +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_UDS) +BOOST_CLASS_EXPORT_KEY(Service::NWM::NWM_UDS::ThreadCallback) diff --git a/src/core/hle/service/pm/pm_app.cpp b/src/core/hle/service/pm/pm_app.cpp index 9599dfcfcf..fd08583565 100644 --- a/src/core/hle/service/pm/pm_app.cpp +++ b/src/core/hle/service/pm/pm_app.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/pm/pm_app.h" +SERIALIZE_EXPORT_IMPL(Service::PM::PM_APP) + namespace Service::PM { PM_APP::PM_APP() : ServiceFramework("pm:app", 3) { diff --git a/src/core/hle/service/pm/pm_app.h b/src/core/hle/service/pm/pm_app.h index 8c7e375f0b..9aefb0cee1 100644 --- a/src/core/hle/service/pm/pm_app.h +++ b/src/core/hle/service/pm/pm_app.h @@ -12,6 +12,11 @@ class PM_APP final : public ServiceFramework { public: PM_APP(); ~PM_APP() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::PM + +BOOST_CLASS_EXPORT_KEY(Service::PM::PM_APP) diff --git a/src/core/hle/service/pm/pm_dbg.cpp b/src/core/hle/service/pm/pm_dbg.cpp index 63879ff201..33e195a6fd 100644 --- a/src/core/hle/service/pm/pm_dbg.cpp +++ b/src/core/hle/service/pm/pm_dbg.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/pm/pm_dbg.h" +SERIALIZE_EXPORT_IMPL(Service::PM::PM_DBG) + namespace Service::PM { PM_DBG::PM_DBG() : ServiceFramework("pm:dbg", 3) { diff --git a/src/core/hle/service/pm/pm_dbg.h b/src/core/hle/service/pm/pm_dbg.h index 77b6449698..3a6e3c5c74 100644 --- a/src/core/hle/service/pm/pm_dbg.h +++ b/src/core/hle/service/pm/pm_dbg.h @@ -12,6 +12,11 @@ class PM_DBG final : public ServiceFramework { public: PM_DBG(); ~PM_DBG() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::PM + +BOOST_CLASS_EXPORT_KEY(Service::PM::PM_DBG) diff --git a/src/core/hle/service/ps/ps_ps.cpp b/src/core/hle/service/ps/ps_ps.cpp index a489033482..1ba4d7f4c9 100644 --- a/src/core/hle/service/ps/ps_ps.cpp +++ b/src/core/hle/service/ps/ps_ps.cpp @@ -4,6 +4,7 @@ #include #include +#include "common/archives.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" @@ -11,6 +12,8 @@ #include "core/hw/aes/arithmetic128.h" #include "core/hw/aes/key.h" +SERIALIZE_EXPORT_IMPL(Service::PS::PS_PS) + namespace Service::PS { enum class AlgorithmType : u8 { diff --git a/src/core/hle/service/ps/ps_ps.h b/src/core/hle/service/ps/ps_ps.h index 6e8b3ad0a2..f5005d4441 100644 --- a/src/core/hle/service/ps/ps_ps.h +++ b/src/core/hle/service/ps/ps_ps.h @@ -18,6 +18,8 @@ public: ~PS_PS() = default; private: + SERVICE_SERIALIZATION_SIMPLE + /** * PS_PS::SignRsaSha256 service function * Inputs: @@ -231,3 +233,5 @@ private: void InstallInterfaces(Core::System& system); } // namespace Service::PS + +BOOST_CLASS_EXPORT_KEY(Service::PS::PS_PS) diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 4af8291bfa..430d48cdd9 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -18,6 +19,8 @@ #include "core/hle/service/ptm/ptm_u.h" #include "core/settings.h" +SERIALIZE_EXPORT_IMPL(Service::PTM::Module) + namespace Service::PTM { /// Values for the default gamecoin.dat file diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index 549e693538..ba23224d78 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -137,7 +137,7 @@ public: */ void CheckNew3DS(Kernel::HLERequestContext& ctx); - private: + protected: std::shared_ptr ptm; }; @@ -145,8 +145,18 @@ private: bool shell_open = true; bool battery_is_charging = true; bool pedometer_is_counting = false; + + template + void serialize(Archive& ar, const unsigned int) { + ar& shell_open; + ar& battery_is_charging; + ar& pedometer_is_counting; + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::PTM + +BOOST_CLASS_EXPORT_KEY(Service::PTM::Module) diff --git a/src/core/hle/service/ptm/ptm_gets.cpp b/src/core/hle/service/ptm/ptm_gets.cpp index e083aed6a9..6feedbf94d 100644 --- a/src/core/hle/service/ptm/ptm_gets.cpp +++ b/src/core/hle/service/ptm/ptm_gets.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ptm/ptm_gets.h" +SERIALIZE_EXPORT_IMPL(Service::PTM::PTM_Gets) + namespace Service::PTM { PTM_Gets::PTM_Gets(std::shared_ptr ptm) diff --git a/src/core/hle/service/ptm/ptm_gets.h b/src/core/hle/service/ptm/ptm_gets.h index a8d71fdc98..57b8e5f550 100644 --- a/src/core/hle/service/ptm/ptm_gets.h +++ b/src/core/hle/service/ptm/ptm_gets.h @@ -12,6 +12,12 @@ namespace Service::PTM { class PTM_Gets final : public Module::Interface { public: explicit PTM_Gets(std::shared_ptr ptm); + +private: + SERVICE_SERIALIZATION(PTM_Gets, ptm, Module) }; } // namespace Service::PTM + +BOOST_CLASS_EXPORT_KEY(Service::PTM::PTM_Gets) +BOOST_SERIALIZATION_CONSTRUCT(Service::PTM::PTM_Gets) diff --git a/src/core/hle/service/ptm/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp index 6ef45780ed..00585ccced 100644 --- a/src/core/hle/service/ptm/ptm_play.cpp +++ b/src/core/hle/service/ptm/ptm_play.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ptm/ptm_play.h" +SERIALIZE_EXPORT_IMPL(Service::PTM::PTM_Play) + namespace Service::PTM { PTM_Play::PTM_Play(std::shared_ptr ptm) diff --git a/src/core/hle/service/ptm/ptm_play.h b/src/core/hle/service/ptm/ptm_play.h index 3a226149df..091e91d30e 100644 --- a/src/core/hle/service/ptm/ptm_play.h +++ b/src/core/hle/service/ptm/ptm_play.h @@ -12,6 +12,12 @@ namespace Service::PTM { class PTM_Play final : public Module::Interface { public: explicit PTM_Play(std::shared_ptr ptm); + +private: + SERVICE_SERIALIZATION(PTM_Play, ptm, Module) }; } // namespace Service::PTM + +BOOST_CLASS_EXPORT_KEY(Service::PTM::PTM_Play) +BOOST_SERIALIZATION_CONSTRUCT(Service::PTM::PTM_Play) diff --git a/src/core/hle/service/ptm/ptm_sets.cpp b/src/core/hle/service/ptm/ptm_sets.cpp index b925f49c95..e0f436ddc7 100644 --- a/src/core/hle/service/ptm/ptm_sets.cpp +++ b/src/core/hle/service/ptm/ptm_sets.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ptm/ptm_sets.h" +SERIALIZE_EXPORT_IMPL(Service::PTM::PTM_Sets) + namespace Service::PTM { PTM_Sets::PTM_Sets(std::shared_ptr ptm) : Module::Interface(std::move(ptm), "ptm:sets", 1) { diff --git a/src/core/hle/service/ptm/ptm_sets.h b/src/core/hle/service/ptm/ptm_sets.h index 317781faf1..573b20dbde 100644 --- a/src/core/hle/service/ptm/ptm_sets.h +++ b/src/core/hle/service/ptm/ptm_sets.h @@ -12,6 +12,12 @@ namespace Service::PTM { class PTM_Sets final : public Module::Interface { public: explicit PTM_Sets(std::shared_ptr ptm); + +private: + SERVICE_SERIALIZATION(PTM_Sets, ptm, Module) }; } // namespace Service::PTM + +BOOST_CLASS_EXPORT_KEY(Service::PTM::PTM_Sets) +BOOST_SERIALIZATION_CONSTRUCT(Service::PTM::PTM_Sets) diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp index 45ee1b6fc5..48b44104e7 100644 --- a/src/core/hle/service/ptm/ptm_sysm.cpp +++ b/src/core/hle/service/ptm/ptm_sysm.cpp @@ -2,8 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ptm/ptm_sysm.h" +SERIALIZE_EXPORT_IMPL(Service::PTM::PTM_S) +SERIALIZE_EXPORT_IMPL(Service::PTM::PTM_Sysm) + namespace Service::PTM { PTM_S_Common::PTM_S_Common(std::shared_ptr ptm, const char* name) diff --git a/src/core/hle/service/ptm/ptm_sysm.h b/src/core/hle/service/ptm/ptm_sysm.h index 8667f2a954..1e01fdb2e6 100644 --- a/src/core/hle/service/ptm/ptm_sysm.h +++ b/src/core/hle/service/ptm/ptm_sysm.h @@ -17,11 +17,22 @@ public: class PTM_S final : public PTM_S_Common { public: explicit PTM_S(std::shared_ptr ptm); + +private: + SERVICE_SERIALIZATION(PTM_S, ptm, Module) }; class PTM_Sysm final : public PTM_S_Common { public: explicit PTM_Sysm(std::shared_ptr ptm); + +private: + SERVICE_SERIALIZATION(PTM_Sysm, ptm, Module) }; } // namespace Service::PTM + +BOOST_CLASS_EXPORT_KEY(Service::PTM::PTM_S) +BOOST_CLASS_EXPORT_KEY(Service::PTM::PTM_Sysm) +BOOST_SERIALIZATION_CONSTRUCT(Service::PTM::PTM_S) +BOOST_SERIALIZATION_CONSTRUCT(Service::PTM::PTM_Sysm) diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp index 647ef59613..4c1820df45 100644 --- a/src/core/hle/service/ptm/ptm_u.cpp +++ b/src/core/hle/service/ptm/ptm_u.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/ptm/ptm_u.h" +SERIALIZE_EXPORT_IMPL(Service::PTM::PTM_U) + namespace Service::PTM { PTM_U::PTM_U(std::shared_ptr ptm) : Module::Interface(std::move(ptm), "ptm:u", 26) { diff --git a/src/core/hle/service/ptm/ptm_u.h b/src/core/hle/service/ptm/ptm_u.h index 618401cec3..213972242b 100644 --- a/src/core/hle/service/ptm/ptm_u.h +++ b/src/core/hle/service/ptm/ptm_u.h @@ -12,6 +12,12 @@ namespace Service::PTM { class PTM_U final : public Module::Interface { public: explicit PTM_U(std::shared_ptr ptm); + +private: + SERVICE_SERIALIZATION(PTM_U, ptm, Module) }; } // namespace Service::PTM + +BOOST_CLASS_EXPORT_KEY(Service::PTM::PTM_U) +BOOST_SERIALIZATION_CONSTRUCT(Service::PTM::PTM_U) diff --git a/src/core/hle/service/pxi/dev.cpp b/src/core/hle/service/pxi/dev.cpp index dcea938a64..1135516905 100644 --- a/src/core/hle/service/pxi/dev.cpp +++ b/src/core/hle/service/pxi/dev.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/service/pxi/dev.h" +SERIALIZE_EXPORT_IMPL(Service::PXI::DEV) + namespace Service::PXI { DEV::DEV() : ServiceFramework("pxi:dev", 1) { diff --git a/src/core/hle/service/pxi/dev.h b/src/core/hle/service/pxi/dev.h index 115dc23084..f16491077b 100644 --- a/src/core/hle/service/pxi/dev.h +++ b/src/core/hle/service/pxi/dev.h @@ -13,6 +13,11 @@ class DEV final : public ServiceFramework { public: DEV(); ~DEV(); + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::PXI + +BOOST_CLASS_EXPORT_KEY(Service::PXI::DEV) diff --git a/src/core/hle/service/qtm/qtm_c.cpp b/src/core/hle/service/qtm/qtm_c.cpp index 84baaba3ad..2adc896dff 100644 --- a/src/core/hle/service/qtm/qtm_c.cpp +++ b/src/core/hle/service/qtm/qtm_c.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/qtm/qtm_c.h" +SERIALIZE_EXPORT_IMPL(Service::QTM::QTM_C) + namespace Service::QTM { QTM_C::QTM_C() : ServiceFramework("qtm:c", 2) { diff --git a/src/core/hle/service/qtm/qtm_c.h b/src/core/hle/service/qtm/qtm_c.h index c9cad0329c..b601cd9de1 100644 --- a/src/core/hle/service/qtm/qtm_c.h +++ b/src/core/hle/service/qtm/qtm_c.h @@ -12,6 +12,11 @@ class QTM_C final : public ServiceFramework { public: QTM_C(); ~QTM_C() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::QTM + +BOOST_CLASS_EXPORT_KEY(Service::QTM::QTM_C) diff --git a/src/core/hle/service/qtm/qtm_s.cpp b/src/core/hle/service/qtm/qtm_s.cpp index 2af7ced7b7..6163ef4dc9 100644 --- a/src/core/hle/service/qtm/qtm_s.cpp +++ b/src/core/hle/service/qtm/qtm_s.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/qtm/qtm_s.h" +SERIALIZE_EXPORT_IMPL(Service::QTM::QTM_S) + namespace Service::QTM { QTM_S::QTM_S() : ServiceFramework("qtm:s", 2) { diff --git a/src/core/hle/service/qtm/qtm_s.h b/src/core/hle/service/qtm/qtm_s.h index 72b5e058b1..b32a497dbd 100644 --- a/src/core/hle/service/qtm/qtm_s.h +++ b/src/core/hle/service/qtm/qtm_s.h @@ -12,6 +12,11 @@ class QTM_S final : public ServiceFramework { public: QTM_S(); ~QTM_S() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::QTM + +BOOST_CLASS_EXPORT_KEY(Service::QTM::QTM_S) diff --git a/src/core/hle/service/qtm/qtm_sp.cpp b/src/core/hle/service/qtm/qtm_sp.cpp index bd5a716050..fdfc80003c 100644 --- a/src/core/hle/service/qtm/qtm_sp.cpp +++ b/src/core/hle/service/qtm/qtm_sp.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/qtm/qtm_sp.h" +SERIALIZE_EXPORT_IMPL(Service::QTM::QTM_SP) + namespace Service::QTM { QTM_SP::QTM_SP() : ServiceFramework("qtm:sp", 2) { diff --git a/src/core/hle/service/qtm/qtm_sp.h b/src/core/hle/service/qtm/qtm_sp.h index c3f1049a1d..2f290f1922 100644 --- a/src/core/hle/service/qtm/qtm_sp.h +++ b/src/core/hle/service/qtm/qtm_sp.h @@ -12,6 +12,11 @@ class QTM_SP final : public ServiceFramework { public: QTM_SP(); ~QTM_SP() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::QTM + +BOOST_CLASS_EXPORT_KEY(Service::QTM::QTM_SP) diff --git a/src/core/hle/service/qtm/qtm_u.cpp b/src/core/hle/service/qtm/qtm_u.cpp index 471692189d..84415dde9c 100644 --- a/src/core/hle/service/qtm/qtm_u.cpp +++ b/src/core/hle/service/qtm/qtm_u.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/qtm/qtm_u.h" +SERIALIZE_EXPORT_IMPL(Service::QTM::QTM_U) + namespace Service::QTM { QTM_U::QTM_U() : ServiceFramework("qtm:u", 2) { diff --git a/src/core/hle/service/qtm/qtm_u.h b/src/core/hle/service/qtm/qtm_u.h index 01bb1e6e08..d1aee1e971 100644 --- a/src/core/hle/service/qtm/qtm_u.h +++ b/src/core/hle/service/qtm/qtm_u.h @@ -12,6 +12,11 @@ class QTM_U final : public ServiceFramework { public: QTM_U(); ~QTM_U() = default; + +private: + SERVICE_SERIALIZATION_SIMPLE }; } // namespace Service::QTM + +BOOST_CLASS_EXPORT_KEY(Service::QTM::QTM_U) diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index db6a0ad233..83b0a5fb1e 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -10,7 +10,10 @@ #include #include #include +#include +#include #include "common/common_types.h" +#include "common/construct.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/object.h" #include "core/hle/service/sm/sm.h" @@ -85,6 +88,7 @@ private: using InvokerFn = void(ServiceFrameworkBase* object, HandlerFnP member, Kernel::HLERequestContext& ctx); + // TODO: Replace all these with virtual functions! ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); ~ServiceFrameworkBase() override; @@ -193,3 +197,45 @@ struct ServiceModuleInfo { extern const std::array service_module_map; } // namespace Service + +#define SERVICE_SERIALIZATION(T, MFIELD, TMODULE) \ + template \ + void save_construct(Archive& ar, const unsigned int file_version) const { \ + ar << MFIELD; \ + } \ + \ + template \ + static void load_construct(Archive& ar, T* t, const unsigned int file_version) { \ + std::shared_ptr MFIELD; \ + ar >> MFIELD; \ + ::new (t) T(MFIELD); \ + } \ + \ + template \ + void serialize(Archive& ar, const unsigned int) { \ + ar& boost::serialization::base_object(*this); \ + } \ + friend class boost::serialization::access; \ + friend class ::construct_access; + +#define SERVICE_SERIALIZATION_SIMPLE \ + template \ + void serialize(Archive& ar, const unsigned int) { \ + ar& boost::serialization::base_object(*this); \ + } \ + friend class boost::serialization::access; + +#define SERVICE_CONSTRUCT(T) \ + namespace boost::serialization { \ + template \ + void load_construct_data(Archive& ar, T* t, const unsigned int); \ + } + +#define SERVICE_CONSTRUCT_IMPL(T) \ + namespace boost::serialization { \ + template \ + void load_construct_data(Archive& ar, T* t, const unsigned int) { \ + ::new (t) T(Core::Global()); \ + } \ + template void load_construct_data(iarchive & ar, T* t, const unsigned int); \ + } diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 6e47fd1525..659e14afb7 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -8,6 +8,10 @@ #include #include #include +#include +#include +#include +#include #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" @@ -80,6 +84,24 @@ private: // For IPC Recorder /// client port Object id -> service name std::unordered_map registered_services_inverse; + + template + void save(Archive& ar, const unsigned int file_version) const { + ar << registered_services; + } + + template + void load(Archive& ar, const unsigned int file_version) { + ar >> registered_services; + registered_services_inverse.clear(); + for (const auto& pair : registered_services) { + registered_services_inverse.emplace(pair.second->GetObjectId(), pair.first); + } + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() + + friend class boost::serialization::access; }; } // namespace Service::SM diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 396bd3559e..da6d57df7f 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -3,6 +3,10 @@ // Refer to the license.txt file included. #include +#include +#include +#include +#include "common/archives.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" @@ -20,8 +24,18 @@ #include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/srv.h" +SERVICE_CONSTRUCT_IMPL(Service::SM::SRV) +SERIALIZE_EXPORT_IMPL(Service::SM::SRV) + namespace Service::SM { +template +void SRV::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& notification_semaphore; + ar& get_service_handle_delayed_map; +} + constexpr int MAX_PENDING_NOTIFICATIONS = 16; /** @@ -71,6 +85,48 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_SRV, "(STUBBED) called"); } +class SRV::ThreadCallback : public Kernel::HLERequestContext::WakeupCallback { + +public: + explicit ThreadCallback(Core::System& system_, std::string name_) + : system(system_), name(name_) {} + + void WakeUp(std::shared_ptr thread, Kernel::HLERequestContext& ctx, + Kernel::ThreadWakeupReason reason) { + LOG_ERROR(Service_SRV, "called service={} wakeup", name); + auto client_port = system.ServiceManager().GetServicePort(name); + + auto session = client_port.Unwrap()->Connect(); + if (session.Succeeded()) { + LOG_DEBUG(Service_SRV, "called service={} -> session={}", name, + (*session)->GetObjectId()); + IPC::RequestBuilder rb(ctx, 0x5, 1, 2); + rb.Push(session.Code()); + rb.PushMoveObjects(std::move(session).Unwrap()); + } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED) { + LOG_ERROR(Service_SRV, "called service={} -> ERR_MAX_CONNECTIONS_REACHED", name); + UNREACHABLE(); + } else { + LOG_ERROR(Service_SRV, "called service={} -> error 0x{:08X}", name, session.Code().raw); + IPC::RequestBuilder rb(ctx, 0x5, 1, 0); + rb.Push(session.Code()); + } + } + +private: + Core::System& system; + std::string name; + + ThreadCallback() : system(Core::Global()) {} + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& name; + } + friend class boost::serialization::access; +}; + /** * SRV::GetServiceHandle service function * Inputs: @@ -100,28 +156,7 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { // TODO(yuriks): Permission checks go here - auto get_handle = [name, this](std::shared_ptr thread, - Kernel::HLERequestContext& ctx, - Kernel::ThreadWakeupReason reason) { - LOG_ERROR(Service_SRV, "called service={} wakeup", name); - auto client_port = system.ServiceManager().GetServicePort(name); - - auto session = client_port.Unwrap()->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service_SRV, "called service={} -> session={}", name, - (*session)->GetObjectId()); - IPC::RequestBuilder rb(ctx, 0x5, 1, 2); - rb.Push(session.Code()); - rb.PushMoveObjects(std::move(session).Unwrap()); - } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED) { - LOG_ERROR(Service_SRV, "called service={} -> ERR_MAX_CONNECTIONS_REACHED", name); - UNREACHABLE(); - } else { - LOG_ERROR(Service_SRV, "called service={} -> error 0x{:08X}", name, session.Code().raw); - IPC::RequestBuilder rb(ctx, 0x5, 1, 0); - rb.Push(session.Code()); - } - }; + auto get_handle = std::make_shared(system, name); auto client_port = system.ServiceManager().GetServicePort(name); if (client_port.Failed()) { @@ -266,3 +301,5 @@ SRV::SRV(Core::System& system) : ServiceFramework("srv:", 4), system(system) { SRV::~SRV() = default; } // namespace Service::SM + +SERIALIZE_EXPORT_IMPL(Service::SM::SRV::ThreadCallback) diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h index 2382f48425..753218dca5 100644 --- a/src/core/hle/service/sm/srv.h +++ b/src/core/hle/service/sm/srv.h @@ -6,6 +6,7 @@ #include #include +#include #include "core/hle/service/service.h" namespace Core { @@ -25,6 +26,8 @@ public: explicit SRV(Core::System& system); ~SRV(); + class ThreadCallback; + private: void RegisterClient(Kernel::HLERequestContext& ctx); void EnableNotification(Kernel::HLERequestContext& ctx); @@ -37,6 +40,14 @@ private: Core::System& system; std::shared_ptr notification_semaphore; std::unordered_map> get_service_handle_delayed_map; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; } // namespace Service::SM + +SERVICE_CONSTRUCT(Service::SM::SRV) +BOOST_CLASS_EXPORT_KEY(Service::SM::SRV) +BOOST_CLASS_EXPORT_KEY(Service::SM::SRV::ThreadCallback) diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 5ebe553563..56b48cd3a5 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "common/archives.h" #include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.h" @@ -52,6 +53,8 @@ #define closesocket(x) close(x) #endif +SERIALIZE_EXPORT_IMPL(Service::SOC::SOC_U) + namespace Service::SOC { const s32 SOCKET_ERROR_VALUE = -1; diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h index 273aac49c7..595a984f2f 100644 --- a/src/core/hle/service/soc_u.h +++ b/src/core/hle/service/soc_u.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "core/hle/service/service.h" namespace Core { @@ -17,6 +18,14 @@ namespace Service::SOC { struct SocketHolder { u32 socket_fd; ///< The socket descriptor bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows. + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& socket_fd; + ar& blocking; + } + friend class boost::serialization::access; }; class SOC_U final : public ServiceFramework { @@ -55,8 +64,17 @@ private: /// Holds info about the currently open sockets std::unordered_map open_sockets; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& open_sockets; + } + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::SOC + +BOOST_CLASS_EXPORT_KEY(Service::SOC::SOC_U) diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index b862c41f1e..8e74c1b7cf 100644 --- a/src/core/hle/service/ssl_c.cpp +++ b/src/core/hle/service/ssl_c.cpp @@ -2,12 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/archives.h" #include "common/common_types.h" #include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/ssl_c.h" +SERIALIZE_EXPORT_IMPL(Service::SSL::SSL_C) namespace Service::SSL { void SSL_C::Initialize(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/ssl_c.h b/src/core/hle/service/ssl_c.h index 807f7c7255..30b87378a0 100644 --- a/src/core/hle/service/ssl_c.h +++ b/src/core/hle/service/ssl_c.h @@ -23,8 +23,12 @@ private: // TODO: Implement a proper CSPRNG in the future when actual security is needed std::mt19937 rand_gen; + + SERVICE_SERIALIZATION_SIMPLE }; void InstallInterfaces(Core::System& system); } // namespace Service::SSL + +BOOST_CLASS_EXPORT_KEY(Service::SSL::SSL_C) diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 356ebd8436..89da8058c4 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/common_funcs.h" #include "common/logging/log.h" #include "core/core.h" @@ -12,8 +13,22 @@ #include "core/hle/service/y2r_u.h" #include "core/hw/y2r.h" +SERVICE_CONSTRUCT_IMPL(Service::Y2R::Y2R_U) +SERIALIZE_EXPORT_IMPL(Service::Y2R::Y2R_U) + namespace Service::Y2R { +template +void Y2R_U::serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + ar& completion_event; + ar& conversion; + ar& dithering_weight_params; + ar& temporal_dithering_enabled; + ar& transfer_end_interrupt_enabled; + ar& spacial_dithering_enabled; +} + static const CoefficientSet standard_coefficients[4] = { {{0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B}}, // ITU_Rec601 {{0x100, 0x193, 0x77, 0x2F, 0x1DB, -0x1933, 0xA7C, -0x1D51}}, // ITU_Rec709 diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 332d3b240f..1ac675f92c 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "common/common_types.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -91,6 +92,16 @@ struct ConversionBuffer { u16 transfer_unit; /// Amount of bytes to be skipped between copying each `transfer_unit` bytes. u16 gap; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& address; + ar& image_size; + ar& transfer_unit; + ar& gap; + } + friend class boost::serialization::access; }; struct ConversionConfiguration { @@ -112,6 +123,26 @@ struct ConversionConfiguration { ResultCode SetInputLineWidth(u16 width); ResultCode SetInputLines(u16 lines); ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient); + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& input_format; + ar& output_format; + ar& rotation; + ar& block_alignment; + ar& input_line_width; + ar& input_lines; + ar& coefficients; + ar& padding; + ar& alpha; + ar& src_Y; + ar& src_U; + ar& src_V; + ar& src_YUYV; + ar& dst; + } + friend class boost::serialization::access; }; struct DitheringWeightParams { @@ -131,6 +162,28 @@ struct DitheringWeightParams { u16 w3_xOdd_yEven; u16 w3_xEven_yOdd; u16 w3_xOdd_yOdd; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& w0_xEven_yEven; + ar& w0_xOdd_yEven; + ar& w0_xEven_yOdd; + ar& w0_xOdd_yOdd; + ar& w1_xEven_yEven; + ar& w1_xOdd_yEven; + ar& w1_xEven_yOdd; + ar& w1_xOdd_yOdd; + ar& w2_xEven_yEven; + ar& w2_xOdd_yEven; + ar& w2_xEven_yOdd; + ar& w2_xOdd_yOdd; + ar& w3_xEven_yEven; + ar& w3_xOdd_yEven; + ar& w3_xEven_yOdd; + ar& w3_xOdd_yOdd; + } + friend class boost::serialization::access; }; struct ConversionParameters { @@ -301,8 +354,15 @@ private: bool temporal_dithering_enabled = false; bool transfer_end_interrupt_enabled = false; bool spacial_dithering_enabled = false; + + template + void serialize(Archive& ar, const unsigned int); + friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::Y2R + +SERVICE_CONSTRUCT(Service::Y2R::Y2R_U) +BOOST_CLASS_EXPORT_KEY(Service::Y2R::Y2R_U) diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index f641afae46..4f6a31441b 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -472,14 +472,7 @@ inline void Write(u32 addr, const T data) { if (config.trigger & 1) { MICROPROFILE_SCOPE(GPU_CmdlistProcessing); - u32* buffer = (u32*)g_memory->GetPhysicalPointer(config.GetPhysicalAddress()); - - if (Pica::g_debug_context && Pica::g_debug_context->recorder) { - Pica::g_debug_context->recorder->MemoryAccessed((u8*)buffer, config.size, - config.GetPhysicalAddress()); - } - - Pica::CommandProcessor::ProcessCommandList(buffer, config.size); + Pica::CommandProcessor::ProcessCommandList(config.GetPhysicalAddress(), config.size); g_regs.command_processor_config.trigger = 0; } diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index ac30bc22ee..3252364e25 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -6,6 +6,8 @@ #include #include +#include +#include #include "common/assert.h" #include "common/bit_field.h" #include "common/common_funcs.h" @@ -270,6 +272,12 @@ private: static inline u32 DecodeAddressRegister(u32 register_value) { return register_value * 8; } + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::make_binary_object(this, sizeof(Regs)); + } + friend class boost::serialization::access; }; static_assert(std::is_standard_layout::value, "Structure does not use standard layout"); diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h index 5e37121f70..9667c7c4c0 100644 --- a/src/core/hw/lcd.h +++ b/src/core/hw/lcd.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -50,6 +51,16 @@ struct Regs { u32* content = reinterpret_cast(this); return content[index]; } + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& color_fill_top.raw; + ar& backlight_top; + ar& color_fill_bottom.raw; + ar& backlight_bottom; + } + friend class boost::serialization::access; }; static_assert(std::is_standard_layout::value, "Structure does not use standard layout"); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 096f4c6977..c6a213ff06 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -4,22 +4,38 @@ #include #include +#include +#include #include "audio_core/dsp_interface.h" +#include "common/archives.h" #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" #include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/global.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/lock.h" #include "core/memory.h" +#include "core/settings.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" +SERIALIZE_EXPORT_IMPL(Memory::MemorySystem::BackingMemImpl) +SERIALIZE_EXPORT_IMPL(Memory::MemorySystem::BackingMemImpl) +SERIALIZE_EXPORT_IMPL(Memory::MemorySystem::BackingMemImpl) +SERIALIZE_EXPORT_IMPL(Memory::MemorySystem::BackingMemImpl) + namespace Memory { +void PageTable::Clear() { + pointers.raw.fill(nullptr); + pointers.refs.fill(MemoryRef()); + attributes.fill(PageType::Unmapped); +} + class RasterizerCacheMarker { public: void Mark(VAddr addr, bool cached) { @@ -52,6 +68,15 @@ private: std::array vram{}; std::array linear_heap{}; std::array new_linear_heap{}; + + static_assert(sizeof(bool) == 1); + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& vram; + ar& linear_heap; + ar& new_linear_heap; + } }; class MemorySystem::Impl { @@ -62,26 +87,140 @@ public: std::unique_ptr vram = std::make_unique(Memory::VRAM_SIZE); std::unique_ptr n3ds_extra_ram = std::make_unique(Memory::N3DS_EXTRA_RAM_SIZE); - PageTable* current_page_table = nullptr; + std::shared_ptr current_page_table = nullptr; RasterizerCacheMarker cache_marker; - std::vector page_table_list; + std::vector> page_table_list; AudioCore::DspInterface* dsp = nullptr; + + std::shared_ptr fcram_mem; + std::shared_ptr vram_mem; + std::shared_ptr n3ds_extra_ram_mem; + std::shared_ptr dsp_mem; + + Impl(); + + const u8* GetPtr(Region r) const { + switch (r) { + case Region::VRAM: + return vram.get(); + case Region::DSP: + return dsp->GetDspMemory().data(); + case Region::FCRAM: + return fcram.get(); + case Region::N3DS: + return n3ds_extra_ram.get(); + default: + UNREACHABLE(); + } + } + + u8* GetPtr(Region r) { + switch (r) { + case Region::VRAM: + return vram.get(); + case Region::DSP: + return dsp->GetDspMemory().data(); + case Region::FCRAM: + return fcram.get(); + case Region::N3DS: + return n3ds_extra_ram.get(); + default: + UNREACHABLE(); + } + } + + u32 GetSize(Region r) const { + switch (r) { + case Region::VRAM: + return VRAM_SIZE; + case Region::DSP: + return DSP_RAM_SIZE; + case Region::FCRAM: + return FCRAM_N3DS_SIZE; + case Region::N3DS: + return N3DS_EXTRA_RAM_SIZE; + default: + UNREACHABLE(); + } + } + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + bool save_n3ds_ram = Settings::values.is_new_3ds; + ar& save_n3ds_ram; + ar& boost::serialization::make_binary_object(vram.get(), Memory::VRAM_SIZE); + ar& boost::serialization::make_binary_object( + fcram.get(), save_n3ds_ram ? Memory::FCRAM_N3DS_SIZE : Memory::FCRAM_SIZE); + ar& boost::serialization::make_binary_object( + n3ds_extra_ram.get(), save_n3ds_ram ? Memory::N3DS_EXTRA_RAM_SIZE : 0); + ar& cache_marker; + ar& page_table_list; + // dsp is set from Core::System at startup + ar& current_page_table; + ar& fcram_mem; + ar& vram_mem; + ar& n3ds_extra_ram_mem; + ar& dsp_mem; + } }; +// We use this rather than BufferMem because we don't want new objects to be allocated when +// deserializing. This avoids unnecessary memory thrashing. +template +class MemorySystem::BackingMemImpl : public BackingMem { +public: + BackingMemImpl() : impl(*Core::Global().Memory().impl) {} + explicit BackingMemImpl(MemorySystem::Impl& impl_) : impl(impl_) {} + u8* GetPtr() override { + return impl.GetPtr(R); + } + const u8* GetPtr() const override { + return impl.GetPtr(R); + } + std::size_t GetSize() const override { + return impl.GetSize(R); + } + +private: + MemorySystem::Impl& impl; + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::base_object(*this); + } + friend class boost::serialization::access; +}; + +MemorySystem::Impl::Impl() + : fcram_mem(std::make_shared>(*this)), + vram_mem(std::make_shared>(*this)), + n3ds_extra_ram_mem(std::make_shared>(*this)), + dsp_mem(std::make_shared>(*this)) {} + MemorySystem::MemorySystem() : impl(std::make_unique()) {} MemorySystem::~MemorySystem() = default; -void MemorySystem::SetCurrentPageTable(PageTable* page_table) { +template +void MemorySystem::serialize(Archive& ar, const unsigned int file_version) { + ar&* impl.get(); +} + +SERIALIZE_IMPL(MemorySystem) + +void MemorySystem::SetCurrentPageTable(std::shared_ptr page_table) { impl->current_page_table = page_table; } -PageTable* MemorySystem::GetCurrentPageTable() const { +std::shared_ptr MemorySystem::GetCurrentPageTable() const { return impl->current_page_table; } -void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { - LOG_DEBUG(HW_Memory, "Mapping {} onto {:08X}-{:08X}", (void*)memory, base * PAGE_SIZE, +void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory, + PageType type) { + LOG_DEBUG(HW_Memory, "Mapping {} onto {:08X}-{:08X}", (void*)memory.GetPtr(), base * PAGE_SIZE, (base + size) * PAGE_SIZE); RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, @@ -101,12 +240,12 @@ void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, u8* memor } base += 1; - if (memory != nullptr) + if (memory != nullptr && memory.GetSize() > PAGE_SIZE) memory += PAGE_SIZE; } } -void MemorySystem::MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) { +void MemorySystem::MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, MemoryRef target) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:08X}", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:08X}", base); MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); @@ -127,26 +266,28 @@ void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) { MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); } -u8* MemorySystem::GetPointerForRasterizerCache(VAddr addr) { +MemoryRef MemorySystem::GetPointerForRasterizerCache(VAddr addr) { if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { - return impl->fcram.get() + (addr - LINEAR_HEAP_VADDR); + return {impl->fcram_mem, addr - LINEAR_HEAP_VADDR}; } if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { - return impl->fcram.get() + (addr - NEW_LINEAR_HEAP_VADDR); + return {impl->fcram_mem, addr - NEW_LINEAR_HEAP_VADDR}; } if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { - return impl->vram.get() + (addr - VRAM_VADDR); + return {impl->vram_mem, addr - VRAM_VADDR}; } UNREACHABLE(); } -void MemorySystem::RegisterPageTable(PageTable* page_table) { +void MemorySystem::RegisterPageTable(std::shared_ptr page_table) { impl->page_table_list.push_back(page_table); } -void MemorySystem::UnregisterPageTable(PageTable* page_table) { - impl->page_table_list.erase( - std::find(impl->page_table_list.begin(), impl->page_table_list.end(), page_table)); +void MemorySystem::UnregisterPageTable(std::shared_ptr page_table) { + auto it = std::find(impl->page_table_list.begin(), impl->page_table_list.end(), page_table); + if (it != impl->page_table_list.end()) { + impl->page_table_list.erase(it); + } } /** @@ -232,9 +373,9 @@ void MemorySystem::Write(const VAddr vaddr, const T data) { } bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { - auto& page_table = process.vm_manager.page_table; + auto& page_table = *process.vm_manager.page_table; - const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; + auto page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; if (page_pointer) return true; @@ -286,6 +427,10 @@ std::string MemorySystem::ReadCString(VAddr vaddr, std::size_t max_length) { } u8* MemorySystem::GetPhysicalPointer(PAddr address) { + return GetPhysicalRef(address); +} + +MemoryRef MemorySystem::GetPhysicalRef(PAddr address) { struct MemoryArea { PAddr paddr_base; u32 size; @@ -312,25 +457,28 @@ u8* MemorySystem::GetPhysicalPointer(PAddr address) { u32 offset_into_region = address - area->paddr_base; - u8* target_pointer = nullptr; + std::shared_ptr target_mem = nullptr; switch (area->paddr_base) { case VRAM_PADDR: - target_pointer = impl->vram.get() + offset_into_region; + target_mem = impl->vram_mem; break; case DSP_RAM_PADDR: - target_pointer = impl->dsp->GetDspMemory().data() + offset_into_region; + target_mem = impl->dsp_mem; break; case FCRAM_PADDR: - target_pointer = impl->fcram.get() + offset_into_region; + target_mem = impl->fcram_mem; break; case N3DS_EXTRA_RAM_PADDR: - target_pointer = impl->n3ds_extra_ram.get() + offset_into_region; + target_mem = impl->n3ds_extra_ram_mem; break; default: UNREACHABLE(); } + if (offset_into_region >= target_mem->GetSize()) { + return {nullptr}; + } - return target_pointer; + return {target_mem, offset_into_region}; } /// For a rasterizer-accessible PAddr, gets a list of all possible VAddr @@ -363,7 +511,7 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { for (VAddr vaddr : PhysicalToVirtualAddressForRasterizer(paddr)) { impl->cache_marker.Mark(vaddr, cached); - for (PageTable* page_table : impl->page_table_list) { + for (auto page_table : impl->page_table_list) { PageType& page_type = page_table->attributes[vaddr >> PAGE_BITS]; if (cached) { @@ -428,6 +576,16 @@ void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) { VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(start, size); } +void RasterizerClearAll(bool flush) { + // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be + // null here + if (VideoCore::g_renderer == nullptr) { + return; + } + + VideoCore::g_renderer->Rasterizer()->ClearAll(flush); +} + void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) { // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be // null here @@ -485,7 +643,7 @@ u64 MemorySystem::Read64(const VAddr addr) { void MemorySystem::ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, const std::size_t size) { - auto& page_table = process.vm_manager.page_table; + auto& page_table = *process.vm_manager.page_table; std::size_t remaining_size = size; std::size_t page_index = src_addr >> PAGE_BITS; @@ -551,7 +709,7 @@ void MemorySystem::Write64(const VAddr addr, const u64 data) { void MemorySystem::WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, const std::size_t size) { - auto& page_table = process.vm_manager.page_table; + auto& page_table = *process.vm_manager.page_table; std::size_t remaining_size = size; std::size_t page_index = dest_addr >> PAGE_BITS; std::size_t page_offset = dest_addr & PAGE_MASK; @@ -599,7 +757,7 @@ void MemorySystem::WriteBlock(const Kernel::Process& process, const VAddr dest_a void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { - auto& page_table = process.vm_manager.page_table; + auto& page_table = *process.vm_manager.page_table; std::size_t remaining_size = size; std::size_t page_index = dest_addr >> PAGE_BITS; std::size_t page_offset = dest_addr & PAGE_MASK; @@ -654,7 +812,7 @@ void MemorySystem::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VA void MemorySystem::CopyBlock(const Kernel::Process& dest_process, const Kernel::Process& src_process, VAddr dest_addr, VAddr src_addr, std::size_t size) { - auto& page_table = src_process.vm_manager.page_table; + auto& page_table = *src_process.vm_manager.page_table; std::size_t remaining_size = size; std::size_t page_index = src_addr >> PAGE_BITS; std::size_t page_offset = src_addr & PAGE_MASK; @@ -744,9 +902,9 @@ void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) mmio_handler->Write64(addr, data); } -u32 MemorySystem::GetFCRAMOffset(u8* pointer) { +u32 MemorySystem::GetFCRAMOffset(const u8* pointer) { ASSERT(pointer >= impl->fcram.get() && pointer <= impl->fcram.get() + Memory::FCRAM_N3DS_SIZE); - return pointer - impl->fcram.get(); + return static_cast(pointer - impl->fcram.get()); } u8* MemorySystem::GetFCRAMPointer(u32 offset) { @@ -754,6 +912,11 @@ u8* MemorySystem::GetFCRAMPointer(u32 offset) { return impl->fcram.get() + offset; } +MemoryRef MemorySystem::GetFCRAMRef(u32 offset) { + ASSERT(offset <= Memory::FCRAM_N3DS_SIZE); + return MemoryRef(impl->fcram_mem, offset); +} + void MemorySystem::SetDSP(AudioCore::DspInterface& dsp) { impl->dsp = &dsp; } diff --git a/src/core/memory.h b/src/core/memory.h index 6caca5a2ba..66b28d87d7 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -9,7 +9,10 @@ #include #include #include +#include +#include #include "common/common_types.h" +#include "common/memory_ref.h" #include "core/mmio.h" class ARM_Interface; @@ -52,6 +55,15 @@ struct SpecialRegion { VAddr base; u32 size; MMIORegionPointer handler; + +private: + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& base; + ar& size; + ar& handler; + } + friend class boost::serialization::access; }; /** @@ -65,7 +77,48 @@ struct PageTable { * Array of memory pointers backing each page. An entry can only be non-null if the * corresponding entry in the `attributes` array is of type `Memory`. */ - std::array pointers; + + // The reason for this rigmarole is to keep the 'raw' and 'refs' arrays in sync. + // We need 'raw' for dynarmic and 'refs' for serialization + struct Pointers { + + struct Entry { + Entry(Pointers& pointers_, VAddr idx_) : pointers(pointers_), idx(idx_) {} + + void operator=(MemoryRef value) { + pointers.refs[idx] = value; + pointers.raw[idx] = value.GetPtr(); + } + + operator u8*() { + return pointers.raw[idx]; + } + + private: + Pointers& pointers; + VAddr idx; + }; + + Entry operator[](VAddr idx) { + return Entry(*this, idx); + } + + u8* operator[](VAddr idx) const { + return raw[idx]; + } + + Entry operator[](std::size_t idx) { + return Entry(*this, static_cast(idx)); + } + + private: + std::array raw; + + std::array refs; + + friend struct PageTable; + }; + Pointers pointers; /** * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of @@ -78,6 +131,24 @@ struct PageTable { * the corresponding entry in `pointers` MUST be set to null. */ std::array attributes; + + std::array& GetPointerArray() { + return pointers.raw; + } + + void Clear(); + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& pointers.refs; + ar& special_regions; + ar& attributes; + for (auto i = 0; i < PAGE_TABLE_NUM_ENTRIES; i++) { + pointers.raw[i] = pointers.refs[i].GetPtr(); + } + } + friend class boost::serialization::access; }; /// Physical memory regions as seen from the ARM11 @@ -121,6 +192,8 @@ enum : PAddr { FCRAM_N3DS_PADDR_END = FCRAM_PADDR + FCRAM_N3DS_SIZE, }; +enum class Region { FCRAM, VRAM, DSP, N3DS }; + /// Virtual user-space memory regions enum : VAddr { /// Where the application text, data and bss reside. @@ -209,6 +282,12 @@ enum class FlushMode { FlushAndInvalidate, }; +/** + * Flushes and invalidates all memory in the rasterizer cache and removes any leftover state + * If flush is true, the rasterizer should flush any cached resources to RAM before clearing + */ +void RasterizerClearAll(bool flush); + /** * Flushes and invalidates any externally cached rasterizer resources touching the given virtual * address region. @@ -228,7 +307,7 @@ public: * @param size The amount of bytes to map. Must be page-aligned. * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. */ - void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target); + void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, MemoryRef target); /** * Maps a region of the emulated process address space as a IO region. @@ -242,8 +321,8 @@ public: void UnmapRegion(PageTable& page_table, VAddr base, u32 size); /// Currently active page table - void SetCurrentPageTable(PageTable* page_table); - PageTable* GetCurrentPageTable() const; + void SetCurrentPageTable(std::shared_ptr page_table); + std::shared_ptr GetCurrentPageTable() const; u8 Read8(VAddr addr); u16 Read16(VAddr addr); @@ -272,26 +351,31 @@ public: */ u8* GetPhysicalPointer(PAddr address); + MemoryRef GetPhysicalRef(PAddr address); + u8* GetPointer(VAddr vaddr); bool IsValidPhysicalAddress(PAddr paddr); /// Gets offset in FCRAM from a pointer inside FCRAM range - u32 GetFCRAMOffset(u8* pointer); + u32 GetFCRAMOffset(const u8* pointer); /// Gets pointer in FCRAM with given offset u8* GetFCRAMPointer(u32 offset); + /// Gets a serializable ref to FCRAM with the given offset + MemoryRef GetFCRAMRef(u32 offset); + /** * Mark each page touching the region as cached. */ void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached); /// Registers page table for rasterizer cache marking - void RegisterPageTable(PageTable* page_table); + void RegisterPageTable(std::shared_ptr page_table); /// Unregisters page table for rasterizer cache marking - void UnregisterPageTable(PageTable* page_table); + void UnregisterPageTable(std::shared_ptr page_table); void SetDSP(AudioCore::DspInterface& dsp); @@ -308,16 +392,29 @@ private: * Since the cache only happens on linear heap or VRAM, we know the exact physical address and * pointer of such virtual address */ - u8* GetPointerForRasterizerCache(VAddr addr); + MemoryRef GetPointerForRasterizerCache(VAddr addr); - void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type); + void MapPages(PageTable& page_table, u32 base, u32 size, MemoryRef memory, PageType type); class Impl; std::unique_ptr impl; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version); + +public: + template + class BackingMemImpl; }; /// Determines if the given VAddr is valid for the specified process. bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); } // namespace Memory + +BOOST_CLASS_EXPORT_KEY(Memory::MemorySystem::BackingMemImpl) +BOOST_CLASS_EXPORT_KEY(Memory::MemorySystem::BackingMemImpl) +BOOST_CLASS_EXPORT_KEY(Memory::MemorySystem::BackingMemImpl) +BOOST_CLASS_EXPORT_KEY(Memory::MemorySystem::BackingMemImpl) diff --git a/src/core/mmio.h b/src/core/mmio.h index 30bafaf5f7..2e6323b49f 100644 --- a/src/core/mmio.h +++ b/src/core/mmio.h @@ -32,6 +32,11 @@ public: virtual void Write64(VAddr addr, u64 data) = 0; virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) {} }; using MMIORegionPointer = std::shared_ptr; diff --git a/src/core/movie.h b/src/core/movie.h index f1be869462..eff148090d 100644 --- a/src/core/movie.h +++ b/src/core/movie.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" namespace Service { @@ -132,5 +133,16 @@ private: u64 init_time; std::function playback_completion_callback; std::size_t current_byte = 0; + + template + void serialize(Archive& ar, const unsigned int) { + // Only serialize what's needed to make savestates useful for TAS: + u64 _current_byte = static_cast(current_byte); + ar& _current_byte; + current_byte = static_cast(_current_byte); + ar& recorded_input; + ar& init_time; + } + friend class boost::serialization::access; }; } // namespace Core \ No newline at end of file diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 94d6c17ca0..00ab24dbcd 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -117,6 +117,14 @@ double PerfStats::GetLastFrameTimeScale() { return duration_cast(previous_frame_length).count() / FRAME_LENGTH; } +void FrameLimiter::WaitOnce() { + if (frame_advancing_enabled) { + // Frame advancing is enabled: wait on event instead of doing framelimiting + frame_advance_event.Wait(); + frame_advance_event.Reset(); + } +} + void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { if (frame_advancing_enabled) { // Frame advancing is enabled: wait on event instead of doing framelimiting @@ -164,10 +172,6 @@ void FrameLimiter::SetFrameAdvancing(bool value) { } void FrameLimiter::AdvanceFrame() { - if (!frame_advancing_enabled) { - // Start frame advancing - frame_advancing_enabled = true; - } frame_advance_event.Set(); } diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 0ef5168faa..c7d22ef8ec 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -98,6 +98,7 @@ public: */ void SetFrameAdvancing(bool value); void AdvanceFrame(); + void WaitOnce(); private: /// Emulated system time (in microseconds) at the last limiter invocation diff --git a/src/core/savestate.cpp b/src/core/savestate.cpp new file mode 100644 index 0000000000..26fc0cbedf --- /dev/null +++ b/src/core/savestate.cpp @@ -0,0 +1,156 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "common/archives.h" +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "common/zstd_compression.h" +#include "core/cheats/cheats.h" +#include "core/core.h" +#include "core/savestate.h" +#include "network/network.h" +#include "video_core/video_core.h" + +namespace Core { + +#pragma pack(push, 1) +struct CSTHeader { + std::array filetype; /// Unique Identifier to check the file type (always "CST"0x1B) + u64_le program_id; /// ID of the ROM being executed. Also called title_id + std::array revision; /// Git hash of the revision this savestate was created with + u64_le time; /// The time when this save state was created + + std::array reserved; /// Make heading 256 bytes so it has consistent size + + template + void serialize(Archive& ar, const unsigned int) { + ar& boost::serialization::binary_object(this, sizeof(CSTHeader)); + } +}; +static_assert(sizeof(CSTHeader) == 256, "CSTHeader should be 256 bytes"); +#pragma pack(pop) + +constexpr std::array header_magic_bytes{{'C', 'S', 'T', 0x1B}}; + +std::string GetSaveStatePath(u64 program_id, u32 slot) { + return fmt::format("{}{:016X}.{:02d}.cst", FileUtil::GetUserPath(FileUtil::UserPath::StatesDir), + program_id, slot); +} + +std::vector ListSaveStates(u64 program_id) { + std::vector result; + for (u32 slot = 1; slot <= SaveStateSlotCount; ++slot) { + const auto path = GetSaveStatePath(program_id, slot); + if (!FileUtil::Exists(path)) { + continue; + } + + SaveStateInfo info; + info.slot = slot; + + FileUtil::IOFile file(path, "rb"); + if (!file) { + LOG_ERROR(Core, "Could not open file {}", path); + continue; + } + CSTHeader header; + if (file.GetSize() < sizeof(header)) { + LOG_ERROR(Core, "File too small {}", path); + continue; + } + if (file.ReadBytes(&header, sizeof(header)) != sizeof(header)) { + LOG_ERROR(Core, "Could not read from file {}", path); + continue; + } + if (header.filetype != header_magic_bytes) { + LOG_WARNING(Core, "Invalid save state file {}", path); + continue; + } + info.time = header.time; + + if (header.program_id != program_id) { + LOG_WARNING(Core, "Save state file isn't for the current game {}", path); + continue; + } + std::string revision = fmt::format("{:02x}", fmt::join(header.revision, "")); + if (revision == Common::g_scm_rev) { + info.status = SaveStateInfo::ValidationStatus::OK; + } else { + LOG_WARNING(Core, "Save state file created from a different revision {}", path); + info.status = SaveStateInfo::ValidationStatus::RevisionDismatch; + } + result.emplace_back(std::move(info)); + } + return result; +} + +void System::SaveState(u32 slot) const { + std::ostringstream sstream{std::ios_base::binary}; + // Serialize + oarchive oa{sstream}; + oa&* this; + + const std::string& str{sstream.str()}; + auto buffer = Common::Compression::CompressDataZSTDDefault( + reinterpret_cast(str.data()), str.size()); + + const auto path = GetSaveStatePath(title_id, slot); + if (!FileUtil::CreateFullPath(path)) { + throw std::runtime_error("Could not create path " + path); + } + + FileUtil::IOFile file(path, "wb"); + if (!file) { + throw std::runtime_error("Could not open file " + path); + } + + CSTHeader header{}; + header.filetype = header_magic_bytes; + header.program_id = title_id; + std::string rev_bytes; + CryptoPP::StringSource(Common::g_scm_rev, true, + new CryptoPP::HexDecoder(new CryptoPP::StringSink(rev_bytes))); + std::memcpy(header.revision.data(), rev_bytes.data(), sizeof(header.revision)); + header.time = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + if (file.WriteBytes(&header, sizeof(header)) != sizeof(header) || + file.WriteBytes(buffer.data(), buffer.size()) != buffer.size()) { + throw std::runtime_error("Could not write to file " + path); + } +} + +void System::LoadState(u32 slot) { + if (Network::GetRoomMember().lock()->IsConnected()) { + throw std::runtime_error("Unable to load while connected to multiplayer"); + } + + const auto path = GetSaveStatePath(title_id, slot); + + std::vector decompressed; + { + std::vector buffer(FileUtil::GetSize(path) - sizeof(CSTHeader)); + + FileUtil::IOFile file(path, "rb"); + file.Seek(sizeof(CSTHeader), SEEK_SET); // Skip header + if (file.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { + throw std::runtime_error("Could not read from file at " + path); + } + decompressed = Common::Compression::DecompressDataZSTD(buffer); + } + std::istringstream sstream{ + std::string{reinterpret_cast(decompressed.data()), decompressed.size()}, + std::ios_base::binary}; + decompressed.clear(); + + // Deserialize + iarchive ia{sstream}; + ia&* this; +} + +} // namespace Core diff --git a/src/core/savestate.h b/src/core/savestate.h new file mode 100644 index 0000000000..f67bee22f8 --- /dev/null +++ b/src/core/savestate.h @@ -0,0 +1,27 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" + +namespace Core { + +struct CSTHeader; + +struct SaveStateInfo { + u32 slot; + u64 time; + enum class ValidationStatus { + OK, + RevisionDismatch, + } status; +}; + +constexpr u32 SaveStateSlotCount = 10; // Maximum count of savestate slots + +std::vector ListSaveStates(u64 program_id); + +} // namespace Core diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 24f7826534..1e0eb4bc9b 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -13,4 +13,4 @@ add_library(network STATIC create_target_directory_groups(network) -target_link_libraries(network PRIVATE common enet) +target_link_libraries(network PRIVATE common enet Boost::serialization) diff --git a/src/network/room_member.h b/src/network/room_member.h index ad5d14b445..ee1c921d46 100644 --- a/src/network/room_member.h +++ b/src/network/room_member.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "common/common_types.h" #include "network/room.h" @@ -30,6 +31,17 @@ struct WifiPacket { MacAddress transmitter_address; ///< Mac address of the transmitter. MacAddress destination_address; ///< Mac address of the receiver. u8 channel; ///< WiFi channel where this frame was transmitted. + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& type; + ar& data; + ar& transmitter_address; + ar& destination_address; + ar& channel; + } + friend class boost::serialization::access; }; /// Represents a chat message. diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index a6fd0de764..1bba53c7e4 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -10,7 +10,7 @@ namespace ArmTests { -static Memory::PageTable* page_table = nullptr; +static std::shared_ptr page_table = nullptr; TestEnvironment::TestEnvironment(bool mutable_memory_) : mutable_memory(mutable_memory_), test_memory(std::make_shared(this)) { @@ -20,10 +20,9 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) kernel = std::make_unique(*memory, *timing, [] {}, 0, 1, 0); kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); - page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; + page_table = kernel->GetCurrentProcess()->vm_manager.page_table; - page_table->pointers.fill(nullptr); - page_table->attributes.fill(Memory::PageType::Unmapped); + page_table->Clear(); memory->MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); memory->MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index cb73d481f6..f5623c8c16 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc.h" @@ -34,7 +35,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel IPC::MakeHeader(0x1234, 0, 0), }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); REQUIRE(context.CommandBuffer()[0] == 0x12340000); } @@ -47,7 +48,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel 0xAABBCCDD, }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); auto* output = context.CommandBuffer(); REQUIRE(output[1] == 0x12345678); @@ -64,7 +65,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel a_handle, }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); auto* output = context.CommandBuffer(); REQUIRE(context.GetIncomingHandle(output[2]) == a); @@ -80,7 +81,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel a_handle, }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); auto* output = context.CommandBuffer(); REQUIRE(context.GetIncomingHandle(output[2]) == a); @@ -100,7 +101,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel process->handle_table.Create(c).Unwrap(), }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); auto* output = context.CommandBuffer(); REQUIRE(context.GetIncomingHandle(output[2]) == a); @@ -115,7 +116,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel 0, }; - auto result = context.PopulateFromIncomingCommandBuffer(input, *process); + auto result = context.PopulateFromIncomingCommandBuffer(input, process); REQUIRE(result == RESULT_SUCCESS); auto* output = context.CommandBuffer(); @@ -129,73 +130,76 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel 0x98989898, }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); REQUIRE(context.CommandBuffer()[2] == process->process_id); } SECTION("translates StaticBuffer descriptors") { - auto buffer = std::make_shared>(Memory::PAGE_SIZE); - std::fill(buffer->begin(), buffer->end(), 0xAB); + auto mem = std::make_shared(Memory::PAGE_SIZE); + MemoryRef buffer{mem}; + std::fill(buffer.GetPtr(), buffer.GetPtr() + buffer.GetSize(), 0xAB); VAddr target_address = 0x10000000; - auto result = process->vm_manager.MapBackingMemory(target_address, buffer->data(), - buffer->size(), MemoryState::Private); + auto result = process->vm_manager.MapBackingMemory(target_address, buffer, buffer.GetSize(), + MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); const u32_le input[]{ IPC::MakeHeader(0, 0, 2), - IPC::StaticBufferDesc(buffer->size(), 0), + IPC::StaticBufferDesc(buffer.GetSize(), 0), target_address, }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); - CHECK(context.GetStaticBuffer(0) == *buffer); + CHECK(context.GetStaticBuffer(0) == mem->Vector()); - REQUIRE(process->vm_manager.UnmapRange(target_address, buffer->size()) == RESULT_SUCCESS); + REQUIRE(process->vm_manager.UnmapRange(target_address, buffer.GetSize()) == RESULT_SUCCESS); } SECTION("translates MappedBuffer descriptors") { - auto buffer = std::make_shared>(Memory::PAGE_SIZE); - std::fill(buffer->begin(), buffer->end(), 0xCD); + auto mem = std::make_shared(Memory::PAGE_SIZE); + MemoryRef buffer{mem}; + std::fill(buffer.GetPtr(), buffer.GetPtr() + buffer.GetSize(), 0xCD); VAddr target_address = 0x10000000; - auto result = process->vm_manager.MapBackingMemory(target_address, buffer->data(), - buffer->size(), MemoryState::Private); + auto result = process->vm_manager.MapBackingMemory(target_address, buffer, buffer.GetSize(), + MemoryState::Private); const u32_le input[]{ IPC::MakeHeader(0, 0, 2), - IPC::MappedBufferDesc(buffer->size(), IPC::R), + IPC::MappedBufferDesc(buffer.GetSize(), IPC::R), target_address, }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); - std::vector other_buffer(buffer->size()); - context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer->size()); + std::vector other_buffer(buffer.GetSize()); + context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer.GetSize()); - CHECK(other_buffer == *buffer); + CHECK(other_buffer == mem->Vector()); - REQUIRE(process->vm_manager.UnmapRange(target_address, buffer->size()) == RESULT_SUCCESS); + REQUIRE(process->vm_manager.UnmapRange(target_address, buffer.GetSize()) == RESULT_SUCCESS); } SECTION("translates mixed params") { - auto buffer_static = std::make_shared>(Memory::PAGE_SIZE); - std::fill(buffer_static->begin(), buffer_static->end(), 0xCE); + auto mem_static = std::make_shared(Memory::PAGE_SIZE); + MemoryRef buffer_static{mem_static}; + std::fill(buffer_static.GetPtr(), buffer_static.GetPtr() + buffer_static.GetSize(), 0xCE); - auto buffer_mapped = std::make_shared>(Memory::PAGE_SIZE); - std::fill(buffer_mapped->begin(), buffer_mapped->end(), 0xDF); + auto mem_mapped = std::make_shared(Memory::PAGE_SIZE); + MemoryRef buffer_mapped{mem_mapped}; + std::fill(buffer_mapped.GetPtr(), buffer_mapped.GetPtr() + buffer_mapped.GetSize(), 0xDF); VAddr target_address_static = 0x10000000; - auto result = - process->vm_manager.MapBackingMemory(target_address_static, buffer_static->data(), - buffer_static->size(), MemoryState::Private); + auto result = process->vm_manager.MapBackingMemory( + target_address_static, buffer_static, buffer_static.GetSize(), MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); VAddr target_address_mapped = 0x20000000; - result = process->vm_manager.MapBackingMemory(target_address_mapped, buffer_mapped->data(), - buffer_mapped->size(), MemoryState::Private); + result = process->vm_manager.MapBackingMemory( + target_address_mapped, buffer_mapped, buffer_mapped.GetSize(), MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); auto a = MakeObject(kernel); @@ -207,27 +211,27 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel process->handle_table.Create(a).Unwrap(), IPC::CallingPidDesc(), 0, - IPC::StaticBufferDesc(buffer_static->size(), 0), + IPC::StaticBufferDesc(buffer_static.GetSize(), 0), target_address_static, - IPC::MappedBufferDesc(buffer_mapped->size(), IPC::R), + IPC::MappedBufferDesc(buffer_mapped.GetSize(), IPC::R), target_address_mapped, }; - context.PopulateFromIncomingCommandBuffer(input, *process); + context.PopulateFromIncomingCommandBuffer(input, process); auto* output = context.CommandBuffer(); CHECK(output[1] == 0x12345678); CHECK(output[2] == 0xABCDEF00); CHECK(context.GetIncomingHandle(output[4]) == a); CHECK(output[6] == process->process_id); - CHECK(context.GetStaticBuffer(0) == *buffer_static); - std::vector other_buffer(buffer_mapped->size()); - context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer_mapped->size()); - CHECK(other_buffer == *buffer_mapped); + CHECK(context.GetStaticBuffer(0) == mem_static->Vector()); + std::vector other_buffer(buffer_mapped.GetSize()); + context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer_mapped.GetSize()); + CHECK(other_buffer == mem_mapped->Vector()); - REQUIRE(process->vm_manager.UnmapRange(target_address_static, buffer_static->size()) == + REQUIRE(process->vm_manager.UnmapRange(target_address_static, buffer_static.GetSize()) == RESULT_SUCCESS); - REQUIRE(process->vm_manager.UnmapRange(target_address_mapped, buffer_mapped->size()) == + REQUIRE(process->vm_manager.UnmapRange(target_address_mapped, buffer_mapped.GetSize()) == RESULT_SUCCESS); } } @@ -314,10 +318,12 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { context.AddStaticBuffer(0, input_buffer); - auto output_buffer = std::make_shared>(Memory::PAGE_SIZE); + auto output_mem = std::make_shared(Memory::PAGE_SIZE); + MemoryRef output_buffer{output_mem}; + VAddr target_address = 0x10000000; auto result = process->vm_manager.MapBackingMemory( - target_address, output_buffer->data(), output_buffer->size(), MemoryState::Private); + target_address, output_buffer, output_buffer.GetSize(), MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); input[0] = IPC::MakeHeader(0, 0, 2); @@ -329,13 +335,13 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { std::array output_cmdbuff; // Set up the output StaticBuffer output_cmdbuff[IPC::COMMAND_BUFFER_LENGTH] = - IPC::StaticBufferDesc(output_buffer->size(), 0); + IPC::StaticBufferDesc(output_buffer.GetSize(), 0); output_cmdbuff[IPC::COMMAND_BUFFER_LENGTH + 1] = target_address; context.WriteToOutgoingCommandBuffer(output_cmdbuff.data(), *process); - CHECK(*output_buffer == input_buffer); - REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer->size()) == + CHECK(output_mem->Vector() == input_buffer); + REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer.GetSize()) == RESULT_SUCCESS); } @@ -343,32 +349,34 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { std::vector input_buffer(Memory::PAGE_SIZE); std::fill(input_buffer.begin(), input_buffer.end(), 0xAB); - auto output_buffer = std::make_shared>(Memory::PAGE_SIZE); + auto output_mem = std::make_shared(Memory::PAGE_SIZE); + MemoryRef output_buffer{output_mem}; + VAddr target_address = 0x10000000; auto result = process->vm_manager.MapBackingMemory( - target_address, output_buffer->data(), output_buffer->size(), MemoryState::Private); + target_address, output_buffer, output_buffer.GetSize(), MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); const u32_le input_cmdbuff[]{ IPC::MakeHeader(0, 0, 2), - IPC::MappedBufferDesc(output_buffer->size(), IPC::W), + IPC::MappedBufferDesc(output_buffer.GetSize(), IPC::W), target_address, }; - context.PopulateFromIncomingCommandBuffer(input_cmdbuff, *process); + context.PopulateFromIncomingCommandBuffer(input_cmdbuff, process); context.GetMappedBuffer(0).Write(input_buffer.data(), 0, input_buffer.size()); input[0] = IPC::MakeHeader(0, 0, 2); - input[1] = IPC::MappedBufferDesc(output_buffer->size(), IPC::W); + input[1] = IPC::MappedBufferDesc(output_buffer.GetSize(), IPC::W); input[2] = 0; context.WriteToOutgoingCommandBuffer(output, *process); - CHECK(output[1] == IPC::MappedBufferDesc(output_buffer->size(), IPC::W)); + CHECK(output[1] == IPC::MappedBufferDesc(output_buffer.GetSize(), IPC::W)); CHECK(output[2] == target_address); - CHECK(*output_buffer == input_buffer); - REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer->size()) == + CHECK(output_mem->Vector() == input_buffer); + REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer.GetSize()) == RESULT_SUCCESS); } } diff --git a/src/tests/core/memory/vm_manager.cpp b/src/tests/core/memory/vm_manager.cpp index bd510864a5..5a8e8b7884 100644 --- a/src/tests/core/memory/vm_manager.cpp +++ b/src/tests/core/memory/vm_manager.cpp @@ -10,47 +10,48 @@ #include "core/memory.h" TEST_CASE("Memory Basics", "[kernel][memory]") { - auto block = std::make_shared>(Memory::PAGE_SIZE); + auto mem = std::make_shared(Memory::PAGE_SIZE); + MemoryRef block{mem}; Memory::MemorySystem memory; SECTION("mapping memory") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. auto manager = std::make_unique(memory); - auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), + auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block, block.GetSize(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); auto vma = manager->FindVMA(Memory::HEAP_VADDR); CHECK(vma != manager->vma_map.end()); - CHECK(vma->second.size == block->size()); + CHECK(vma->second.size == block.GetSize()); CHECK(vma->second.type == Kernel::VMAType::BackingMemory); - CHECK(vma->second.backing_memory == block->data()); + CHECK(vma->second.backing_memory.GetPtr() == block.GetPtr()); CHECK(vma->second.meminfo_state == Kernel::MemoryState::Private); } SECTION("unmapping memory") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. auto manager = std::make_unique(memory); - auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), + auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block, block.GetSize(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); - ResultCode code = manager->UnmapRange(Memory::HEAP_VADDR, block->size()); + ResultCode code = manager->UnmapRange(Memory::HEAP_VADDR, block.GetSize()); REQUIRE(code == RESULT_SUCCESS); auto vma = manager->FindVMA(Memory::HEAP_VADDR); CHECK(vma != manager->vma_map.end()); CHECK(vma->second.type == Kernel::VMAType::Free); - CHECK(vma->second.backing_memory == nullptr); + CHECK(vma->second.backing_memory.GetPtr() == nullptr); } SECTION("changing memory permissions") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. auto manager = std::make_unique(memory); - auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), + auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block, block.GetSize(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); - ResultCode code = manager->ReprotectRange(Memory::HEAP_VADDR, block->size(), + ResultCode code = manager->ReprotectRange(Memory::HEAP_VADDR, block.GetSize(), Kernel::VMAPermission::Execute); CHECK(code == RESULT_SUCCESS); @@ -58,24 +59,24 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { CHECK(vma != manager->vma_map.end()); CHECK(vma->second.permissions == Kernel::VMAPermission::Execute); - code = manager->UnmapRange(Memory::HEAP_VADDR, block->size()); + code = manager->UnmapRange(Memory::HEAP_VADDR, block.GetSize()); REQUIRE(code == RESULT_SUCCESS); } SECTION("changing memory state") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. auto manager = std::make_unique(memory); - auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), + auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block, block.GetSize(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); - ResultCode code = manager->ReprotectRange(Memory::HEAP_VADDR, block->size(), + ResultCode code = manager->ReprotectRange(Memory::HEAP_VADDR, block.GetSize(), Kernel::VMAPermission::ReadWrite); REQUIRE(code == RESULT_SUCCESS); SECTION("with invalid address") { ResultCode code = manager->ChangeMemoryState( - 0xFFFFFFFF, block->size(), Kernel::MemoryState::Locked, + 0xFFFFFFFF, block.GetSize(), Kernel::MemoryState::Locked, Kernel::VMAPermission::ReadWrite, Kernel::MemoryState::Aliased, Kernel::VMAPermission::Execute); CHECK(code == Kernel::ERR_INVALID_ADDRESS); @@ -83,7 +84,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { SECTION("ignoring the original permissions") { ResultCode code = manager->ChangeMemoryState( - Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Private, + Memory::HEAP_VADDR, block.GetSize(), Kernel::MemoryState::Private, Kernel::VMAPermission::None, Kernel::MemoryState::Locked, Kernel::VMAPermission::Write); CHECK(code == RESULT_SUCCESS); @@ -96,7 +97,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { SECTION("enforcing the original permissions with correct expectations") { ResultCode code = manager->ChangeMemoryState( - Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Private, + Memory::HEAP_VADDR, block.GetSize(), Kernel::MemoryState::Private, Kernel::VMAPermission::ReadWrite, Kernel::MemoryState::Aliased, Kernel::VMAPermission::Execute); CHECK(code == RESULT_SUCCESS); @@ -109,7 +110,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { SECTION("with incorrect permission expectations") { ResultCode code = manager->ChangeMemoryState( - Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Private, + Memory::HEAP_VADDR, block.GetSize(), Kernel::MemoryState::Private, Kernel::VMAPermission::Execute, Kernel::MemoryState::Aliased, Kernel::VMAPermission::Execute); CHECK(code == Kernel::ERR_INVALID_ADDRESS_STATE); @@ -122,7 +123,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { SECTION("with incorrect state expectations") { ResultCode code = manager->ChangeMemoryState( - Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Locked, + Memory::HEAP_VADDR, block.GetSize(), Kernel::MemoryState::Locked, Kernel::VMAPermission::ReadWrite, Kernel::MemoryState::Aliased, Kernel::VMAPermission::Execute); CHECK(code == Kernel::ERR_INVALID_ADDRESS_STATE); @@ -133,7 +134,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { CHECK(vma->second.meminfo_state == Kernel::MemoryState::Private); } - code = manager->UnmapRange(Memory::HEAP_VADDR, block->size()); + code = manager->UnmapRange(Memory::HEAP_VADDR, block.GetSize()); REQUIRE(code == RESULT_SUCCESS); } } diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 6c827c5ce2..a6f9860eb0 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -142,7 +142,7 @@ endif() create_target_directory_groups(video_core) target_link_libraries(video_core PUBLIC common core) -target_link_libraries(video_core PRIVATE glad nihstro-headers) +target_link_libraries(video_core PRIVATE glad nihstro-headers Boost::serialization) if (ARCHITECTURE_x86_64) target_link_libraries(video_core PUBLIC xbyak) diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 72f6f21611..1c633cf5c4 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -640,8 +640,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { reinterpret_cast(&id)); } -void ProcessCommandList(const u32* list, u32 size) { - g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = list; +void ProcessCommandList(PAddr list, u32 size) { + + u32* buffer = (u32*)VideoCore::g_memory->GetPhysicalPointer(list); + + if (Pica::g_debug_context && Pica::g_debug_context->recorder) { + Pica::g_debug_context->recorder->MemoryAccessed((u8*)buffer, size, list); + } + + g_state.cmd_list.addr = list; + g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = buffer; g_state.cmd_list.length = size / sizeof(u32); while (g_state.cmd_list.current_ptr < g_state.cmd_list.head_ptr + g_state.cmd_list.length) { diff --git a/src/video_core/command_processor.h b/src/video_core/command_processor.h index 82b1543274..3b4e055198 100644 --- a/src/video_core/command_processor.h +++ b/src/video_core/command_processor.h @@ -32,6 +32,6 @@ static_assert(std::is_standard_layout::value == true, "CommandHeader does not use standard layout"); static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!"); -void ProcessCommandList(const u32* list, u32 size); +void ProcessCommandList(PAddr list, u32 size); } // namespace Pica::CommandProcessor diff --git a/src/video_core/geometry_pipeline.cpp b/src/video_core/geometry_pipeline.cpp index 3a24b71c97..0ddbda9436 100644 --- a/src/video_core/geometry_pipeline.cpp +++ b/src/video_core/geometry_pipeline.cpp @@ -2,6 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include +#include +#include "common/archives.h" #include "video_core/geometry_pipeline.h" #include "video_core/pica_state.h" #include "video_core/regs.h" @@ -30,6 +34,11 @@ public: * @return if the buffer is full and the geometry shader should be invoked */ virtual bool SubmitVertex(const Shader::AttributeBuffer& input) = 0; + +private: + template + void serialize(Archive& ar, const unsigned int file_version) {} + friend class boost::serialization::access; }; // In the Point mode, vertex attributes are sent to the input registers in the geometry shader unit. @@ -79,6 +88,38 @@ private: Common::Vec4* buffer_cur; Common::Vec4* buffer_end; unsigned int vs_output_num; + + GeometryPipeline_Point() : regs(g_state.regs), unit(g_state.gs_unit) {} + + template + static void serialize_common(Class* self, Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*self); + ar & self->attribute_buffer; + ar & self->vs_output_num; + } + + template + void save(Archive& ar, const unsigned int version) const { + serialize_common(this, ar, version); + auto buffer_idx = static_cast(buffer_cur - attribute_buffer.attr); + auto buffer_size = static_cast(buffer_end - attribute_buffer.attr); + ar << buffer_idx; + ar << buffer_size; + } + + template + void load(Archive& ar, const unsigned int version) { + serialize_common(this, ar, version); + u32 buffer_idx, buffer_size; + ar >> buffer_idx; + ar >> buffer_size; + buffer_cur = attribute_buffer.attr + buffer_idx; + buffer_end = attribute_buffer.attr + buffer_size; + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() + + friend class boost::serialization::access; }; // In VariablePrimitive mode, vertex attributes are buffered into the uniform registers in the @@ -144,6 +185,36 @@ private: unsigned int total_vertex_num; Common::Vec4* buffer_cur; unsigned int vs_output_num; + + GeometryPipeline_VariablePrimitive() : regs(g_state.regs), setup(g_state.gs) {} + + template + static void serialize_common(Class* self, Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*self); + ar & self->need_index; + ar & self->main_vertex_num; + ar & self->total_vertex_num; + ar & self->vs_output_num; + } + + template + void save(Archive& ar, const unsigned int version) const { + serialize_common(this, ar, version); + auto buffer_idx = static_cast(buffer_cur - setup.uniforms.f); + ar << buffer_idx; + } + + template + void load(Archive& ar, const unsigned int version) { + serialize_common(this, ar, version); + u32 buffer_idx; + ar >> buffer_idx; + buffer_cur = setup.uniforms.f + buffer_idx; + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() + + friend class boost::serialization::access; }; // In FixedPrimitive mode, vertex attributes are buffered into the uniform registers in the geometry @@ -190,6 +261,41 @@ private: Common::Vec4* buffer_cur; Common::Vec4* buffer_end; unsigned int vs_output_num; + + GeometryPipeline_FixedPrimitive() : regs(g_state.regs), setup(g_state.gs) {} + + template + static void serialize_common(Class* self, Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*self); + ar & self->vs_output_num; + } + + template + void save(Archive& ar, const unsigned int version) const { + serialize_common(this, ar, version); + auto buffer_offset = static_cast(buffer_begin - setup.uniforms.f); + auto buffer_idx = static_cast(buffer_cur - setup.uniforms.f); + auto buffer_size = static_cast(buffer_end - setup.uniforms.f); + ar << buffer_offset; + ar << buffer_idx; + ar << buffer_size; + } + + template + void load(Archive& ar, const unsigned int version) { + serialize_common(this, ar, version); + u32 buffer_offset, buffer_idx, buffer_size; + ar >> buffer_offset; + ar >> buffer_idx; + ar >> buffer_size; + buffer_begin = setup.uniforms.f + buffer_offset; + buffer_cur = setup.uniforms.f + buffer_idx; + buffer_end = setup.uniforms.f + buffer_size; + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() + + friend class boost::serialization::access; }; GeometryPipeline::GeometryPipeline(State& state) : state(state) {} @@ -271,4 +377,15 @@ void GeometryPipeline::SubmitVertex(const Shader::AttributeBuffer& input) { } } +template +void GeometryPipeline::serialize(Archive& ar, const unsigned int version) { + // vertex_handler and shader_engine are always set to the same value + ar& backend; +} + } // namespace Pica + +SERIALIZE_EXPORT_IMPL(Pica::GeometryPipeline_Point) +SERIALIZE_EXPORT_IMPL(Pica::GeometryPipeline_VariablePrimitive) +SERIALIZE_EXPORT_IMPL(Pica::GeometryPipeline_FixedPrimitive) +SERIALIZE_IMPL(Pica::GeometryPipeline) diff --git a/src/video_core/geometry_pipeline.h b/src/video_core/geometry_pipeline.h index 91fdd31923..1a903b1e0e 100644 --- a/src/video_core/geometry_pipeline.h +++ b/src/video_core/geometry_pipeline.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "video_core/shader/shader.h" namespace Pica { @@ -12,6 +13,9 @@ namespace Pica { struct State; class GeometryPipelineBackend; +class GeometryPipeline_Point; +class GeometryPipeline_VariablePrimitive; +class GeometryPipeline_FixedPrimitive; /// A pipeline receiving from vertex shader and sending to geometry shader and primitive assembler class GeometryPipeline { @@ -45,5 +49,14 @@ private: Shader::ShaderEngine* shader_engine; std::unique_ptr backend; State& state; + + template + void serialize(Archive& ar, const unsigned int version); + + friend class boost::serialization::access; }; } // namespace Pica + +BOOST_CLASS_EXPORT_KEY(Pica::GeometryPipeline_Point) +BOOST_CLASS_EXPORT_KEY(Pica::GeometryPipeline_VariablePrimitive) +BOOST_CLASS_EXPORT_KEY(Pica::GeometryPipeline_FixedPrimitive) diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp index 1475e3a927..5e0c2d480a 100644 --- a/src/video_core/pica.cpp +++ b/src/video_core/pica.cpp @@ -3,12 +3,20 @@ // Refer to the license.txt file included. #include +#include "core/global.h" #include "video_core/geometry_pipeline.h" #include "video_core/pica.h" #include "video_core/pica_state.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" +namespace Core { +template <> +Pica::State& Global() { + return Pica::g_state; +} +} // namespace Core + namespace Pica { State g_state; diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h index 5a97ae952c..3c29ff84ea 100644 --- a/src/video_core/pica_state.h +++ b/src/video_core/pica_state.h @@ -5,6 +5,8 @@ #pragma once #include +#include +#include #include "common/bit_field.h" #include "common/common_types.h" #include "common/vector_math.h" @@ -12,6 +14,20 @@ #include "video_core/primitive_assembly.h" #include "video_core/regs.h" #include "video_core/shader/shader.h" +#include "video_core/video_core.h" + +// Boost::serialization doesn't like union types for some reason, +// so we need to mark arrays of union values with a special serialization method +template +struct UnionArray : public std::array { +private: + template + void serialize(Archive& ar, const unsigned int) { + static_assert(sizeof(Value) == sizeof(u32)); + ar&* static_cast(static_cast(this->data())); + } + friend class boost::serialization::access; +}; namespace Pica { @@ -74,11 +90,22 @@ struct State { } }; - std::array noise_table; - std::array color_map_table; - std::array alpha_map_table; - std::array color_table; - std::array color_diff_table; + UnionArray noise_table; + UnionArray color_map_table; + UnionArray alpha_map_table; + UnionArray color_table; + UnionArray color_diff_table; + + private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& noise_table; + ar& color_map_table; + ar& alpha_map_table; + ar& color_table; + ar& color_diff_table; + } } proctex; struct Lighting { @@ -101,9 +128,14 @@ struct State { float diff = static_cast(difference) / 2047.f; return neg_difference ? -diff : diff; } + + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& raw; + } }; - std::array, 24> luts; + std::array, 24> luts; } lighting; struct { @@ -123,11 +155,12 @@ struct State { } }; - std::array lut; + UnionArray lut; } fog; /// Current Pica command list struct { + PAddr addr; // This exists only for serialization const u32* head_ptr; const u32* current_ptr; u32 length; @@ -141,6 +174,16 @@ struct State { u32 current_attribute = 0; // Indicates the immediate mode just started and the geometry pipeline needs to reconfigure bool reset_geometry_pipeline = true; + + private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& input_vertex; + ar& current_attribute; + ar& reset_geometry_pipeline; + } + } immediate; // the geometry shader needs to be kept in the global state because some shaders relie on @@ -161,6 +204,46 @@ struct State { int default_attr_counter = 0; u32 default_attr_write_buffer[3]{}; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& regs.reg_array; + ar& vs; + ar& gs; + ar& input_default_attributes; + ar& proctex; + ar& lighting.luts; + ar& fog.lut; + ar& cmd_list.addr; + ar& cmd_list.length; + ar& immediate; + ar& gs_unit; + ar& geometry_pipeline; + ar& primitive_assembler; + ar& vs_float_regs_counter; + ar& vs_uniform_write_buffer; + ar& gs_float_regs_counter; + ar& gs_uniform_write_buffer; + ar& default_attr_counter; + ar& default_attr_write_buffer; + boost::serialization::split_member(ar, *this, file_version); + } + + template + void save(Archive& ar, const unsigned int file_version) const { + ar << static_cast(cmd_list.current_ptr - cmd_list.head_ptr); + } + + template + void load(Archive& ar, const unsigned int file_version) { + u32 offset{}; + ar >> offset; + cmd_list.head_ptr = + reinterpret_cast(VideoCore::g_memory->GetPhysicalPointer(cmd_list.addr)); + cmd_list.current_ptr = cmd_list.head_ptr + offset; + } }; extern State g_state; ///< Current Pica state diff --git a/src/video_core/pica_types.h b/src/video_core/pica_types.h index 5aca37b693..33012c2595 100644 --- a/src/video_core/pica_types.h +++ b/src/video_core/pica_types.h @@ -6,6 +6,7 @@ #include #include +#include #include "common/common_types.h" namespace Pica { @@ -140,6 +141,12 @@ private: // Stored as a regular float, merely for convenience // TODO: Perform proper arithmetic on this! float value; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& value; + } }; using float24 = Float<16, 7>; diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h index fd5445aa8e..404bc5316c 100644 --- a/src/video_core/primitive_assembly.h +++ b/src/video_core/primitive_assembly.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "video_core/regs_pipeline.h" namespace Pica { @@ -62,6 +63,16 @@ private: VertexType buffer[2]; bool strip_ready = false; bool winding = false; + + template + void serialize(Archive& ar, const unsigned int version) { + ar& topology; + ar& buffer_index; + ar& buffer; + ar& strip_ready; + ar& winding; + } + friend class boost::serialization::access; }; } // namespace Pica diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 468d84084a..873e4273ea 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -55,6 +55,9 @@ public: /// and invalidated virtual void FlushAndInvalidateRegion(PAddr addr, u32 size) = 0; + /// Removes as much state as possible from the rasterizer in preparation for a save/load state + virtual void ClearAll(bool flush) = 0; + /// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0 virtual bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { return false; @@ -84,5 +87,7 @@ public: virtual void LoadDiskResources(const std::atomic_bool& stop_loading, const DiskResourceLoadCallback& callback) {} + + virtual void SyncEntireState() {} }; } // namespace VideoCore diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 4065d1a7de..8d18b68008 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -28,3 +28,7 @@ void RendererBase::RefreshRasterizerSetting() { } } } + +void RendererBase::Sync() { + rasterizer->SyncEntireState(); +} diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 7fcaf53702..578939a6e6 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -68,6 +68,7 @@ public: } void RefreshRasterizerSetting(); + void Sync(); protected: Frontend::EmuWindow& render_window; ///< Reference to the render window handle. diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 4ac9e36c2a..b2fd23ff7d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1367,6 +1367,10 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { res_cache.InvalidateRegion(addr, size, nullptr); } +void RasterizerOpenGL::ClearAll(bool flush) { + res_cache.ClearAll(flush); +} + bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { MICROPROFILE_SCOPE(OpenGL_Blits); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index b3356a69b6..fee8363b63 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -53,6 +53,7 @@ public: void FlushRegion(PAddr addr, u32 size) override; void InvalidateRegion(PAddr addr, u32 size) override; void FlushAndInvalidateRegion(PAddr addr, u32 size) override; + void ClearAll(bool flush) override; bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) override; bool AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) override; bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config) override; @@ -60,6 +61,9 @@ public: u32 pixel_stride, ScreenInfo& screen_info) override; bool AccelerateDrawBatch(bool is_indexed) override; + /// Syncs entire status to match PICA registers + void SyncEntireState() override; + private: struct SamplerInfo { using TextureConfig = Pica::TexturingRegs::TextureConfig; @@ -131,9 +135,6 @@ private: GLvec3 view; }; - /// Syncs entire status to match PICA registers - void SyncEntireState(); - /// Syncs the clip enabled status to match the PICA register void SyncClipEnabled(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index b8aaabf96b..040e6e4778 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1071,9 +1071,7 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() { } RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { - FlushAll(); - while (!surface_cache.empty()) - UnregisterSurface(*surface_cache.begin()->second.begin()); + ClearAll(false); } MICROPROFILE_DEFINE(OpenGL_BlitSurface, "OpenGL", "BlitSurface", MP_RGB(128, 192, 64)); @@ -1767,6 +1765,31 @@ bool RasterizerCacheOpenGL::ValidateByReinterpretation(const Surface& surface, return false; } +void RasterizerCacheOpenGL::ClearAll(bool flush) { + const auto flush_interval = PageMap::interval_type::right_open(0x0, 0xFFFFFFFF); + // Force flush all surfaces from the cache + if (flush) { + FlushRegion(0x0, 0xFFFFFFFF); + } + // Unmark all of the marked pages + for (auto& pair : RangeFromInterval(cached_pages, flush_interval)) { + const auto interval = pair.first & flush_interval; + const int count = pair.second; + + const PAddr interval_start_addr = boost::icl::first(interval) << Memory::PAGE_BITS; + const PAddr interval_end_addr = boost::icl::last_next(interval) << Memory::PAGE_BITS; + const u32 interval_size = interval_end_addr - interval_start_addr; + + VideoCore::g_memory->RasterizerMarkRegionCached(interval_start_addr, interval_size, false); + } + + // Remove the whole cache without really looking at it. + cached_pages -= flush_interval; + dirty_regions -= SurfaceInterval(0x0, 0xFFFFFFFF); + surface_cache -= SurfaceInterval(0x0, 0xFFFFFFFF); + remove_surfaces.clear(); +} + void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surface) { if (size == 0) return; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 24dd9f594a..673beb4496 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -280,6 +280,9 @@ public: /// Flush all cached resources tracked by this cache manager void FlushAll(); + /// Clear all cached resources tracked by this cache manager + void ClearAll(bool flush); + private: void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface); diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index bb6a5fae73..05a1e8b80a 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #include #include "common/assert.h" #include "common/common_funcs.h" @@ -31,6 +34,13 @@ using SwizzleData = std::array; struct AttributeBuffer { alignas(16) Common::Vec4 attr[16]; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& attr; + } }; /// Handler type for receiving vertex outputs from vertex shader or geometry shader @@ -54,6 +64,20 @@ struct OutputVertex { static void ValidateSemantics(const RasterizerRegs& regs); static OutputVertex FromAttributeBuffer(const RasterizerRegs& regs, const AttributeBuffer& output); + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& pos; + ar& quat; + ar& color; + ar& tc0; + ar& tc1; + ar& tc0_w; + ar& view; + ar& tc2; + } + friend class boost::serialization::access; }; #define ASSERT_POS(var, pos) \ static_assert(offsetof(OutputVertex, var) == pos * sizeof(float24), "Semantic at wrong " \ @@ -90,6 +114,18 @@ struct GSEmitter { GSEmitter(); ~GSEmitter(); void Emit(Common::Vec4 (&output_regs)[16]); + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& buffer; + ar& vertex_id; + ar& prim_emit; + ar& winding; + ar& output_mask; + // Handlers are ignored because they're constant + } }; static_assert(std::is_standard_layout::value, "GSEmitter is not standard layout type"); @@ -107,6 +143,15 @@ struct UnitState { alignas(16) Common::Vec4 input[16]; alignas(16) Common::Vec4 temporary[16]; alignas(16) Common::Vec4 output[16]; + + private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& input; + ar& temporary; + ar& output; + } } registers; static_assert(std::is_pod::value, "Structure is not POD"); @@ -159,6 +204,16 @@ struct UnitState { void LoadInput(const ShaderRegs& config, const AttributeBuffer& input); void WriteOutput(const ShaderRegs& config, AttributeBuffer& output); + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& registers; + ar& conditional_code; + ar& address_registers; + // emitter_ptr is only set by GSUnitState and is serialized there + } }; /** @@ -172,6 +227,14 @@ struct GSUnitState : public UnitState { void ConfigOutput(const ShaderRegs& config); GSEmitter emitter; + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& boost::serialization::base_object(*this); + ar& emitter; + } }; struct Uniforms { @@ -193,6 +256,15 @@ struct Uniforms { static std::size_t GetIntUniformOffset(unsigned index) { return offsetof(Uniforms, i) + index * sizeof(Common::Vec4); } + +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& f; + ar& b; + ar& i; + } }; struct ShaderSetup { @@ -237,6 +309,18 @@ private: bool swizzle_data_hash_dirty = true; u64 program_code_hash = 0xDEADC0DE; u64 swizzle_data_hash = 0xDEADC0DE; + + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int file_version) { + ar& uniforms; + ar& program_code; + ar& swizzle_data; + ar& program_code_hash_dirty; + ar& swizzle_data_hash_dirty; + ar& program_code_hash; + ar& swizzle_data_hash; + } }; class ShaderEngine { diff --git a/src/video_core/swrasterizer/swrasterizer.h b/src/video_core/swrasterizer/swrasterizer.h index e2292f4a4c..9e7a140f1d 100644 --- a/src/video_core/swrasterizer/swrasterizer.h +++ b/src/video_core/swrasterizer/swrasterizer.h @@ -22,6 +22,7 @@ class SWRasterizer : public RasterizerInterface { void FlushRegion(PAddr addr, u32 size) override {} void InvalidateRegion(PAddr addr, u32 size) override {} void FlushAndInvalidateRegion(PAddr addr, u32 size) override {} + void ClearAll(bool flush) override {} }; } // namespace VideoCore diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 1748efcc63..619ea3d4c5 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -3,9 +3,11 @@ // Refer to the license.txt file included. #include +#include "common/archives.h" #include "common/logging/log.h" #include "core/settings.h" #include "video_core/pica.h" +#include "video_core/pica_state.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_vars.h" #include "video_core/renderer_opengl/renderer_opengl.h" @@ -87,4 +89,11 @@ u16 GetResolutionScaleFactor() { } } +template +void serialize(Archive& ar, const unsigned int) { + ar& Pica::g_state; +} + } // namespace VideoCore + +SERIALIZE_IMPL(VideoCore) diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 9951cd9a76..409f4deb2a 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "core/frontend/emu_window.h" @@ -62,4 +63,7 @@ void RequestScreenshot(void* data, std::function callback, u16 GetResolutionScaleFactor(); +template +void serialize(Archive& ar, const unsigned int file_version); + } // namespace VideoCore