From 26e90a99cd25685ee96184a8e21ff010913d8895 Mon Sep 17 00:00:00 2001
From: Hamish Milne <hamishmilne83@gmail.com>
Date: Fri, 3 Jan 2020 17:19:59 +0000
Subject: [PATCH] Added basic UI; misc memory fixes

---
 TODO                             | 13 +++++++++----
 src/citra_qt/main.cpp            | 24 ++++++++++++++++++++++++
 src/citra_qt/main.h              |  2 ++
 src/citra_qt/main.ui             | 18 ++++++++++++++++++
 src/common/CMakeLists.txt        |  1 -
 src/core/hle/kernel/config_mem.h |  3 +--
 src/core/hle/kernel/ipc.cpp      | 12 ++++++------
 src/core/hle/kernel/ipc.h        | 12 +++++-------
 src/core/hle/kernel/process.cpp  |  2 +-
 src/core/hle/kernel/process.h    |  2 +-
 src/core/hw/gpu.h                |  3 +--
 src/core/memory.cpp              | 19 ++++++++-----------
 src/core/memory.h                | 15 ++++++++++++++-
 13 files changed, 90 insertions(+), 36 deletions(-)

diff --git a/TODO b/TODO
index bc4003d44..ad64a0084 100644
--- a/TODO
+++ b/TODO
@@ -1,8 +1,11 @@
 ☐ Save/load UI
+    ✔ Basic version @done(20-01-03 15:27)
+    ☐ Multiple slots etc.
 ✔ CPU @done(19-08-13 15:41)
 ✔ Memory @done(19-08-13 15:41)
     ☐ Page tables
-    ☐ Skip N3DS RAM if unused
+    ✘ Skip N3DS RAM if unused @cancelled(20-01-03 15:26)
+        Since no n3ds support, leave this for now
 ✔ DSP @done(19-12-28 16:57)
     Memory only
 ✔ Service manager @started(19-12-23 00:36) @done(19-12-23 11:38) @lasted(11h2m3s)
@@ -67,14 +70,16 @@
         ✔ Mutex @done(19-08-13 16:43)
         ✔ Object @done(19-08-13 15:41)
         ✔ Process @started(19-08-13 16:43) @done(19-12-22 18:41)
