Merge pull request #4908 from hamish-milne/feature/savestates-2

Save states
This commit is contained in:
Ben 2020-04-17 21:52:51 +02:00 committed by GitHub
commit c605bb42db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
354 changed files with 6100 additions and 604 deletions

View file

@ -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)

View file

@ -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

@ -1 +1 @@
Subproject commit 502437b2ae3f1da821aa7d5d5174ec356fa89269
Subproject commit 36603a1e665e849d29b1735a12c0a51284a10dd0

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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_) {

View file

@ -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)

View file

@ -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

View file

@ -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");

View file

@ -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

View file

@ -132,7 +132,9 @@ void OpenGLWindow::Present() {
return;
context->makeCurrent(this);
if (VideoCore::g_renderer) {
VideoCore::g_renderer->TryPresent(100);
}
context->swapBuffers(this);
auto f = context->versionFunctions<QOpenGLFunctions_3_3_Core>();
f->glFinish();

View file

@ -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}},

View file

@ -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(

View file

@ -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 =

View file

@ -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;

View file

@ -79,11 +79,28 @@
<property name="title">
<string>&amp;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>&amp;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>

View file

@ -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
View 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)

View file

@ -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
View 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); \
} \
}

View file

@ -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

View file

@ -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

View 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
View 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;
};

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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 = &current_page_table->pointers;
config.page_table = &current_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();
}

View file

@ -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;
};

View file

@ -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];
}

View file

@ -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);

View file

@ -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); });

View file

@ -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);

View file

@ -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();
telemetry_session.reset();
if (!is_deserializing) {
GDBStub::Shutdown();
perf_stats.reset();
rpc_server.reset();
cheat_engine.reset();
app_loader.reset();
}
telemetry_session.reset();
rpc_server.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

View file

@ -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)

View file

@ -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();
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;

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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(

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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) {

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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,

View 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)

View file

@ -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(&current, current_directory_offset);
directory_metadata_offset_map.emplace(&current, static_cast<u32>(current_directory_offset));
directory_list.emplace_back(&current);
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(&current, current_file_offset);
file_metadata_offset_map.emplace(&current, static_cast<u32>(current_file_offset));
file_list.emplace_back(&current);
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);

View file

@ -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)

View file

@ -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());

View file

@ -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)

View file

@ -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)

View file

@ -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
View 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

View file

@ -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;
}
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,
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 = std::dynamic_pointer_cast<WakeupCallback>(shared_from_this());
switch (type) {

View file

@ -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)

View file

@ -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) {}

View file

@ -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)

View file

@ -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) {}

View file

@ -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)

View file

@ -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() {

View file

@ -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)

View file

@ -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) {}

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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),

View file

@ -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.

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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,18 +160,14 @@ 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()),
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()),
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);

View file

@ -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

View file

@ -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