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