-        ☐ Code set @started(19-12-22 18:41)
+        ✔ Code set @started(19-12-22 18:41) @done(20-01-03 15:15) @lasted(1w4d20h34m2s)
             Needs a way to reference loaded images (so we don't serialize the entire ROM as well)
+            ☐ Serialize codeset with an apploader reference instead
         ✔ Resource limit @done(19-08-13 16:43)
         ✔ Semaphore @done(19-08-13 16:44)
         ✔ Server port @done(19-08-13 16:44)
         ✔ Server session @done(19-08-13 16:44)
-            ☐ Mapped buffer context
-                This may not be needed!
+            ✔ Mapped buffer context @done(20-01-03 15:25)
+                This is needed because IPC can take as long as it takes
+                Changed the unique_ptr<u8[]> to vector<u8>
         ✔ Session @done(19-08-13 16:44)
         ☐ Shared memory @started(19-12-22 21:20)
             Need to figure out backing memory (a u8*)
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index daccbb083..08e3cac02 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include <clocale>
+#include <fstream>
 #include <memory>
 #include <thread>
 #include <QDesktopWidget>
@@ -606,6 +607,8 @@ void GMainWindow::ConnectMenuEvents() {
             &GMainWindow::OnMenuReportCompatibility);
     connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
     connect(ui.action_Cheats, &QAction::triggered, this, &GMainWindow::OnCheats);
+    connect(ui.action_Save, &QAction::triggered, this, &GMainWindow::OnSave);
+    connect(ui.action_Load, &QAction::triggered, this, &GMainWindow::OnLoad);
 
     // View
     connect(ui.action_Single_Window_Mode, &QAction::triggered, this,
@@ -1033,6 +1036,8 @@ void GMainWindow::ShutdownGame() {
     ui.action_Stop->setEnabled(false);
     ui.action_Restart->setEnabled(false);
     ui.action_Cheats->setEnabled(false);
+    ui.action_Save->setEnabled(false);
+    ui.action_Load->setEnabled(false);
     ui.action_Load_Amiibo->setEnabled(false);
     ui.action_Remove_Amiibo->setEnabled(false);
     ui.action_Report_Compatibility->setEnabled(false);
@@ -1343,6 +1348,8 @@ void GMainWindow::OnStartGame() {
     ui.action_Stop->setEnabled(true);
     ui.action_Restart->setEnabled(true);
     ui.action_Cheats->setEnabled(true);
+    ui.action_Save->setEnabled(true);
+    ui.action_Load->setEnabled(true);
     ui.action_Load_Amiibo->setEnabled(true);
     ui.action_Report_Compatibility->setEnabled(true);
     ui.action_Enable_Frame_Advancing->setEnabled(true);
@@ -1496,6 +1503,23 @@ void GMainWindow::OnCheats() {
     cheat_dialog.exec();
 }
 
+void GMainWindow::OnSave() {
+    Core::System& system{Core::System::GetInstance()};
+    auto fs = std::ofstream("save0.citrasave");
+    emu_thread->SetRunning(false);
+    Core::System::GetInstance().Save(fs);
+    emu_thread->SetRunning(true);
+}
+
+void GMainWindow::OnLoad() {
+    if (QFileInfo("save0.citrasave").exists()) {
+        auto fs = std::ifstream("save0.citrasave");
+        emu_thread->SetRunning(false);
+        Core::System::GetInstance().Load(fs);
+        emu_thread->SetRunning(true);
+    }
+}
+
 void GMainWindow::OnConfigure() {
     ConfigureDialog configureDialog(this, hotkey_registry,
                                     !multiplayer_state->IsHostingPublicRoom());
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 91d7eed1b..1858d5988 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -163,6 +163,8 @@ private slots:
     void OnStartGame();
     void OnPauseGame();
     void OnStopGame();
+    void OnSave();
+    void OnLoad();
     void OnMenuReportCompatibility();
     /// Called whenever a user selects a game in the game list widget.
     void OnGameListLoadFile(QString game_path);
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index 6011b0e7f..c0c38e4c8 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -88,6 +88,8 @@
     <addaction name="separator"/>
     <addaction name="action_Configure"/>
     <addaction name="action_Cheats"/>
+    <addaction name="action_Save"/>
+    <addaction name="action_Load"/>
    </widget>
    <widget class="QMenu" name="menu_View">
     <property name="title">
@@ -217,6 +219,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>
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 39ddffbcc..c6294c464 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -86,7 +86,6 @@ add_library(common STATIC
     misc.cpp
     param_package.cpp
     param_package.h
-    pod.h
     quaternion.h
     ring_buffer.h
     scm_rev.cpp
diff --git a/src/core/hle/kernel/config_mem.h b/src/core/hle/kernel/config_mem.h
index e4e895516..ef1c889a0 100644
--- a/src/core/hle/kernel/config_mem.h
+++ b/src/core/hle/kernel/config_mem.h
@@ -61,8 +61,7 @@ private:
     friend class boost::serialization::access;
     template <class Archive>
     void serialize(Archive& ar, const unsigned int file_version) {
-        auto o_config_mem = boost::serialization::binary_object(&config_mem, sizeof(config_mem));
-        ar& o_config_mem;
+        ar& boost::serialization::make_binary_object(&config_mem, sizeof(config_mem));
     }
 };
 
diff --git a/src/core/hle/kernel/ipc.cpp b/src/core/hle/kernel/ipc.cpp
index 3dacb9831..e45b87e49 100644
--- a/src/core/hle/kernel/ipc.cpp
+++ b/src/core/hle/kernel/ipc.cpp
@@ -193,19 +193,19 @@ 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);
+            auto reserve_buffer = std::vector<u8>(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.data(),
                 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);
+            auto buffer = std::vector<u8>(num_pages * Memory::PAGE_SIZE);
+            memory.ReadBlock(*src_process, source_address, buffer.data() + 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,
+                                            buffer.data(), num_pages * Memory::PAGE_SIZE,
                                             Kernel::MemoryState::Shared)
                     .Unwrap();
 
@@ -213,7 +213,7 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
 
             // 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::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer.data(),
                 Memory::PAGE_SIZE, Kernel::MemoryState::Reserved);
 
             mapped_buffer_context.push_back({permissions, size, source_address,
diff --git a/src/core/hle/kernel/ipc.h b/src/core/hle/kernel/ipc.h
index 2c69617d4..f9fd1a00e 100644
--- a/src/core/hle/kernel/ipc.h
+++ b/src/core/hle/kernel/ipc.h
@@ -6,7 +6,7 @@
 
 #include <memory>
 #include <vector>
-#include <boost/serialization/unique_ptr.hpp>
+#include <boost/serialization/vector.hpp>
 #include "common/common_types.h"
 #include "core/hle/ipc.h"
 #include "core/hle/kernel/thread.h"
@@ -25,8 +25,8 @@ struct MappedBufferContext {
     VAddr source_address;
     VAddr target_address;
 
-    std::unique_ptr<u8[]> buffer;
-    std::unique_ptr<u8[]> reserve_buffer;
+    std::vector<u8> buffer;
+    std::vector<u8> reserve_buffer;
 
 private:
     template <class Archive>
@@ -35,10 +35,8 @@ private:
         ar& size;
         ar& source_address;
         ar& target_address;
-        // TODO: Check whether we need these. If we do, add a field for the size and/or change to a
-        // 'vector'
-        // ar & buffer;
-        // ar & reserve_buffer;
+        ar& buffer;
+        ar& reserve_buffer;
     }
     friend class boost::serialization::access;
 };
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 78b695e97..18de2cd91 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -29,7 +29,7 @@ template <class Archive>
 void Process::serialize(Archive& ar, const unsigned int file_version) {
     ar& boost::serialization::base_object<Object>(*this);
     ar& handle_table;
-    ar& codeset;
+    ar& codeset; // TODO: Replace with apploader reference
     ar& resource_limit;
     ar& svc_access_mask;
     ar& handle_table_size;
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index f9aa76c67..576cf7e15 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -133,7 +133,7 @@ private:
     template <class Archive>
     void serialize(Archive& ar, const unsigned int file_version) {
         ar& boost::serialization::base_object<Object>(*this);
-        // TODO: memory reference
+        ar& memory;
         ar& segments;
         ar& entrypoint;
         ar& name;
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index fcf7a3137..9169bf9ae 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -301,8 +301,7 @@ private:
 
     template <class Archive>
     void serialize(Archive& ar, const unsigned int) {
-        auto obj = boost::serialization::binary_object(this, sizeof(Regs));
-        ar& obj;
+        ar& boost::serialization::make_binary_object(this, sizeof(Regs));
     }
     friend class boost::serialization::access;
 };
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 6a41ca957..92bce0b69 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -84,18 +84,15 @@ private:
     friend class boost::serialization::access;
     template <class Archive>
     void serialize(Archive& ar, const unsigned int file_version) {
-        // TODO: Skip n3ds ram when not used?
-        auto s_fcram = boost::serialization::binary_object(fcram.get(), Memory::FCRAM_N3DS_SIZE);
-        auto s_vram = boost::serialization::binary_object(vram.get(), Memory::VRAM_SIZE);
-        auto s_extra =
-            boost::serialization::binary_object(n3ds_extra_ram.get(), Memory::N3DS_EXTRA_RAM_SIZE);
-        ar& s_fcram;
-        ar& s_vram;
-        ar& s_extra;
+        ar& boost::serialization::make_binary_object(fcram.get(), Memory::FCRAM_N3DS_SIZE);
+        ar& boost::serialization::make_binary_object(vram.get(), Memory::VRAM_SIZE);
+        // TODO: When n3ds support is added, put this back in
+        // ar& boost::serialization::make_binary_object(n3ds_extra_ram.get(),
+        //                                              Memory::N3DS_EXTRA_RAM_SIZE);
+        ar& current_page_table;
         ar& cache_marker;
-        // TODO: How the hell to do page tables..
-        // ar & page_table_list;
-        // ar & current_page_table;
+        ar& page_table_list;
+        // dsp is set from Core::System at startup
     }
 };
 
diff --git a/src/core/memory.h b/src/core/memory.h
index aac57af26..564191d89 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -9,7 +9,9 @@
 #include <memory>
 #include <string>
 #include <vector>
-#include "boost/serialization/access.hpp"
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/array.hpp>
+#include <boost/serialization/vector.hpp>
 #include "common/common_types.h"
 #include "core/mmio.h"
 
@@ -54,12 +56,14 @@ struct SpecialRegion {
     u32 size;
     MMIORegionPointer handler;
 
+private:
     template <class Archive>
     void serialize(Archive& ar, const unsigned int file_version) {
         ar& base;
         ar& size;
         ar& handler;
     }
+    friend class boost::serialization::access;
 };
 
 /**
@@ -86,6 +90,15 @@ struct PageTable {
      * the corresponding entry in `pointers` MUST be set to null.
      */
     std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
+
+private:
+    template <class Archive>
+    void serialize(Archive& ar, const unsigned int) {
+        // TODO: Pointers; same as VMA backing regions we need to serialize the u8*
+        ar& special_regions;
+        ar& attributes;
+    }
+    friend class boost::serialization::access;
 };
 
 /// Physical memory regions as seen from the ARM11