summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/citra.cpp13
-rw-r--r--src/citra/config.cpp3
-rw-r--r--src/citra/default_ini.h8
-rw-r--r--src/citra_qt/CMakeLists.txt1
-rw-r--r--src/citra_qt/config.cpp4
-rw-r--r--src/citra_qt/configure_graphics.cpp2
-rw-r--r--src/citra_qt/configure_graphics.ui7
-rw-r--r--src/citra_qt/main.cpp23
-rw-r--r--src/citra_qt/main.h7
-rw-r--r--src/citra_qt/version.h11
-rw-r--r--src/common/common_paths.h2
-rw-r--r--src/common/file_util.cpp13
-rw-r--r--src/common/file_util.h1
-rw-r--r--src/common/key_map.cpp8
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h4
-rw-r--r--src/core/CMakeLists.txt36
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp4
-rw-r--r--src/core/file_sys/archive_backend.h28
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp115
-rw-r--r--src/core/file_sys/archive_ncch.cpp (renamed from src/core/file_sys/archive_savedatacheck.cpp)22
-rw-r--r--src/core/file_sys/archive_ncch.h (renamed from src/core/file_sys/archive_savedatacheck.h)8
-rw-r--r--src/core/file_sys/archive_other_savedata.cpp145
-rw-r--r--src/core/file_sys/archive_other_savedata.h52
-rw-r--r--src/core/file_sys/archive_romfs.h2
-rw-r--r--src/core/file_sys/archive_savedata.cpp79
-rw-r--r--src/core/file_sys/archive_savedata.h8
-rw-r--r--src/core/file_sys/archive_sdmc.cpp279
-rw-r--r--src/core/file_sys/archive_sdmc.h28
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.cpp70
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.h57
-rw-r--r--src/core/file_sys/archive_source_sd_savedata.cpp93
-rw-r--r--src/core/file_sys/archive_source_sd_savedata.h30
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp4
-rw-r--r--src/core/file_sys/archive_systemsavedata.h2
-rw-r--r--src/core/file_sys/directory_backend.h6
-rw-r--r--src/core/file_sys/disk_archive.cpp150
-rw-r--r--src/core/file_sys/disk_archive.h43
-rw-r--r--src/core/file_sys/errors.h43
-rw-r--r--src/core/file_sys/file_backend.h6
-rw-r--r--src/core/file_sys/ivfc_archive.cpp31
-rw-r--r--src/core/file_sys/ivfc_archive.h20
-rw-r--r--src/core/file_sys/path_parser.cpp98
-rw-r--r--src/core/file_sys/path_parser.h61
-rw-r--r--src/core/file_sys/savedata_archive.cpp283
-rw-r--r--src/core/file_sys/savedata_archive.h43
-rw-r--r--src/core/hle/applets/applet.cpp4
-rw-r--r--src/core/hle/applets/applet.h10
-rw-r--r--src/core/hle/applets/erreula.cpp4
-rw-r--r--src/core/hle/applets/erreula.h8
-rw-r--r--src/core/hle/applets/mii_selector.cpp4
-rw-r--r--src/core/hle/applets/mii_selector.h9
-rw-r--r--src/core/hle/applets/swkbd.cpp4
-rw-r--r--src/core/hle/applets/swkbd.h9
-rw-r--r--src/core/hle/kernel/kernel.cpp6
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/result.h10
-rw-r--r--src/core/hle/service/ac_u.cpp20
-rw-r--r--src/core/hle/service/act_a.cpp7
-rw-r--r--src/core/hle/service/act_u.cpp3
-rw-r--r--src/core/hle/service/am/am.cpp10
-rw-r--r--src/core/hle/service/am/am.h20
-rw-r--r--src/core/hle/service/am/am_app.cpp5
-rw-r--r--src/core/hle/service/am/am_net.cpp157
-rw-r--r--src/core/hle/service/am/am_sys.cpp71
-rw-r--r--src/core/hle/service/am/am_u.cpp96
-rw-r--r--src/core/hle/service/apt/apt_a.cpp77
-rw-r--r--src/core/hle/service/apt/apt_s.cpp3
-rw-r--r--src/core/hle/service/boss/boss_p.cpp71
-rw-r--r--src/core/hle/service/cecd/cecd_u.cpp14
-rw-r--r--src/core/hle/service/cfg/cfg.cpp9
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp1
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp1
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp1
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp7
-rw-r--r--src/core/hle/service/frd/frd.cpp12
-rw-r--r--src/core/hle/service/frd/frd.h9
-rw-r--r--src/core/hle/service/frd/frd_u.cpp2
-rw-r--r--src/core/hle/service/fs/archive.cpp70
-rw-r--r--src/core/hle/service/fs/archive.h6
-rw-r--r--src/core/hle/service/fs/fs_user.cpp26
-rw-r--r--src/core/hle/service/gsp_gpu.cpp2
-rw-r--r--src/core/hle/service/gsp_lcd.cpp4
-rw-r--r--src/core/hle/service/http_c.cpp4
-rw-r--r--src/core/hle/service/mic_u.cpp340
-rw-r--r--src/core/hle/service/mic_u.h1
-rw-r--r--src/core/hle/service/mvd/mvd.cpp17
-rw-r--r--src/core/hle/service/mvd/mvd.h14
-rw-r--r--src/core/hle/service/mvd/mvd_std.cpp32
-rw-r--r--src/core/hle/service/mvd/mvd_std.h22
-rw-r--r--src/core/hle/service/nfc/nfc.cpp18
-rw-r--r--src/core/hle/service/nfc/nfc.h14
-rw-r--r--src/core/hle/service/nfc/nfc_m.cpp44
-rw-r--r--src/core/hle/service/nfc/nfc_m.h22
-rw-r--r--src/core/hle/service/nfc/nfc_u.cpp41
-rw-r--r--src/core/hle/service/nfc/nfc_u.h22
-rw-r--r--src/core/hle/service/nim/nim_s.cpp1
-rw-r--r--src/core/hle/service/nim/nim_u.cpp1
-rw-r--r--src/core/hle/service/nwm_uds.cpp12
-rw-r--r--src/core/hle/service/pm_app.cpp15
-rw-r--r--src/core/hle/service/ptm/ptm.cpp15
-rw-r--r--src/core/hle/service/ptm/ptm.h7
-rw-r--r--src/core/hle/service/ptm/ptm_gets.cpp37
-rw-r--r--src/core/hle/service/ptm/ptm_gets.h22
-rw-r--r--src/core/hle/service/ptm/ptm_play.cpp20
-rw-r--r--src/core/hle/service/ptm/ptm_play.h4
-rw-r--r--src/core/hle/service/ptm/ptm_sets.cpp20
-rw-r--r--src/core/hle/service/ptm/ptm_sets.h22
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp27
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.h13
-rw-r--r--src/core/hle/service/ptm/ptm_u.cpp2
-rw-r--r--src/core/hle/service/ptm/ptm_u.h4
-rw-r--r--src/core/hle/service/qtm/qtm.cpp21
-rw-r--r--src/core/hle/service/qtm/qtm.h14
-rw-r--r--src/core/hle/service/qtm/qtm_s.cpp23
-rw-r--r--src/core/hle/service/qtm/qtm_s.h22
-rw-r--r--src/core/hle/service/qtm/qtm_sp.cpp23
-rw-r--r--src/core/hle/service/qtm/qtm_sp.h22
-rw-r--r--src/core/hle/service/qtm/qtm_u.cpp23
-rw-r--r--src/core/hle/service/qtm/qtm_u.h22
-rw-r--r--src/core/hle/service/service.cpp66
-rw-r--r--src/core/hle/service/service.h12
-rw-r--r--src/core/hle/service/ssl_c.cpp3
-rw-r--r--src/core/hw/gpu.cpp58
-rw-r--r--src/core/hw/gpu.h1
-rw-r--r--src/core/loader/loader.h12
-rw-r--r--src/core/loader/ncch.cpp10
-rw-r--r--src/core/loader/ncch.h6
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/system.cpp4
-rw-r--r--src/core/system.h2
-rw-r--r--src/tests/CMakeLists.txt2
-rw-r--r--src/tests/core/file_sys/path_parser.cpp38
-rw-r--r--src/tests/glad.cpp14
-rw-r--r--src/video_core/command_processor.cpp4
-rw-r--r--src/video_core/pica.h2
-rw-r--r--src/video_core/rasterizer.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp51
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp31
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_state.h3
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp17
-rw-r--r--src/video_core/video_core.cpp1
-rw-r--r--src/video_core/video_core.h1
147 files changed, 3314 insertions, 784 deletions
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index e47375f88..f9387e61c 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -129,15 +129,22 @@ int main(int argc, char** argv) {
std::unique_ptr<EmuWindow_SDL2> emu_window = std::make_unique<EmuWindow_SDL2>();
- System::Init(emu_window.get());
- SCOPE_EXIT({ System::Shutdown(); });
-
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(boot_filename);
if (!loader) {
LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", boot_filename.c_str());
return -1;
}
+ boost::optional<u32> system_mode = loader->LoadKernelSystemMode();
+
+ if (!system_mode) {
+ LOG_CRITICAL(Frontend, "Failed to load ROM (Could not determine system mode)!");
+ return -1;
+ }
+
+ System::Init(emu_window.get(), system_mode.get());
+ SCOPE_EXIT({ System::Shutdown(); });
+
Loader::ResultStatus load_result = loader->Load();
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Frontend, "Failed to load ROM (Error %i)!", load_result);
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index fd30bfc85..29462c982 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -59,7 +59,6 @@ void Config::ReadValues() {
// Core
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
- Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
// Renderer
Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true);
@@ -67,6 +66,8 @@ void Config::ReadValues() {
Settings::values.use_scaled_resolution =
sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false);
Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false);
+ Settings::values.toggle_framelimit =
+ sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true);
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index b22627a2f..001b18ac2 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -42,10 +42,6 @@ pad_circle_modifier_scale =
# 0: Interpreter (slow), 1 (default): JIT (fast)
use_cpu_jit =
-# The applied frameskip amount. Must be a power of two.
-# 0 (default): No frameskip, 1: x2 frameskip, 2: x4 frameskip, 3: x8 frameskip, etc.
-frame_skip =
-
[Renderer]
# Whether to use software or hardware rendering.
# 0: Software, 1 (default): Hardware
@@ -68,6 +64,10 @@ use_vsync =
# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen
layout_option =
+#Whether to toggle frame limiter on or off.
+# 0: Off , 1 (default): On
+toggle_framelimit =
+
# Swaps the prominent screen with the other screen.
# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 384875450..a9dacd5f1 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -65,7 +65,6 @@ set(HEADERS
hotkeys.h
main.h
ui_settings.h
- version.h
)
set(UIS
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 3d2312619..06a4e9d25 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -39,7 +39,6 @@ void Config::ReadValues() {
qt_config->beginGroup("Core");
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
- Settings::values.frame_skip = qt_config->value("frame_skip", 0).toInt();
qt_config->endGroup();
qt_config->beginGroup("Renderer");
@@ -48,6 +47,7 @@ void Config::ReadValues() {
Settings::values.use_scaled_resolution =
qt_config->value("use_scaled_resolution", false).toBool();
Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool();
+ Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool();
Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat();
Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat();
@@ -146,7 +146,6 @@ void Config::SaveValues() {
qt_config->beginGroup("Core");
qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit);
- qt_config->setValue("frame_skip", Settings::values.frame_skip);
qt_config->endGroup();
qt_config->beginGroup("Renderer");
@@ -154,6 +153,7 @@ void Config::SaveValues() {
qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit);
qt_config->setValue("use_scaled_resolution", Settings::values.use_scaled_resolution);
qt_config->setValue("use_vsync", Settings::values.use_vsync);
+ qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit);
// Cast to double because Qt's written float values are not human-readable
qt_config->setValue("bg_red", (double)Settings::values.bg_red);
diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configure_graphics.cpp
index 29834e11b..36f10c8d7 100644
--- a/src/citra_qt/configure_graphics.cpp
+++ b/src/citra_qt/configure_graphics.cpp
@@ -23,6 +23,7 @@ void ConfigureGraphics::setConfiguration() {
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution);
ui->toggle_vsync->setChecked(Settings::values.use_vsync);
+ ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit);
ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option));
ui->swap_screen->setChecked(Settings::values.swap_screen);
}
@@ -32,6 +33,7 @@ void ConfigureGraphics::applyConfiguration() {
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked();
Settings::values.use_vsync = ui->toggle_vsync->isChecked();
+ Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked();
Settings::values.layout_option =
static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex());
Settings::values.swap_screen = ui->swap_screen->isChecked();
diff --git a/src/citra_qt/configure_graphics.ui b/src/citra_qt/configure_graphics.ui
index af16a4292..964aa0bbd 100644
--- a/src/citra_qt/configure_graphics.ui
+++ b/src/citra_qt/configure_graphics.ui
@@ -50,6 +50,13 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QCheckBox" name="toggle_framelimit">
+ <property name="text">
+ <string>Limit framerate</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 0bf9f48d6..a3887f9ab 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -253,7 +253,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
}
}
-bool GMainWindow::InitializeSystem() {
+bool GMainWindow::InitializeSystem(u32 system_mode) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
@@ -270,7 +270,7 @@ bool GMainWindow::InitializeSystem() {
}
// Initialize the core emulation
- System::Result system_result = System::Init(render_window);
+ System::Result system_result = System::Init(render_window, system_mode);
if (System::Result::Success != system_result) {
switch (system_result) {
case System::Result::ErrorInitVideoCore:
@@ -299,8 +299,20 @@ bool GMainWindow::LoadROM(const std::string& filename) {
return false;
}
+ boost::optional<u32> system_mode = app_loader->LoadKernelSystemMode();
+ if (!system_mode) {
+ LOG_CRITICAL(Frontend, "Failed to load ROM!");
+ QMessageBox::critical(this, tr("Error while loading ROM!"),
+ tr("Could not determine the system mode."));
+ return false;
+ }
+
+ if (!InitializeSystem(system_mode.get()))
+ return false;
+
Loader::ResultStatus result = app_loader->Load();
if (Loader::ResultStatus::Success != result) {
+ System::Shutdown();
LOG_CRITICAL(Frontend, "Failed to load ROM!");
switch (result) {
@@ -338,13 +350,8 @@ void GMainWindow::BootGame(const std::string& filename) {
LOG_INFO(Frontend, "Citra starting...");
StoreRecentFile(filename); // Put the filename on top of the list
- if (!InitializeSystem())
- return;
-
- if (!LoadROM(filename)) {
- System::Shutdown();
+ if (!LoadROM(filename))
return;
- }
// Create and start the emulation thread
emu_thread = std::make_unique<EmuThread>(render_window);
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 82eb90aae..f87178227 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -60,7 +60,12 @@ signals:
void EmulationStopping();
private:
- bool InitializeSystem();
+ /**
+ * Initializes the emulation system.
+ * @param system_mode The system mode with which to intialize the kernel.
+ * @returns Whether the system was properly initialized.
+ */
+ bool InitializeSystem(u32 system_mode);
bool LoadROM(const std::string& filename);
void BootGame(const std::string& filename);
void ShutdownGame();
diff --git a/src/citra_qt/version.h b/src/citra_qt/version.h
deleted file mode 100644
index 9d5a2b1a2..000000000
--- a/src/citra_qt/version.h
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// TODO: Supposed to be generated...
-// GENERATED - Do not edit!
-#ifndef VERSION_H_
-#define VERSION_H_
-#define __BUILD__ "40"
-#define VERSION __BUILD__
-#endif // VERSION_H_
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index a5342a610..37304d236 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -19,7 +19,7 @@
#define EMU_DATA_DIR USER_DIR
#else
#ifdef _WIN32
-#define EMU_DATA_DIR "Citra Emulator"
+#define EMU_DATA_DIR "Citra"
#else
#define EMU_DATA_DIR "citra-emu"
#endif
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 407ed047a..b6161f2dc 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -594,6 +594,15 @@ std::string& GetExeDirectory() {
}
return exe_path;
}
+
+std::string AppDataRoamingDirectory() {
+ PWSTR pw_local_path = nullptr;
+ // Only supported by Windows Vista or later
+ SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pw_local_path);
+ std::string local_path = Common::UTF16ToUTF8(pw_local_path);
+ CoTaskMemFree(pw_local_path);
+ return local_path;
+}
#else
/**
* @return The user’s home directory on POSIX systems
@@ -671,6 +680,10 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
if (paths[D_USER_IDX].empty()) {
#ifdef _WIN32
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
+ if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
+ paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
+ }
+
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
#else
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 204b06f14..ac58607c5 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -154,6 +154,7 @@ std::string GetBundleDirectory();
#ifdef _WIN32
std::string& GetExeDirectory();
+std::string AppDataRoamingDirectory();
#endif
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp
index 79b3fcb18..97cafe9c9 100644
--- a/src/common/key_map.cpp
+++ b/src/common/key_map.cpp
@@ -49,7 +49,7 @@ static bool circle_pad_right = false;
static bool circle_pad_modifier = false;
static void UpdateCirclePad(EmuWindow& emu_window) {
- constexpr float SQRT_HALF = 0.707106781;
+ constexpr float SQRT_HALF = 0.707106781f;
int x = 0, y = 0;
if (circle_pad_right)
@@ -61,9 +61,9 @@ static void UpdateCirclePad(EmuWindow& emu_window) {
if (circle_pad_down)
--y;
- float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0;
- emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF),
- y * modifier * (x == 0 ? 1.0 : SQRT_HALF));
+ float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0f;
+ emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0f : SQRT_HALF),
+ y * modifier * (x == 0 ? 1.0f : SQRT_HALF));
}
int NewDeviceId() {
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 88209081d..7fd397fe5 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -43,6 +43,7 @@ namespace Log {
SUB(Service, AM) \
SUB(Service, PTM) \
SUB(Service, LDR) \
+ SUB(Service, MIC) \
SUB(Service, NDM) \
SUB(Service, NIM) \
SUB(Service, NWM) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 8d3a2d03e..96d0dfb8c 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -60,6 +60,7 @@ enum class Class : ClassType {
Service_AM, ///< The AM (Application manager) service
Service_PTM, ///< The PTM (Power status & misc.) service
Service_LDR, ///< The LDR (3ds dll loader) service
+ Service_MIC, ///< The MIC (Microphone) service
Service_NDM, ///< The NDM (Network daemon manager) service
Service_NIM, ///< The NIM (Network interface manager) service
Service_NWM, ///< The NWM (Network wlan manager) service
@@ -84,8 +85,7 @@ enum class Class : ClassType {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
-
- Count ///< Total number of logging classes
+ Count ///< Total number of logging classes
};
/// Logs a message to the global logger.
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4a9c6fd2f..c2c04473c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -17,13 +17,18 @@ set(SRCS
core_timing.cpp
file_sys/archive_backend.cpp
file_sys/archive_extsavedata.cpp
+ file_sys/archive_ncch.cpp
+ file_sys/archive_other_savedata.cpp
file_sys/archive_romfs.cpp
file_sys/archive_savedata.cpp
- file_sys/archive_savedatacheck.cpp
file_sys/archive_sdmc.cpp
+ file_sys/archive_sdmcwriteonly.cpp
+ file_sys/archive_source_sd_savedata.cpp
file_sys/archive_systemsavedata.cpp
file_sys/disk_archive.cpp
file_sys/ivfc_archive.cpp
+ file_sys/path_parser.cpp
+ file_sys/savedata_archive.cpp
gdbstub/gdbstub.cpp
hle/config_mem.cpp
hle/hle.cpp
@@ -100,8 +105,13 @@ set(SRCS
hle/service/ldr_ro/ldr_ro.cpp
hle/service/ldr_ro/memory_synchronizer.cpp
hle/service/mic_u.cpp
+ hle/service/mvd/mvd.cpp
+ hle/service/mvd/mvd_std.cpp
hle/service/ndm/ndm.cpp
hle/service/ndm/ndm_u.cpp
+ hle/service/nfc/nfc.cpp
+ hle/service/nfc/nfc_m.cpp
+ hle/service/nfc/nfc_u.cpp
hle/service/news/news.cpp
hle/service/news/news_s.cpp
hle/service/news/news_u.cpp
@@ -113,9 +123,15 @@ set(SRCS
hle/service/nwm_uds.cpp
hle/service/pm_app.cpp
hle/service/ptm/ptm.cpp
+ hle/service/ptm/ptm_gets.cpp
hle/service/ptm/ptm_play.cpp
+ hle/service/ptm/ptm_sets.cpp
hle/service/ptm/ptm_sysm.cpp
hle/service/ptm/ptm_u.cpp
+ hle/service/qtm/qtm.cpp
+ hle/service/qtm/qtm_s.cpp
+ hle/service/qtm/qtm_sp.cpp
+ hle/service/qtm/qtm_u.cpp
hle/service/service.cpp
hle/service/soc_u.cpp
hle/service/srv.cpp
@@ -159,15 +175,20 @@ set(HEADERS
core_timing.h
file_sys/archive_backend.h
file_sys/archive_extsavedata.h
+ file_sys/archive_ncch.h
+ file_sys/archive_other_savedata.h
file_sys/archive_romfs.h
file_sys/archive_savedata.h
- file_sys/archive_savedatacheck.h
file_sys/archive_sdmc.h
+ file_sys/archive_sdmcwriteonly.h
+ file_sys/archive_source_sd_savedata.h
file_sys/archive_systemsavedata.h
file_sys/directory_backend.h
file_sys/disk_archive.h
file_sys/file_backend.h
file_sys/ivfc_archive.h
+ file_sys/path_parser.h
+ file_sys/savedata_archive.h
gdbstub/gdbstub.h
hle/config_mem.h
hle/function_wrappers.h
@@ -246,8 +267,13 @@ set(HEADERS
hle/service/ldr_ro/ldr_ro.h
hle/service/ldr_ro/memory_synchronizer.h
hle/service/mic_u.h
+ hle/service/mvd/mvd.h
+ hle/service/mvd/mvd_std.h
hle/service/ndm/ndm.h
hle/service/ndm/ndm_u.h
+ hle/service/nfc/nfc.h
+ hle/service/nfc/nfc_m.h
+ hle/service/nfc/nfc_u.h
hle/service/news/news.h
hle/service/news/news_s.h
hle/service/news/news_u.h
@@ -259,9 +285,15 @@ set(HEADERS
hle/service/nwm_uds.h
hle/service/pm_app.h
hle/service/ptm/ptm.h
+ hle/service/ptm/ptm_gets.h
hle/service/ptm/ptm_play.h
+ hle/service/ptm/ptm_sets.h
hle/service/ptm/ptm_sysm.h
hle/service/ptm/ptm_u.h
+ hle/service/qtm/qtm.h
+ hle/service/qtm/qtm_s.h
+ hle/service/qtm/qtm_sp.h
+ hle/service/qtm/qtm_u.h
hle/service/service.h
hle/service/soc_u.h
hle/service/srv.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 2595defdf..ca8a94ee9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -131,9 +131,9 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit);
- jit->Run(static_cast<unsigned>(num_instructions));
+ unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions));
- AddTicks(num_instructions);
+ AddTicks(ticks_executed);
}
void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) {
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index 06b8f2ed7..58f6c150c 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -87,7 +87,7 @@ public:
* @return Opened file, or error code
*/
virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
- const Mode mode) const = 0;
+ const Mode& mode) const = 0;
/**
* Delete a file specified by its path
@@ -100,53 +100,53 @@ public:
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
- * @return Whether rename succeeded
+ * @return Result of the operation
*/
- virtual bool RenameFile(const Path& src_path, const Path& dest_path) const = 0;
+ virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be deleted
+ * @return Result of the operation
*/
- virtual bool DeleteDirectory(const Path& path) const = 0;
+ virtual ResultCode DeleteDirectory(const Path& path) const = 0;
/**
* Delete a directory specified by its path and anything under it
* @param path Path relative to the archive
- * @return Whether the directory could be deleted
+ * @return Result of the operation
*/
- virtual bool DeleteDirectoryRecursively(const Path& path) const = 0;
+ virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
- * @return File creation result code
+ * @return Result of the operation
*/
virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
- * @return Whether the directory could be created
+ * @return Result of the operation
*/
- virtual bool CreateDirectory(const Path& path) const = 0;
+ virtual ResultCode CreateDirectory(const Path& path) const = 0;
/**
* Rename a Directory specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
- * @return Whether rename succeeded
+ * @return Result of the operation
*/
- virtual bool RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
+ virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
- * @return Opened directory, or nullptr
+ * @return Opened directory, or error code
*/
- virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
+ virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0;
/**
* Get the free space
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
index e1d29efd3..5b172df4a 100644
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ b/src/core/file_sys/archive_extsavedata.cpp
@@ -11,6 +11,9 @@
#include "common/string_util.h"
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/disk_archive.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/path_parser.h"
+#include "core/file_sys/savedata_archive.h"
#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -18,6 +21,116 @@
namespace FileSys {
+/**
+ * A modified version of DiskFile for fixed-size file used by ExtSaveData
+ * The file size can't be changed by SetSize or Write.
+ */
+class FixSizeDiskFile : public DiskFile {
+public:
+ FixSizeDiskFile(FileUtil::IOFile&& file, const Mode& mode) : DiskFile(std::move(file), mode) {
+ size = GetSize();
+ }
+
+ bool SetSize(u64 size) const override {
+ return false;
+ }
+
+ ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
+ const u8* buffer) const override {
+ if (offset > size) {
+ return ResultCode(ErrorDescription::FS_WriteBeyondEnd, ErrorModule::FS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ } else if (offset == size) {
+ return MakeResult<size_t>(0);
+ }
+
+ if (offset + length > size) {
+ length = size - offset;
+ }
+
+ return DiskFile::Write(offset, length, flush, buffer);
+ }
+
+private:
+ u64 size{};
+};
+
+/**
+ * Archive backend for general extsave data archive type.
+ * The behaviour of ExtSaveDataArchive is almost the same as SaveDataArchive, except for
+ * - file size can't be changed once created (thus creating zero-size file and openning with create
+ * flag are prohibited);
+ * - always open a file with read+write permission.
+ */
+class ExtSaveDataArchive : public SaveDataArchive {
+public:
+ explicit ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {}
+
+ std::string GetName() const override {
+ return "ExtSaveDataArchive: " + mount_point;
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
+ const Mode& mode) const override {
+ LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
+
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ if (mode.hex == 0) {
+ LOG_ERROR(Service_FS, "Empty open mode");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ if (mode.create_flag) {
+ LOG_ERROR(Service_FS, "Create flag is not supported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ case PathParser::PathNotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ FileUtil::IOFile file(full_path, "r+b");
+ if (!file.IsOpen()) {
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ Mode rwmode;
+ rwmode.write_flag.Assign(1);
+ rwmode.read_flag.Assign(1);
+ auto disk_file = std::make_unique<FixSizeDiskFile>(std::move(file), rwmode);
+ return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
+ }
+
+ ResultCode CreateFile(const Path& path, u64 size) const override {
+ if (size == 0) {
+ LOG_ERROR(Service_FS, "Zero-size file is not supported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+ return SaveDataArchive::CreateFile(path, size);
+ }
+};
+
std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) {
std::vector<u8> vec_data = path.AsBinary();
const u32* data = reinterpret_cast<const u32*>(vec_data.data());
@@ -84,7 +197,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons
ErrorSummary::InvalidState, ErrorLevel::Status);
}
}
- auto archive = std::make_unique<DiskArchive>(fullpath);
+ auto archive = std::make_unique<ExtSaveDataArchive>(fullpath);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_ncch.cpp
index 6c4542b7d..6f1aadfc3 100644
--- a/src/core/file_sys/archive_savedatacheck.cpp
+++ b/src/core/file_sys/archive_ncch.cpp
@@ -9,7 +9,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/file_sys/archive_savedatacheck.h"
+#include "core/file_sys/archive_ncch.h"
#include "core/file_sys/ivfc_archive.h"
#include "core/hle/service/fs/archive.h"
@@ -18,22 +18,22 @@
namespace FileSys {
-static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) {
+static std::string GetNCCHContainerPath(const std::string& nand_directory) {
return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str());
}
-static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) {
+static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) {
return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", mount_point.c_str(),
high, low);
}
-ArchiveFactory_SaveDataCheck::ArchiveFactory_SaveDataCheck(const std::string& nand_directory)
- : mount_point(GetSaveDataCheckContainerPath(nand_directory)) {}
+ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory)
+ : mount_point(GetNCCHContainerPath(nand_directory)) {}
-ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(const Path& path) {
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) {
auto vec = path.AsBinary();
const u32* data = reinterpret_cast<u32*>(vec.data());
- std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]);
+ std::string file_path = GetNCCHPath(mount_point, data[1], data[0]);
auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb");
if (!file->IsOpen()) {
@@ -45,15 +45,15 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
-ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path,
- const FileSys::ArchiveFormatInfo& format_info) {
- LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive.");
+ResultCode ArchiveFactory_NCCH::Format(const Path& path,
+ const FileSys::ArchiveFormatInfo& format_info) {
+ LOG_ERROR(Service_FS, "Attempted to format a NCCH archive.");
// TODO: Verify error code
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
ErrorLevel::Permanent);
}
-ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const {
+ResultVal<ArchiveFormatInfo> ArchiveFactory_NCCH::GetFormatInfo(const Path& path) const {
// TODO(Subv): Implement
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
return ResultCode(-1);
diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_ncch.h
index e9cafbed9..753b91f96 100644
--- a/src/core/file_sys/archive_savedatacheck.h
+++ b/src/core/file_sys/archive_ncch.h
@@ -14,13 +14,13 @@
namespace FileSys {
-/// File system interface to the SaveDataCheck archive
-class ArchiveFactory_SaveDataCheck final : public ArchiveFactory {
+/// File system interface to the NCCH archive
+class ArchiveFactory_NCCH final : public ArchiveFactory {
public:
- ArchiveFactory_SaveDataCheck(const std::string& mount_point);
+ explicit ArchiveFactory_NCCH(const std::string& mount_point);
std::string GetName() const override {
- return "SaveDataCheck";
+ return "NCCH";
}
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
diff --git a/src/core/file_sys/archive_other_savedata.cpp b/src/core/file_sys/archive_other_savedata.cpp
new file mode 100644
index 000000000..d3cf080da
--- /dev/null
+++ b/src/core/file_sys/archive_other_savedata.cpp
@@ -0,0 +1,145 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <tuple>
+#include "core/file_sys/archive_other_savedata.h"
+#include "core/file_sys/errors.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/fs/archive.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+// TODO(wwylele): The storage info in exheader should be checked before accessing these archives
+
+using Service::FS::MediaType;
+
+namespace {
+
+template <typename T>
+ResultVal<std::tuple<MediaType, u64>> ParsePath(const Path& path, T program_id_reader) {
+ if (path.GetType() != Binary) {
+ LOG_ERROR(Service_FS, "Wrong path type %d", static_cast<int>(path.GetType()));
+ return ERROR_INVALID_PATH;
+ }
+
+ std::vector<u8> vec_data = path.AsBinary();
+
+ if (vec_data.size() != 12) {
+ LOG_ERROR(Service_FS, "Wrong path length %zu", vec_data.size());
+ return ERROR_INVALID_PATH;
+ }
+
+ const u32* data = reinterpret_cast<const u32*>(vec_data.data());
+ auto media_type = static_cast<MediaType>(data[0]);
+
+ if (media_type != MediaType::SDMC && media_type != MediaType::GameCard) {
+ LOG_ERROR(Service_FS, "Unsupported media type %u", static_cast<u32>(media_type));
+
+ // Note: this is strange, but the error code was verified with a real 3DS
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ return MakeResult<std::tuple<MediaType, u64>>(media_type, program_id_reader(data));
+}
+
+ResultVal<std::tuple<MediaType, u64>> ParsePathPermitted(const Path& path) {
+ return ParsePath(path,
+ [](const u32* data) -> u64 { return (data[1] << 8) | 0x0004000000000000ULL; });
+}
+
+ResultVal<std::tuple<MediaType, u64>> ParsePathGeneral(const Path& path) {
+ return ParsePath(
+ path, [](const u32* data) -> u64 { return data[1] | (static_cast<u64>(data[2]) << 32); });
+}
+
+} // namespace
+
+ArchiveFactory_OtherSaveDataPermitted::ArchiveFactory_OtherSaveDataPermitted(
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
+ : sd_savedata_source(sd_savedata) {}
+
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataPermitted::Open(
+ const Path& path) {
+ MediaType media_type;
+ u64 program_id;
+ CASCADE_RESULT(std::tie(media_type, program_id), ParsePathPermitted(path));
+
+ if (media_type == MediaType::GameCard) {
+ LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
+ return ERROR_GAMECARD_NOT_INSERTED;
+ }
+
+ return sd_savedata_source->Open(program_id);
+}
+
+ResultCode ArchiveFactory_OtherSaveDataPermitted::Format(
+ const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
+ LOG_ERROR(Service_FS, "Attempted to format a OtherSaveDataPermitted archive.");
+ return ERROR_INVALID_PATH;
+}
+
+ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataPermitted::GetFormatInfo(
+ const Path& path) const {
+ MediaType media_type;
+ u64 program_id;
+ CASCADE_RESULT(std::tie(media_type, program_id), ParsePathPermitted(path));
+
+ if (media_type == MediaType::GameCard) {
+ LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
+ return ERROR_GAMECARD_NOT_INSERTED;
+ }
+
+ return sd_savedata_source->GetFormatInfo(program_id);
+}
+
+ArchiveFactory_OtherSaveDataGeneral::ArchiveFactory_OtherSaveDataGeneral(
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
+ : sd_savedata_source(sd_savedata) {}
+
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataGeneral::Open(
+ const Path& path) {
+ MediaType media_type;
+ u64 program_id;
+ CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
+
+ if (media_type == MediaType::GameCard) {
+ LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
+ return ERROR_GAMECARD_NOT_INSERTED;
+ }
+
+ return sd_savedata_source->Open(program_id);
+}
+
+ResultCode ArchiveFactory_OtherSaveDataGeneral::Format(
+ const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
+ MediaType media_type;
+ u64 program_id;
+ CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
+
+ if (media_type == MediaType::GameCard) {
+ LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
+ return ERROR_GAMECARD_NOT_INSERTED;
+ }
+
+ return sd_savedata_source->Format(program_id, format_info);
+}
+
+ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataGeneral::GetFormatInfo(
+ const Path& path) const {
+ MediaType media_type;
+ u64 program_id;
+ CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
+
+ if (media_type == MediaType::GameCard) {
+ LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
+ return ERROR_GAMECARD_NOT_INSERTED;
+ }
+
+ return sd_savedata_source->GetFormatInfo(program_id);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_other_savedata.h b/src/core/file_sys/archive_other_savedata.h
new file mode 100644
index 000000000..d80725158
--- /dev/null
+++ b/src/core/file_sys/archive_other_savedata.h
@@ -0,0 +1,52 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/file_sys/archive_source_sd_savedata.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// File system interface to the OtherSaveDataPermitted archive
+class ArchiveFactory_OtherSaveDataPermitted final : public ArchiveFactory {
+public:
+ explicit ArchiveFactory_OtherSaveDataPermitted(
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
+
+ std::string GetName() const override {
+ return "OtherSaveDataPermitted";
+ }
+
+ ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::string mount_point;
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
+};
+
+/// File system interface to the OtherSaveDataGeneral archive
+class ArchiveFactory_OtherSaveDataGeneral final : public ArchiveFactory {
+public:
+ explicit ArchiveFactory_OtherSaveDataGeneral(
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
+
+ std::string GetName() const override {
+ return "OtherSaveDataGeneral";
+ }
+
+ ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::string mount_point;
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
index 8a8082a05..1eaf99b54 100644
--- a/src/core/file_sys/archive_romfs.h
+++ b/src/core/file_sys/archive_romfs.h
@@ -20,7 +20,7 @@ namespace FileSys {
/// File system interface to the RomFS archive
class ArchiveFactory_RomFS final : public ArchiveFactory {
public:
- ArchiveFactory_RomFS(Loader::AppLoader& app_loader);
+ explicit ArchiveFactory_RomFS(Loader::AppLoader& app_loader);
std::string GetName() const override {
return "RomFS";
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
index 6711035ec..61f7654f7 100644
--- a/src/core/file_sys/archive_savedata.cpp
+++ b/src/core/file_sys/archive_savedata.cpp
@@ -2,96 +2,29 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
-#include <memory>
-#include "common/common_types.h"
-#include "common/file_util.h"
-#include "common/logging/log.h"
-#include "common/string_util.h"
#include "core/file_sys/archive_savedata.h"
-#include "core/file_sys/disk_archive.h"
#include "core/hle/kernel/process.h"
-#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
-static std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
- return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(),
- SYSTEM_ID.c_str(), SDCARD_ID.c_str());
-}
-
-static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
- u32 high = (u32)(program_id >> 32);
- u32 low = (u32)(program_id & 0xFFFFFFFF);
- return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high,
- low);
-}
-
-static std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) {
- u32 high = (u32)(program_id >> 32);
- u32 low = (u32)(program_id & 0xFFFFFFFF);
- return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(),
- high, low);
-}
-
-ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory)
- : mount_point(GetSaveDataContainerPath(sdmc_directory)) {
- LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
-}
+ArchiveFactory_SaveData::ArchiveFactory_SaveData(
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
+ : sd_savedata_source(sd_savedata) {}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) {
- std::string concrete_mount_point =
- GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id);
- if (!FileUtil::Exists(concrete_mount_point)) {
- // When a SaveData archive is created for the first time, it is not yet formatted and the
- // save file/directory structure expected by the game has not yet been initialized.
- // Returning the NotFormatted error code will signal the game to provision the SaveData
- // archive with the files and folders that it expects.
- return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
- ErrorSummary::InvalidState, ErrorLevel::Status);
- }
-
- auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point));
- return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
+ return sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id);
}
ResultCode ArchiveFactory_SaveData::Format(const Path& path,
const FileSys::ArchiveFormatInfo& format_info) {
- std::string concrete_mount_point =
- GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id);
- FileUtil::DeleteDirRecursively(concrete_mount_point);
- FileUtil::CreateFullPath(concrete_mount_point);
-
- // Write the format metadata
- std::string metadata_path =
- GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
- FileUtil::IOFile file(metadata_path, "wb");
-
- if (file.IsOpen()) {
- file.WriteBytes(&format_info, sizeof(format_info));
- return RESULT_SUCCESS;
- }
- return RESULT_SUCCESS;
+ return sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info);
}
ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
- std::string metadata_path =
- GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
- FileUtil::IOFile file(metadata_path, "rb");
-
- if (!file.IsOpen()) {
- LOG_ERROR(Service_FS, "Could not open metadata information for archive");
- // TODO(Subv): Verify error code
- return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
- ErrorSummary::InvalidState, ErrorLevel::Status);
- }
-
- ArchiveFormatInfo info = {};
- file.ReadBytes(&info, sizeof(info));
- return MakeResult<ArchiveFormatInfo>(info);
+ return sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id);
}
} // namespace FileSys
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h
index 6a372865a..41aa6f189 100644
--- a/src/core/file_sys/archive_savedata.h
+++ b/src/core/file_sys/archive_savedata.h
@@ -4,10 +4,7 @@
#pragma once
-#include <memory>
-#include <string>
-#include "core/file_sys/archive_backend.h"
-#include "core/hle/result.h"
+#include "core/file_sys/archive_source_sd_savedata.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
@@ -17,7 +14,7 @@ namespace FileSys {
/// File system interface to the SaveData archive
class ArchiveFactory_SaveData final : public ArchiveFactory {
public:
- ArchiveFactory_SaveData(const std::string& mount_point);
+ explicit ArchiveFactory_SaveData(std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
std::string GetName() const override {
return "SaveData";
@@ -30,6 +27,7 @@ public:
private:
std::string mount_point;
+ std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
};
} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
index bcb03ed36..333dfb92e 100644
--- a/src/core/file_sys/archive_sdmc.cpp
+++ b/src/core/file_sys/archive_sdmc.cpp
@@ -8,6 +8,8 @@
#include "common/logging/log.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/file_sys/disk_archive.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/path_parser.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -15,6 +17,281 @@
namespace FileSys {
+ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
+ const Mode& mode) const {
+ Mode modified_mode;
+ modified_mode.hex = mode.hex;
+
+ // SDMC archive always opens a file with at least read permission
+ modified_mode.read_flag.Assign(1);
+
+ return OpenFileBase(path, modified_mode);
+}
+
+ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& path,
+ const Mode& mode) const {
+ LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
+
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ if (mode.hex == 0) {
+ LOG_ERROR(Service_FS, "Empty open mode");
+ return ERROR_INVALID_OPEN_FLAGS;
+ }
+
+ if (mode.create_flag && !mode.write_flag) {
+ LOG_ERROR(Service_FS, "Create flag set but write flag not set");
+ return ERROR_INVALID_OPEN_FLAGS;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ case PathParser::NotFound:
+ if (!mode.create_flag) {
+ LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
+ full_path.c_str());
+ return ERROR_NOT_FOUND;
+ } else {
+ // Create the file
+ FileUtil::CreateEmptyFile(full_path);
+ }
+ break;
+ }
+
+ FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
+ if (!file.IsOpen()) {
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ }
+
+ auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
+ return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
+}
+
+ResultCode SDMCArchive::DeleteFile(const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ }
+
+ if (FileUtil::Delete(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+}
+
+ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
+ if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) {
+ return RESULT_SUCCESS;
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+template <typename T>
+static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
+ T deleter) {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ if (path_parser.IsRootDirectory())
+ return ERROR_NOT_FOUND;
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ }
+
+ if (deleter(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+}
+
+ResultCode SDMCArchive::DeleteDirectory(const Path& path) const {
+ return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
+}
+
+ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
+ return DeleteDirectoryHelper(
+ path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
+}
+
+ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_ALREADY_EXISTS;
+ }
+
+ if (size == 0) {
+ FileUtil::CreateEmptyFile(full_path);
+ return RESULT_SUCCESS;
+ }
+
+ FileUtil::IOFile file(full_path, "wb");
+ // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
+ // We do this by seeking to the right size, then writing a single null byte.
+ if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Too large file");
+ return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
+ ErrorLevel::Info);
+}
+
+ResultCode SDMCArchive::CreateDirectory(const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::DirectoryFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_ALREADY_EXISTS;
+ }
+
+ if (FileUtil::CreateDir(mount_point + path.AsString())) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
+ ErrorLevel::Status);
+}
+
+ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()))
+ return RESULT_SUCCESS;
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::NotFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
+ return ERROR_NOT_FOUND;
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
+ }
+
+ auto directory = std::make_unique<DiskDirectory>(full_path);
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
+}
+
+u64 SDMCArchive::GetFreeBytes() const {
+ // TODO: Stubbed to return 1GiB
+ return 1024 * 1024 * 1024;
+}
+
ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
: sdmc_directory(sdmc_directory) {
LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
@@ -35,7 +312,7 @@ bool ArchiveFactory_SDMC::Initialize() {
}
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
- auto archive = std::make_unique<DiskArchive>(sdmc_directory);
+ auto archive = std::make_unique<SDMCArchive>(sdmc_directory);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
index 88e855351..f6c70bfcc 100644
--- a/src/core/file_sys/archive_sdmc.h
+++ b/src/core/file_sys/archive_sdmc.h
@@ -14,10 +14,36 @@
namespace FileSys {
+/// Archive backend for SDMC archive
+class SDMCArchive : public ArchiveBackend {
+public:
+ explicit SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
+
+ std::string GetName() const override {
+ return "SDMCArchive: " + mount_point;
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
+ const Mode& mode) const override;
+ ResultCode DeleteFile(const Path& path) const override;
+ ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
+ ResultCode DeleteDirectory(const Path& path) const override;
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override;
+ ResultCode CreateFile(const Path& path, u64 size) const override;
+ ResultCode CreateDirectory(const Path& path) const override;
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
+ u64 GetFreeBytes() const override;
+
+protected:
+ ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const;
+ std::string mount_point;
+};
+
/// File system interface to the SDMC archive
class ArchiveFactory_SDMC final : public ArchiveFactory {
public:
- ArchiveFactory_SDMC(const std::string& mount_point);
+ explicit ArchiveFactory_SDMC(const std::string& mount_point);
/**
* Initialize the archive.
diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp
new file mode 100644
index 000000000..2aafc9b1d
--- /dev/null
+++ b/src/core/file_sys/archive_sdmcwriteonly.cpp
@@ -0,0 +1,70 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "common/file_util.h"
+#include "core/file_sys/archive_sdmcwriteonly.h"
+#include "core/file_sys/directory_backend.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/file_backend.h"
+#include "core/settings.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
+ const Mode& mode) const {
+ if (mode.read_flag) {
+ LOG_ERROR(Service_FS, "Read flag is not supported");
+ return ERROR_INVALID_READ_FLAG;
+ }
+ return SDMCArchive::OpenFileBase(path, mode);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory(
+ const Path& path) const {
+ LOG_ERROR(Service_FS, "Not supported");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+}
+
+ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point)
+ : sdmc_directory(mount_point) {
+ LOG_INFO(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
+}
+
+bool ArchiveFactory_SDMCWriteOnly::Initialize() {
+ if (!Settings::values.use_virtual_sd) {
+ LOG_WARNING(Service_FS, "SDMC disabled by config.");
+ return false;
+ }
+
+ if (!FileUtil::CreateFullPath(sdmc_directory)) {
+ LOG_ERROR(Service_FS, "Unable to create SDMC path.");
+ return false;
+ }
+
+ return true;
+}
+
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(const Path& path) {
+ auto archive = std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory);
+ return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
+}
+
+ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path,
+ const FileSys::ArchiveFormatInfo& format_info) {
+ // TODO(wwylele): hwtest this
+ LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive.");
+ return ResultCode(-1);
+}
+
+ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const Path& path) const {
+ // TODO(Subv): Implement
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+ return ResultCode(-1);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h
new file mode 100644
index 000000000..9cd38d96f
--- /dev/null
+++ b/src/core/file_sys/archive_sdmcwriteonly.h
@@ -0,0 +1,57 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/file_sys/archive_sdmc.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/**
+ * Archive backend for SDMC write-only archive.
+ * The behaviour of SDMCWriteOnlyArchive is almost the same as SDMCArchive, except for
+ * - OpenDirectory is unsupported;
+ * - OpenFile with read flag is unsupported.
+ */
+class SDMCWriteOnlyArchive : public SDMCArchive {
+public:
+ explicit SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {}
+
+ std::string GetName() const override {
+ return "SDMCWriteOnlyArchive: " + mount_point;
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
+ const Mode& mode) const override;
+
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
+};
+
+/// File system interface to the SDMC write-only archive
+class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory {
+public:
+ explicit ArchiveFactory_SDMCWriteOnly(const std::string& mount_point);
+
+ /**
+ * Initialize the archive.
+ * @return true if it initialized successfully
+ */
+ bool Initialize();
+
+ std::string GetName() const override {
+ return "SDMCWriteOnly";
+ }
+
+ ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::string sdmc_directory;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp
new file mode 100644
index 000000000..2d8a950a3
--- /dev/null
+++ b/src/core/file_sys/archive_source_sd_savedata.cpp
@@ -0,0 +1,93 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/file_util.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/file_sys/archive_source_sd_savedata.h"
+#include "core/file_sys/savedata_archive.h"
+#include "core/hle/service/fs/archive.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+namespace {
+
+std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
+ return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(),
+ SYSTEM_ID.c_str(), SDCARD_ID.c_str());
+}
+
+std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
+ u32 high = static_cast<u32>(program_id >> 32);
+ u32 low = static_cast<u32>(program_id & 0xFFFFFFFF);
+ return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high,
+ low);
+}
+
+std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) {
+ u32 high = static_cast<u32>(program_id >> 32);
+ u32 low = static_cast<u32>(program_id & 0xFFFFFFFF);
+ return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(),
+ high, low);
+}
+
+} // namespace
+
+ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_directory)
+ : mount_point(GetSaveDataContainerPath(sdmc_directory)) {
+ LOG_INFO(Service_FS, "Directory %s set as SaveData.", mount_point.c_str());
+}
+
+ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 program_id) {
+ std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
+ if (!FileUtil::Exists(concrete_mount_point)) {
+ // When a SaveData archive is created for the first time, it is not yet formatted and the
+ // save file/directory structure expected by the game has not yet been initialized.
+ // Returning the NotFormatted error code will signal the game to provision the SaveData
+ // archive with the files and folders that it expects.
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
+
+ auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
+ return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
+}
+
+ResultCode ArchiveSource_SDSaveData::Format(u64 program_id,
+ const FileSys::ArchiveFormatInfo& format_info) {
+ std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
+ FileUtil::DeleteDirRecursively(concrete_mount_point);
+ FileUtil::CreateFullPath(concrete_mount_point);
+
+ // Write the format metadata
+ std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
+ FileUtil::IOFile file(metadata_path, "wb");
+
+ if (file.IsOpen()) {
+ file.WriteBytes(&format_info, sizeof(format_info));
+ return RESULT_SUCCESS;
+ }
+ return RESULT_SUCCESS;
+}
+
+ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program_id) const {
+ std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
+ FileUtil::IOFile file(metadata_path, "rb");
+
+ if (!file.IsOpen()) {
+ LOG_ERROR(Service_FS, "Could not open metadata information for archive");
+ // TODO(Subv): Verify error code
+ return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
+ ErrorSummary::InvalidState, ErrorLevel::Status);
+ }
+
+ ArchiveFormatInfo info = {};
+ file.ReadBytes(&info, sizeof(info));
+ return MakeResult<ArchiveFormatInfo>(info);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_source_sd_savedata.h b/src/core/file_sys/archive_source_sd_savedata.h
new file mode 100644
index 000000000..b33126c31
--- /dev/null
+++ b/src/core/file_sys/archive_source_sd_savedata.h
@@ -0,0 +1,30 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include "core/file_sys/archive_backend.h"
+#include "core/hle/result.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// A common source of SD save data archive
+class ArchiveSource_SDSaveData {
+public:
+ explicit ArchiveSource_SDSaveData(const std::string& mount_point);
+
+ ResultVal<std::unique_ptr<ArchiveBackend>> Open(u64 program_id);
+ ResultCode Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info);
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id) const;
+
+private:
+ std::string mount_point;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
index 48ebc0ed4..54e7793e0 100644
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ b/src/core/file_sys/archive_systemsavedata.cpp
@@ -9,7 +9,7 @@
#include "common/file_util.h"
#include "common/string_util.h"
#include "core/file_sys/archive_systemsavedata.h"
-#include "core/file_sys/disk_archive.h"
+#include "core/file_sys/savedata_archive.h"
#include "core/hle/service/fs/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -56,7 +56,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
ErrorSummary::InvalidState, ErrorLevel::Status);
}
- auto archive = std::make_unique<DiskArchive>(fullpath);
+ auto archive = std::make_unique<SaveDataArchive>(fullpath);
return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
}
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h
index a24b89f2b..52eb6c630 100644
--- a/src/core/file_sys/archive_systemsavedata.h
+++ b/src/core/file_sys/archive_systemsavedata.h
@@ -18,7 +18,7 @@ namespace FileSys {
/// File system interface to the SystemSaveData archive
class ArchiveFactory_SystemSaveData final : public ArchiveFactory {
public:
- ArchiveFactory_SystemSaveData(const std::string& mount_point);
+ explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);
ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory_backend.h
index b55e382ef..0c93f2074 100644
--- a/src/core/file_sys/directory_backend.h
+++ b/src/core/file_sys/directory_backend.h
@@ -41,12 +41,6 @@ public:
virtual ~DirectoryBackend() {}
/**
- * Open the directory
- * @return true if the directory opened correctly
- */
- virtual bool Open() = 0;
-
- /**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index 2f05af361..a243d9a13 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -15,144 +15,8 @@
namespace FileSys {
-ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path,
- const Mode mode) const {
- LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
- auto file = std::make_unique<DiskFile>(*this, path, mode);
- ResultCode result = file->Open();
- if (result.IsError())
- return result;
- return MakeResult<std::unique_ptr<FileBackend>>(std::move(file));
-}
-
-ResultCode DiskArchive::DeleteFile(const Path& path) const {
- std::string file_path = mount_point + path.AsString();
-
- if (FileUtil::IsDirectory(file_path))
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-
- if (!FileUtil::Exists(file_path))
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
- ErrorLevel::Status);
-
- if (FileUtil::Delete(file_path))
- return RESULT_SUCCESS;
-
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-}
-
-bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
- return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString());
-}
-
-bool DiskArchive::DeleteDirectory(const Path& path) const {
- return FileUtil::DeleteDir(mount_point + path.AsString());
-}
-
-bool DiskArchive::DeleteDirectoryRecursively(const Path& path) const {
- return FileUtil::DeleteDirRecursively(mount_point + path.AsString());
-}
-
-ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const {
- std::string full_path = mount_point + path.AsString();
-
- if (FileUtil::IsDirectory(full_path))
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-
- if (FileUtil::Exists(full_path))
- return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS,
- ErrorSummary::NothingHappened, ErrorLevel::Status);
-
- if (size == 0) {
- FileUtil::CreateEmptyFile(full_path);
- return RESULT_SUCCESS;
- }
-
- FileUtil::IOFile file(full_path, "wb");
- // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
- // We do this by seeking to the right size, then writing a single null byte.
- if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1)
- return RESULT_SUCCESS;
-
- return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
- ErrorLevel::Info);
-}
-
-bool DiskArchive::CreateDirectory(const Path& path) const {
- return FileUtil::CreateDir(mount_point + path.AsString());
-}
-
-bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
- return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString());
-}
-
-std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
- LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
- auto directory = std::make_unique<DiskDirectory>(*this, path);
- if (!directory->Open())
- return nullptr;
- return std::move(directory);
-}
-
-u64 DiskArchive::GetFreeBytes() const {
- // TODO: Stubbed to return 1GiB
- return 1024 * 1024 * 1024;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../etc/passwd can give the emulated program your users list.
- this->path = archive.mount_point + path.AsString();
- this->mode.hex = mode.hex;
-}
-
-ResultCode DiskFile::Open() {
- if (FileUtil::IsDirectory(path))
- return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled,
- ErrorLevel::Status);
-
- // Specifying only the Create flag is invalid
- if (mode.create_flag && !mode.read_flag && !mode.write_flag) {
- return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
- ErrorSummary::Canceled, ErrorLevel::Status);
- }
-
- if (!FileUtil::Exists(path)) {
- if (!mode.create_flag) {
- LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
- path.c_str());
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
- ErrorSummary::NotFound, ErrorLevel::Status);
- } else {
- // Create the file
- FileUtil::CreateEmptyFile(path);
- }
- }
-
- std::string mode_string = "";
- if (mode.write_flag)
- mode_string += "r+"; // Files opened with Write access can be read from
- else if (mode.read_flag)
- mode_string += "r";
-
- // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
- mode_string += "b";
-
- file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
- if (file->IsOpen())
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
- ErrorLevel::Status);
-}
-
ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
- if (!mode.read_flag && !mode.write_flag)
+ if (!mode.read_flag)
return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
ErrorSummary::Canceled, ErrorLevel::Status);
@@ -189,21 +53,11 @@ bool DiskFile::Close() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
-DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() {
- // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
- // the root directory we set while opening the archive.
- // For example, opening /../../usr/bin can give the emulated program your installed programs.
- this->path = archive.mount_point + path.AsString();
-}
-
-bool DiskDirectory::Open() {
- if (!FileUtil::IsDirectory(path))
- return false;
+DiskDirectory::DiskDirectory(const std::string& path) : directory() {
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
directory.size = size;
directory.isDirectory = true;
children_iterator = directory.children.begin();
- return true;
}
u32 DiskDirectory::Read(const u32 count, Entry* entries) {
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index 59ebb2002..eb9166df6 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -20,43 +20,13 @@
namespace FileSys {
-/**
- * Helper which implements a backend accessing the host machine's filesystem.
- * This should be subclassed by concrete archive types, which will provide the
- * base directory on the host filesystem and override any required functionality.
- */
-class DiskArchive : public ArchiveBackend {
-public:
- DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
-
- virtual std::string GetName() const override {
- return "DiskArchive: " + mount_point;
- }
-
- ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
- const Mode mode) const override;
- ResultCode DeleteFile(const Path& path) const override;
- bool RenameFile(const Path& src_path, const Path& dest_path) const override;
- bool DeleteDirectory(const Path& path) const override;
- bool DeleteDirectoryRecursively(const Path& path) const override;
- ResultCode CreateFile(const Path& path, u64 size) const override;
- bool CreateDirectory(const Path& path) const override;
- bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
- std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
- u64 GetFreeBytes() const override;
-
-protected:
- friend class DiskFile;
- friend class DiskDirectory;
-
- std::string mount_point;
-};
-
class DiskFile : public FileBackend {
public:
- DiskFile(const DiskArchive& archive, const Path& path, const Mode mode);
+ DiskFile(FileUtil::IOFile&& file_, const Mode& mode_)
+ : file(new FileUtil::IOFile(std::move(file_))) {
+ mode.hex = mode_.hex;
+ }
- ResultCode Open() override;
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
@@ -68,20 +38,18 @@ public:
}
protected:
- std::string path;
Mode mode;
std::unique_ptr<FileUtil::IOFile> file;
};
class DiskDirectory : public DirectoryBackend {
public:
- DiskDirectory(const DiskArchive& archive, const Path& path);
+ DiskDirectory(const std::string& path);
~DiskDirectory() override {
Close();
}
- bool Open() override;
u32 Read(const u32 count, Entry* entries) override;
bool Close() const override {
@@ -89,7 +57,6 @@ public:
}
protected:
- std::string path;
u32 total_entries_in_directory;
FileUtil::FSTEntry directory;
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
new file mode 100644
index 000000000..4d5f62b08
--- /dev/null
+++ b/src/core/file_sys/errors.h
@@ -0,0 +1,43 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+const ResultCode ERROR_INVALID_PATH(ErrorDescription::FS_InvalidPath, ErrorModule::FS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+const ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrorDescription::FS_UnsupportedOpenFlags,
+ ErrorModule::FS, ErrorSummary::NotSupported,
+ ErrorLevel::Usage);
+const ResultCode ERROR_INVALID_OPEN_FLAGS(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS,
+ ErrorSummary::Canceled, ErrorLevel::Status);
+const ResultCode ERROR_INVALID_READ_FLAG(ErrorDescription::FS_InvalidReadFlag, ErrorModule::FS,
+ ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+const ResultCode ERROR_FILE_NOT_FOUND(ErrorDescription::FS_FileNotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+const ResultCode ERROR_PATH_NOT_FOUND(ErrorDescription::FS_PathNotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+const ResultCode ERROR_NOT_FOUND(ErrorDescription::FS_NotFound, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status);
+const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrorDescription::FS_UnexpectedFileOrDirectory,
+ ErrorModule::FS, ErrorSummary::NotSupported,
+ ErrorLevel::Usage);
+const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrorDescription::FS_NotAFile,
+ ErrorModule::FS, ErrorSummary::Canceled,
+ ErrorLevel::Status);
+const ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrorDescription::FS_DirectoryAlreadyExists,
+ ErrorModule::FS, ErrorSummary::NothingHappened,
+ ErrorLevel::Status);
+const ResultCode ERROR_FILE_ALREADY_EXISTS(ErrorDescription::FS_FileAlreadyExists, ErrorModule::FS,
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+const ResultCode ERROR_ALREADY_EXISTS(ErrorDescription::FS_AlreadyExists, ErrorModule::FS,
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpty, ErrorModule::FS,
+ ErrorSummary::Canceled, ErrorLevel::Status);
+const ResultCode ERROR_GAMECARD_NOT_INSERTED(ErrorDescription::FS_GameCardNotInserted,
+ ErrorModule::FS, ErrorSummary::NotFound,
+ ErrorLevel::Status);
+
+} // namespace FileSys
diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h
index ed997537f..5e7c2bab4 100644
--- a/src/core/file_sys/file_backend.h
+++ b/src/core/file_sys/file_backend.h
@@ -19,12 +19,6 @@ public:
virtual ~FileBackend() {}
/**
- * Open the file
- * @return Result of the file operation
- */
- virtual ResultCode Open() = 0;
-
- /**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp
index af59d296d..2735d2e3c 100644
--- a/src/core/file_sys/ivfc_archive.cpp
+++ b/src/core/file_sys/ivfc_archive.cpp
@@ -18,7 +18,7 @@ std::string IVFCArchive::GetName() const {
}
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path,
- const Mode mode) const {
+ const Mode& mode) const {
return MakeResult<std::unique_ptr<FileBackend>>(
std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
}
@@ -31,22 +31,25 @@ ResultCode IVFCArchive::DeleteFile(const Path& path) const {
ErrorLevel::Status);
}
-bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
+ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-bool IVFCArchive::DeleteDirectory(const Path& path) const {
+ResultCode IVFCArchive::DeleteDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-bool IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
+ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
@@ -57,20 +60,22 @@ ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
ErrorLevel::Permanent);
}
-bool IVFCArchive::CreateDirectory(const Path& path) const {
+ResultCode IVFCArchive::CreateDirectory(const Path& path) const {
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
GetName().c_str());
- return false;
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
}
-std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const {
- return std::make_unique<IVFCDirectory>();
+ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const {
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>());
}
u64 IVFCArchive::GetFreeBytes() const {
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h
index 2fbb3a568..e6fbdfb1f 100644
--- a/src/core/file_sys/ivfc_archive.h
+++ b/src/core/file_sys/ivfc_archive.h
@@ -33,15 +33,15 @@ public:
std::string GetName() const override;
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
- const Mode mode) const override;
+ const Mode& mode) const override;
ResultCode DeleteFile(const Path& path) const override;
- bool RenameFile(const Path& src_path, const Path& dest_path) const override;
- bool DeleteDirectory(const Path& path) const override;
- bool DeleteDirectoryRecursively(const Path& path) const override;
+ ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
+ ResultCode DeleteDirectory(const Path& path) const override;
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override;
ResultCode CreateFile(const Path& path, u64 size) const override;
- bool CreateDirectory(const Path& path) const override;
- bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
- std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
+ ResultCode CreateDirectory(const Path& path) const override;
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
u64 GetFreeBytes() const override;
protected:
@@ -55,9 +55,6 @@ public:
IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
: romfs_file(file), data_offset(offset), data_size(size) {}
- ResultCode Open() override {
- return RESULT_SUCCESS;
- }
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
u64 GetSize() const override;
@@ -75,9 +72,6 @@ private:
class IVFCDirectory : public DirectoryBackend {
public:
- bool Open() override {
- return false;
- }
u32 Read(const u32 count, Entry* entries) override {
return 0;
}
diff --git a/src/core/file_sys/path_parser.cpp b/src/core/file_sys/path_parser.cpp
new file mode 100644
index 000000000..5a89b02b8
--- /dev/null
+++ b/src/core/file_sys/path_parser.cpp
@@ -0,0 +1,98 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <set>
+#include "common/file_util.h"
+#include "common/string_util.h"
+#include "core/file_sys/path_parser.h"
+
+namespace FileSys {
+
+PathParser::PathParser(const Path& path) {
+ if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) {
+ is_valid = false;
+ return;
+ }
+
+ auto path_string = path.AsString();
+ if (path_string.size() == 0 || path_string[0] != '/') {
+ is_valid = false;
+ return;
+ }
+
+ // Filter out invalid characters for the host system.
+ // Although some of these characters are valid on 3DS, they are unlikely to be used by games.
+ if (std::find_if(path_string.begin(), path_string.end(), [](char c) {
+ static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'};
+ return invalid_chars.find(c) != invalid_chars.end();
+ }) != path_string.end()) {
+ is_valid = false;
+ return;
+ }
+
+ Common::SplitString(path_string, '/', path_sequence);
+
+ auto begin = path_sequence.begin();
+ auto end = path_sequence.end();
+ end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; });
+ path_sequence = std::vector<std::string>(begin, end);
+
+ // checks if the path is out of bounds.
+ int level = 0;
+ for (auto& node : path_sequence) {
+ if (node == "..") {
+ --level;
+ if (level < 0) {
+ is_valid = false;
+ return;
+ }
+ } else {
+ ++level;
+ }
+ }
+
+ is_valid = true;
+ is_root = level == 0;
+}
+
+PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const {
+ auto path = mount_point;
+ if (!FileUtil::IsDirectory(path))
+ return InvalidMountPoint;
+ if (path_sequence.empty()) {
+ return DirectoryFound;
+ }
+
+ for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) {
+ if (path.back() != '/')
+ path += '/';
+ path += *iter;
+
+ if (!FileUtil::Exists(path))
+ return PathNotFound;
+ if (FileUtil::IsDirectory(path))
+ continue;
+ return FileInPath;
+ }
+
+ path += "/" + path_sequence.back();
+ if (!FileUtil::Exists(path))
+ return NotFound;
+ if (FileUtil::IsDirectory(path))
+ return DirectoryFound;
+ return FileFound;
+}
+
+std::string PathParser::BuildHostPath(const std::string& mount_point) const {
+ std::string path = mount_point;
+ for (auto& node : path_sequence) {
+ if (path.back() != '/')
+ path += '/';
+ path += node;
+ }
+ return path;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h
new file mode 100644
index 000000000..b9f52f65d
--- /dev/null
+++ b/src/core/file_sys/path_parser.h
@@ -0,0 +1,61 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include "core/file_sys/archive_backend.h"
+
+namespace FileSys {
+
+/**
+ * A helper class parsing and verifying a string-type Path.
+ * Every archives with a sub file system should use this class to parse the path argument and check
+ * the status of the file / directory in question on the host file system.
+ */
+class PathParser {
+public:
+ explicit PathParser(const Path& path);
+
+ /**
+ * Checks if the Path is valid.
+ * This function should be called once a PathParser is constructed.
+ * A Path is valid if:
+ * - it is a string path (with type LowPathType::Char or LowPathType::Wchar),
+ * - it starts with "/" (this seems a hard requirement in real 3DS),
+ * - it doesn't contain invalid characters, and
+ * - it doesn't go out of the root directory using "..".
+ */
+ bool IsValid() const {
+ return is_valid;
+ }
+
+ /// Checks if the Path represents the root directory.
+ bool IsRootDirectory() const {
+ return is_root;
+ }
+
+ enum HostStatus {
+ InvalidMountPoint,
+ PathNotFound, // "/a/b/c" when "a" doesn't exist
+ FileInPath, // "/a/b/c" when "a" is a file
+ FileFound, // "/a/b/c" when "c" is a file
+ DirectoryFound, // "/a/b/c" when "c" is a directory
+ NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist
+ };
+
+ /// Checks the status of the specified file / directory by the Path on the host file system.
+ HostStatus GetHostStatus(const std::string& mount_point) const;
+
+ /// Builds a full path on the host file system.
+ std::string BuildHostPath(const std::string& mount_point) const;
+
+private:
+ std::vector<std::string> path_sequence;
+ bool is_valid{};
+ bool is_root{};
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp
new file mode 100644
index 000000000..f2e6a06bc
--- /dev/null
+++ b/src/core/file_sys/savedata_archive.cpp
@@ -0,0 +1,283 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/file_util.h"
+#include "core/file_sys/disk_archive.h"
+#include "core/file_sys/errors.h"
+#include "core/file_sys/path_parser.h"
+#include "core/file_sys/savedata_archive.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
+ const Mode& mode) const {
+ LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
+
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ if (mode.hex == 0) {
+ LOG_ERROR(Service_FS, "Empty open mode");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ if (mode.create_flag && !mode.write_flag) {
+ LOG_ERROR(Service_FS, "Create flag set but write flag not set");
+ return ERROR_UNSUPPORTED_OPEN_FLAGS;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ case PathParser::PathNotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::DirectoryFound:
+ LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ case PathParser::NotFound:
+ if (!mode.create_flag) {
+ LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
+ full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ } else {
+ // Create the file
+ FileUtil::CreateEmptyFile(full_path);
+ }
+ break;
+ }
+
+ FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
+ if (!file.IsOpen()) {
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
+ return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
+}
+
+ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ case PathParser::PathNotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::DirectoryFound:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "File not found %s", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ if (FileUtil::Delete(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
+ return ERROR_FILE_NOT_FOUND;
+}
+
+ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
+ if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) {
+ return RESULT_SUCCESS;
+ }
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+template <typename T>
+static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
+ T deleter) {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ if (path_parser.IsRootDirectory())
+ return ERROR_DIRECTORY_NOT_EMPTY;
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ }
+
+ if (deleter(full_path)) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
+ return ERROR_DIRECTORY_NOT_EMPTY;
+}
+
+ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
+ return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
+}
+
+ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
+ return DeleteDirectoryHelper(
+ path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
+}
+
+ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ case PathParser::PathNotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ case PathParser::DirectoryFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_FILE_ALREADY_EXISTS;
+ }
+
+ if (size == 0) {
+ FileUtil::CreateEmptyFile(full_path);
+ return RESULT_SUCCESS;
+ }
+
+ FileUtil::IOFile file(full_path, "wb");
+ // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
+ // We do this by seeking to the right size, then writing a single null byte.
+ if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Too large file");
+ return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
+ ErrorLevel::Info);
+}
+
+ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ case PathParser::PathNotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ case PathParser::DirectoryFound:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
+ return ERROR_DIRECTORY_ALREADY_EXISTS;
+ }
+
+ if (FileUtil::CreateDir(mount_point + path.AsString())) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
+ ErrorLevel::Status);
+}
+
+ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()))
+ return RESULT_SUCCESS;
+
+ // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
+ // exist or similar. Verify.
+ return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
+ ErrorSummary::NothingHappened, ErrorLevel::Status);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(
+ const Path& path) const {
+ const PathParser path_parser(path);
+
+ if (!path_parser.IsValid()) {
+ LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
+ return ERROR_INVALID_PATH;
+ }
+
+ const auto full_path = path_parser.BuildHostPath(mount_point);
+
+ switch (path_parser.GetHostStatus(mount_point)) {
+ case PathParser::InvalidMountPoint:
+ LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
+ return ERROR_FILE_NOT_FOUND;
+ case PathParser::PathNotFound:
+ case PathParser::NotFound:
+ LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
+ return ERROR_PATH_NOT_FOUND;
+ case PathParser::FileInPath:
+ case PathParser::FileFound:
+ LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
+ return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
+ }
+
+ auto directory = std::make_unique<DiskDirectory>(full_path);
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
+}
+
+u64 SaveDataArchive::GetFreeBytes() const {
+ // TODO: Stubbed to return 1GiB
+ return 1024 * 1024 * 1024;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h
new file mode 100644
index 000000000..176d35710
--- /dev/null
+++ b/src/core/file_sys/savedata_archive.h
@@ -0,0 +1,43 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include "core/file_sys/archive_backend.h"
+#include "core/file_sys/directory_backend.h"
+#include "core/file_sys/file_backend.h"
+#include "core/hle/result.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// Archive backend for general save data archive type (SaveData and SystemSaveData)
+class SaveDataArchive : public ArchiveBackend {
+public:
+ explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
+
+ std::string GetName() const override {
+ return "SaveDataArchive: " + mount_point;
+ }
+
+ ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
+ const Mode& mode) const override;
+ ResultCode DeleteFile(const Path& path) const override;
+ ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
+ ResultCode DeleteDirectory(const Path& path) const override;
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override;
+ ResultCode CreateFile(const Path& path, u64 size) const override;
+ ResultCode CreateDirectory(const Path& path) const override;
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
+ u64 GetFreeBytes() const override;
+
+protected:
+ std::string mount_point;
+};
+
+} // namespace FileSys
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp
index 4311d9897..645b2d5fe 100644
--- a/src/core/hle/applets/applet.cpp
+++ b/src/core/hle/applets/applet.cpp
@@ -101,6 +101,10 @@ ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter)
return result;
}
+bool Applet::IsRunning() const {
+ return is_running;
+}
+
bool IsLibraryAppletRunning() {
// Check the applets map for instances of any applet
for (auto itr = applets.begin(); itr != applets.end(); ++itr)
diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h
index bfdcad126..ebeed9813 100644
--- a/src/core/hle/applets/applet.h
+++ b/src/core/hle/applets/applet.h
@@ -13,8 +13,7 @@ namespace Applets {
class Applet {
public:
- virtual ~Applet() {}
- Applet(Service::APT::AppletId id) : id(id) {}
+ virtual ~Applet() = default;
/**
* Creates an instance of the Applet subclass identified by the parameter.
@@ -48,7 +47,7 @@ public:
/**
* Whether the applet is currently executing instead of the host application or not.
*/
- virtual bool IsRunning() const = 0;
+ bool IsRunning() const;
/**
* Handles an update tick for the Applet, lets it update the screen, send commands, etc.
@@ -56,6 +55,8 @@ public:
virtual void Update() = 0;
protected:
+ explicit Applet(Service::APT::AppletId id) : id(id) {}
+
/**
* Handles the Applet start event, triggered from the application.
* @param parameter Parameter data to handle.
@@ -65,6 +66,9 @@ protected:
Service::APT::AppletId id; ///< Id of this Applet
std::shared_ptr<std::vector<u8>> heap_memory; ///< Heap memory for this Applet
+
+ /// Whether this applet is currently running instead of the host application or not.
+ bool is_running = false;
};
/// Returns whether a library applet is currently running
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
index e1379ac4d..75d7fd9fc 100644
--- a/src/core/hle/applets/erreula.cpp
+++ b/src/core/hle/applets/erreula.cpp
@@ -47,7 +47,7 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
}
ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parameter) {
- started = true;
+ is_running = true;
// TODO(Subv): Set the expected fields in the response buffer before resending it to the
// application.
@@ -62,7 +62,7 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame
message.sender_id = static_cast<u32>(id);
Service::APT::SendParameter(message);
- started = false;
+ is_running = false;
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/applets/erreula.h b/src/core/hle/applets/erreula.h
index a7ec7ec01..681bbea0c 100644
--- a/src/core/hle/applets/erreula.h
+++ b/src/core/hle/applets/erreula.h
@@ -17,18 +17,12 @@ public:
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
void Update() override;
- bool IsRunning() const override {
- return started;
- }
+private:
/// This SharedMemory will be created when we receive the LibAppJustStarted message.
/// It holds the framebuffer info retrieved by the application with
/// GSPGPU::ImportDisplayCaptureInfo
Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
-
-private:
- /// Whether this applet is currently running instead of the host application or not.
- bool started = false;
};
} // namespace Applets
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 3455b9201..07c7f5b99 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -55,7 +55,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
}
ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& parameter) {
- started = true;
+ is_running = true;
// TODO(Subv): Set the expected fields in the response buffer before resending it to the
// application.
@@ -78,7 +78,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
message.sender_id = static_cast<u32>(id);
Service::APT::SendParameter(message);
- started = false;
+ is_running = false;
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
index e3ab9f0cd..ec00e29d2 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -65,23 +65,18 @@ ASSERT_REG_POSITION(unk_6C, 0x6C);
class MiiSelector final : public Applet {
public:
- MiiSelector(Service::APT::AppletId id) : Applet(id), started(false) {}
+ MiiSelector(Service::APT::AppletId id) : Applet(id) {}
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
void Update() override;
- bool IsRunning() const override {
- return started;
- }
+private:
/// This SharedMemory will be created when we receive the LibAppJustStarted message.
/// It holds the framebuffer info retrieved by the application with
/// GSPGPU::ImportDisplayCaptureInfo
Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory;
- /// Whether this applet is currently running instead of the host application or not.
- bool started;
-
MiiConfig config;
};
}
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp
index 1e21337f5..edd30d7ef 100644
--- a/src/core/hle/applets/swkbd.cpp
+++ b/src/core/hle/applets/swkbd.cpp
@@ -70,7 +70,7 @@ ResultCode SoftwareKeyboard::StartImpl(Service::APT::AppletStartupParameter cons
DrawScreenKeyboard();
- started = true;
+ is_running = true;
return RESULT_SUCCESS;
}
@@ -113,7 +113,7 @@ void SoftwareKeyboard::Finalize() {
message.sender_id = static_cast<u32>(id);
Service::APT::SendParameter(message);
- started = false;
+ is_running = false;
}
}
} // namespace
diff --git a/src/core/hle/applets/swkbd.h b/src/core/hle/applets/swkbd.h
index ea0b1fba9..cc92a8f19 100644
--- a/src/core/hle/applets/swkbd.h
+++ b/src/core/hle/applets/swkbd.h
@@ -52,14 +52,11 @@ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config
class SoftwareKeyboard final : public Applet {
public:
- SoftwareKeyboard(Service::APT::AppletId id) : Applet(id), started(false) {}
+ SoftwareKeyboard(Service::APT::AppletId id) : Applet(id) {}
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
void Update() override;
- bool IsRunning() const override {
- return started;
- }
/**
* Draws a keyboard to the current bottom screen framebuffer.
@@ -72,6 +69,7 @@ public:
*/
void Finalize();
+private:
/// This SharedMemory will be created when we receive the LibAppJustStarted message.
/// It holds the framebuffer info retrieved by the application with
/// GSPGPU::ImportDisplayCaptureInfo
@@ -82,9 +80,6 @@ public:
/// Configuration of this instance of the SoftwareKeyboard, as received from the application
SoftwareKeyboardConfig config;
-
- /// Whether this applet is currently running instead of the host application or not.
- bool started;
};
}
} // namespace
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 9e1795927..0c8752670 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -124,13 +124,11 @@ void HandleTable::Clear() {
}
/// Initialize the kernel
-void Init() {
+void Init(u32 system_mode) {
ConfigMem::Init();
SharedPage::Init();
- // TODO(yuriks): The memory type parameter needs to be determined by the ExHeader field instead
- // For now it defaults to the one with a largest allocation to the app
- Kernel::MemoryInit(2); // Allocates 96MB to the application
+ Kernel::MemoryInit(system_mode);
Kernel::ResourceLimitsInit();
Kernel::ThreadingInit();
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 6b8dbecff..231cf7b75 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -286,8 +286,8 @@ private:
extern HandleTable g_handle_table;
-/// Initialize the kernel
-void Init();
+/// Initialize the kernel with the specified system mode.
+void Init(u32 system_mode);
/// Shutdown the kernel
void Shutdown();
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 7f8d8e00d..8d29117a8 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -20,15 +20,25 @@ enum class ErrorDescription : u32 {
OS_InvalidBufferDescriptor = 48,
WrongAddress = 53,
FS_ArchiveNotMounted = 101,
+ FS_FileNotFound = 112,
+ FS_PathNotFound = 113,
+ FS_GameCardNotInserted = 141,
FS_NotFound = 120,
+ FS_FileAlreadyExists = 180,
+ FS_DirectoryAlreadyExists = 185,
FS_AlreadyExists = 190,
FS_InvalidOpenFlags = 230,
+ FS_DirectoryNotEmpty = 240,
FS_NotAFile = 250,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
OutofRangeOrMisalignedAddress =
513, // TODO(purpasmart): Check if this name fits its actual usage
GPU_FirstInitialization = 519,
+ FS_InvalidReadFlag = 700,
FS_InvalidPath = 702,
+ FS_WriteBeyondEnd = 705,
+ FS_UnsupportedOpenFlags = 760,
+ FS_UnexpectedFileOrDirectory = 770,
InvalidSection = 1000,
TooLarge = 1001,
NotAuthorized = 1002,
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index 18026975f..fe367aca5 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -230,6 +230,24 @@ static void IsConnected(Service::Interface* self) {
LOG_WARNING(Service_AC, "(STUBBED) called");
}
+/**
+ * AC_U::SetClientVersion service function
+ * Inputs:
+ * 1 : Used SDK Version
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetClientVersion(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ const u32 version = cmd_buff[1];
+ self->SetVersion(version);
+
+ LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x%08X", version);
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, CreateDefaultConfig, "CreateDefaultConfig"},
{0x00040006, ConnectAsync, "ConnectAsync"},
@@ -250,7 +268,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"},
{0x003C0042, nullptr, "GetAPSSIDList"},
{0x003E0042, IsConnected, "IsConnected"},
- {0x00400042, nullptr, "SetClientVersion"},
+ {0x00400042, SetClientVersion, "SetClientVersion"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/act_a.cpp b/src/core/hle/service/act_a.cpp
index 3a775fa90..35ba71f79 100644
--- a/src/core/hle/service/act_a.cpp
+++ b/src/core/hle/service/act_a.cpp
@@ -10,6 +10,13 @@
namespace ACT_A {
const Interface::FunctionInfo FunctionTable[] = {
+ // act:u shared commands
+ {0x00010084, nullptr, "Initialize"},
+ {0x00020040, nullptr, "GetErrorCode"},
+ {0x000600C2, nullptr, "GetAccountDataBlock"},
+ {0x000B0042, nullptr, "AcquireEulaList"},
+ {0x000D0040, nullptr, "GenerateUuid"},
+ // act:a
{0x041300C2, nullptr, "UpdateMiiImage"},
{0x041B0142, nullptr, "AgreeEula"},
{0x04210042, nullptr, "UploadMii"},
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp
index 05de4d002..1b871d441 100644
--- a/src/core/hle/service/act_u.cpp
+++ b/src/core/hle/service/act_u.cpp
@@ -10,10 +10,13 @@
namespace ACT_U {
const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
{0x00010084, nullptr, "Initialize"},
{0x00020040, nullptr, "GetErrorCode"},
{0x000600C2, nullptr, "GetAccountDataBlock"},
+ {0x000B0042, nullptr, "AcquireEulaList"},
{0x000D0040, nullptr, "GenerateUuid"},
+ // clang-format on
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index f7a990d69..d344a622f 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -20,7 +20,7 @@ static std::array<u32, 3> am_titles_list_count = {0, 0, 0};
static u32 am_ticket_count = 0;
static u32 am_ticket_list_count = 0;
-void GetTitleCount(Service::Interface* self) {
+void GetNumPrograms(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 media_type = cmd_buff[1] & 0xFF;
@@ -81,7 +81,7 @@ void DeleteContents(Service::Interface* self) {
media_type, title_id, am_content_count[media_type], content_ids_pointer);
}
-void GetTitleList(Service::Interface* self) {
+void GetProgramList(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 media_type = cmd_buff[2] & 0xFF;
@@ -97,7 +97,7 @@ void GetTitleList(Service::Interface* self) {
media_type, am_titles_list_count[media_type], title_ids_output_pointer);
}
-void GetTitleInfo(Service::Interface* self) {
+void GetProgramInfos(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 media_type = cmd_buff[1] & 0xFF;
@@ -113,7 +113,7 @@ void GetTitleInfo(Service::Interface* self) {
}
void GetDataTitleInfos(Service::Interface* self) {
- GetTitleInfo(self);
+ GetProgramInfos(self);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
@@ -151,7 +151,7 @@ void DeleteTicket(Service::Interface* self) {
LOG_WARNING(Service_AM, "(STUBBED) called title_id=0x%016" PRIx64 "", title_id);
}
-void GetTicketCount(Service::Interface* self) {
+void GetNumTickets(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 5676cdd5f..9bc2ca305 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -11,7 +11,7 @@ class Interface;
namespace AM {
/**
- * AM::GetTitleCount service function
+ * AM::GetNumPrograms service function
* Gets the number of installed titles in the requested media type
* Inputs:
* 0 : Command header (0x00010040)
@@ -20,7 +20,7 @@ namespace AM {
* 1 : Result, 0 on success, otherwise error code
* 2 : The number of titles in the requested media type
*/
-void GetTitleCount(Service::Interface* self);
+void GetNumPrograms(Service::Interface* self);
/**
* AM::FindContentInfos service function
@@ -62,7 +62,7 @@ void ListContentInfos(Service::Interface* self);
void DeleteContents(Service::Interface* self);
/**
- * AM::GetTitleList service function
+ * AM::GetProgramList service function
* Loads information about the desired number of titles from the desired media type into an array
* Inputs:
* 1 : Title count
@@ -72,10 +72,10 @@ void DeleteContents(Service::Interface* self);
* 1 : Result, 0 on success, otherwise error code
* 2 : The number of titles loaded from the requested media type
*/
-void GetTitleList(Service::Interface* self);
+void GetProgramList(Service::Interface* self);
/**
- * AM::GetTitleInfo service function
+ * AM::GetProgramInfos service function
* Inputs:
* 1 : u8 Mediatype
* 2 : Total titles
@@ -84,11 +84,11 @@ void GetTitleList(Service::Interface* self);
* Outputs:
* 1 : Result, 0 on success, otherwise error code
*/
-void GetTitleInfo(Service::Interface* self);
+void GetProgramInfos(Service::Interface* self);
/**
* AM::GetDataTitleInfos service function
- * Wrapper for AM::GetTitleInfo
+ * Wrapper for AM::GetProgramInfos
* Inputs:
* 1 : u8 Mediatype
* 2 : Total titles
@@ -135,12 +135,12 @@ void GetNumContentInfos(Service::Interface* self);
void DeleteTicket(Service::Interface* self);
/**
- * AM::GetTicketCount service function
+ * AM::GetNumTickets service function
* Outputs:
* 1 : Result, 0 on success, otherwise error code
- * 2 : Total titles
+ * 2 : Number of tickets
*/
-void GetTicketCount(Service::Interface* self);
+void GetNumTickets(Service::Interface* self);
/**
* AM::GetTicketList service function
diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp
index bfc1ca6bd..218375c8f 100644
--- a/src/core/hle/service/am/am_app.cpp
+++ b/src/core/hle/service/am/am_app.cpp
@@ -14,9 +14,14 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x10030142, ListContentInfos, "ListContentInfos"},
{0x10040102, DeleteContents, "DeleteContents"},
{0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
+ {0x10060080, nullptr, "GetNumDataTitleTickets"},
{0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
+ {0x100801C2, nullptr, "GetItemRights"},
{0x100900C0, nullptr, "IsDataTitleInUse"},
{0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
+ {0x100B00C0, nullptr, "GetNumExistingContentInfos"},
+ {0x100C0142, nullptr, "ListExistingContentInfos"},
+ {0x100D0084, nullptr, "GetPatchTitleInfos"},
};
AM_APP_Interface::AM_APP_Interface() {
diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp
index 3a597a34c..f3cd1d23f 100644
--- a/src/core/hle/service/am/am_net.cpp
+++ b/src/core/hle/service/am/am_net.cpp
@@ -9,61 +9,116 @@ namespace Service {
namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, GetTitleCount, "GetTitleCount"},
- {0x00020082, GetTitleList, "GetTitleList"},
- {0x00030084, GetTitleInfo, "GetTitleInfo"},
- {0x000400C0, nullptr, "DeleteApplicationTitle"},
- {0x000500C0, nullptr, "GetTitleProductCode"},
- {0x000600C0, nullptr, "GetTitleExtDataId"},
+ {0x00010040, GetNumPrograms, "GetNumPrograms"},
+ {0x00020082, GetProgramList, "GetProgramList"},
+ {0x00030084, GetProgramInfos, "GetProgramInfos"},
+ {0x000400C0, nullptr, "DeleteUserProgram"},
+ {0x000500C0, nullptr, "GetProductCode"},
+ {0x000600C0, nullptr, "GetStorageId"},
{0x00070080, DeleteTicket, "DeleteTicket"},
- {0x00080000, GetTicketCount, "GetTicketCount"},
+ {0x00080000, GetNumTickets, "GetNumTickets"},
{0x00090082, GetTicketList, "GetTicketList"},
{0x000A0000, nullptr, "GetDeviceID"},
- {0x000D0084, nullptr, "GetPendingTitleInfo"},
- {0x000E00C0, nullptr, "DeletePendingTitle"},
- {0x00140040, nullptr, "FinalizePendingTitles"},
- {0x00150040, nullptr, "DeleteAllPendingTitles"},
+ {0x000B0040, nullptr, "GetNumImportTitleContexts"},
+ {0x000C0082, nullptr, "GetImportTitleContextList"},
+ {0x000D0084, nullptr, "GetImportTitleContexts"},
+ {0x000E00C0, nullptr, "DeleteImportTitleContext"},
+ {0x000F00C0, nullptr, "GetNumImportContentContexts"},
+ {0x00100102, nullptr, "GetImportContentContextList"},
+ {0x00110104, nullptr, "GetImportContentContexts"},
+ {0x00120102, nullptr, "DeleteImportContentContexts"},
+ {0x00130040, nullptr, "NeedsCleanup"},
+ {0x00140040, nullptr, "DoCleanup"},
+ {0x00150040, nullptr, "DeleteAllImportContexts"},
+ {0x00160000, nullptr, "DeleteAllTemporaryPrograms"},
+ {0x00170044, nullptr, "ImportTwlBackupLegacy"},
{0x00180080, nullptr, "InitializeTitleDatabase"},
- {0x00190040, nullptr, "ReloadDBS"},
- {0x001A00C0, nullptr, "GetDSiWareExportSize"},
- {0x001B0144, nullptr, "ExportDSiWare"},
- {0x001C0084, nullptr, "ImportDSiWare"},
- {0x00230080, nullptr, "TitleIDListGetTotal2"},
- {0x002400C2, nullptr, "GetTitleIDList2"},
- {0x04010080, nullptr, "InstallFIRM"},
- {0x04020040, nullptr, "StartInstallCIADB0"},
- {0x04030000, nullptr, "StartInstallCIADB1"},
- {0x04040002, nullptr, "AbortCIAInstall"},
- {0x04050002, nullptr, "CloseCIAFinalizeInstall"},
- {0x04060002, nullptr, "CloseCIA"},
- {0x040700C2, nullptr, "FinalizeTitlesInstall"},
- {0x04080042, nullptr, "GetCiaFileInfo"},
- {0x040E00C2, nullptr, "InstallTitlesFinish"},
- {0x040F0000, nullptr, "InstallNATIVEFIRM"},
- {0x041000C0, nullptr, "DeleteTitle"},
- {0x04120000, nullptr, "Initialize"},
- {0x041700C0, nullptr, "MigrateAGBtoSAV"},
- {0x08010000, nullptr, "OpenTicket"},
- {0x08020002, nullptr, "TicketAbortInstall"},
- {0x08030002, nullptr, "TicketFinalizeInstall"},
- {0x08040100, nullptr, "InstallTitleBegin"},
- {0x08050000, nullptr, "InstallTitleAbort"},
- {0x080600C0, nullptr, "InstallTitleResume"},
- {0x08070000, nullptr, "InstallTitleAbortTMD"},
- {0x08080000, nullptr, "InstallTitleFinish"},
- {0x080A0000, nullptr, "OpenTMD"},
- {0x080B0002, nullptr, "TMDAbortInstall"},
- {0x080C0042, nullptr, "TMDFinalizeInstall"},
- {0x080E0040, nullptr, "OpenContentCreate"},
- {0x080F0002, nullptr, "ContentAbortInstall"},
- {0x08100040, nullptr, "OpenContentResume"},
- {0x08120002, nullptr, "ContentFinalizeInstall"},
- {0x08130000, nullptr, "GetTotalContents"},
- {0x08140042, nullptr, "GetContentIndexes"},
- {0x08150044, nullptr, "GetContentsInfo"},
- {0x08180042, nullptr, "GetCTCert"},
- {0x08190108, nullptr, "SetCertificates"},
- {0x081B00C2, nullptr, "InstallTitlesFinish"},
+ {0x00190040, nullptr, "QueryAvailableTitleDatabase"},
+ {0x001A00C0, nullptr, "CalcTwlBackupSize"},
+ {0x001B0144, nullptr, "ExportTwlBackup"},
+ {0x001C0084, nullptr, "ImportTwlBackup"},
+ {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"},
+ {0x001E00C8, nullptr, "ReadTwlBackupInfo"},
+ {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"},
+ {0x00200000, nullptr, "GetTwlArchiveResourceInfo"},
+ {0x00210042, nullptr, "GetPersonalizedTicketInfoList"},
+ {0x00220080, nullptr, "DeleteAllImportContextsFiltered"},
+ {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"},
+ {0x002400C2, nullptr, "GetImportTitleContextListFiltered"},
+ {0x002500C0, nullptr, "CheckContentRights"},
+ {0x00260044, nullptr, "GetTicketLimitInfos"},
+ {0x00270044, nullptr, "GetDemoLaunchInfos"},
+ {0x00280108, nullptr, "ReadTwlBackupInfoEx"},
+ {0x00290082, nullptr, "DeleteUserProgramsAtomically"},
+ {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"},
+ {0x002B0142, nullptr, "ListExistingContentInfosSystem"},
+ {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"},
+ {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"},
+ {0x04010080, nullptr, "UpdateFirmwareTo"},
+ {0x04020040, nullptr, "BeginImportProgram"},
+ {0x04030000, nullptr, "BeginImportProgramTemporarily"},
+ {0x04040002, nullptr, "CancelImportProgram"},
+ {0x04050002, nullptr, "EndImportProgram"},
+ {0x04060002, nullptr, "EndImportProgramWithoutCommit"},
+ {0x040700C2, nullptr, "CommitImportPrograms"},
+ {0x04080042, nullptr, "GetProgramInfoFromCia"},
+ {0x04090004, nullptr, "GetSystemMenuDataFromCia"},
+ {0x040A0002, nullptr, "GetDependencyListFromCia"},
+ {0x040B0002, nullptr, "GetTransferSizeFromCia"},
+ {0x040C0002, nullptr, "GetCoreVersionFromCia"},
+ {0x040D0042, nullptr, "GetRequiredSizeFromCia"},
+ {0x040E00C2, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
+ {0x040F0000, nullptr, "UpdateFirmwareAuto"},
+ {0x041000C0, nullptr, "DeleteProgram"},
+ {0x04110044, nullptr, "GetTwlProgramListForReboot"},
+ {0x04120000, nullptr, "GetSystemUpdaterMutex"},
+ {0x04130002, nullptr, "GetMetaSizeFromCia"},
+ {0x04140044, nullptr, "GetMetaDataFromCia"},
+ {0x04150080, nullptr, "CheckDemoLaunchRights"},
+ {0x041600C0, nullptr, "GetInternalTitleLocationInfo"},
+ {0x041700C0, nullptr, "PerpetuateAgbSaveData"},
+ {0x04180040, nullptr, "BeginImportProgramForOverWrite"},
+ {0x04190000, nullptr, "BeginImportSystemProgram"},
+ {0x08010000, nullptr, "BeginImportTicket"},
+ {0x08020002, nullptr, "CancelImportTicket"},
+ {0x08030002, nullptr, "EndImportTicket"},
+ {0x08040100, nullptr, "BeginImportTitle"},
+ {0x08050000, nullptr, "StopImportTitle"},
+ {0x080600C0, nullptr, "ResumeImportTitle"},
+ {0x08070000, nullptr, "CancelImportTitle"},
+ {0x08080000, nullptr, "EndImportTitle"},
+ {0x080900C2, nullptr, "CommitImportTitles"},
+ {0x080A0000, nullptr, "BeginImportTmd"},
+ {0x080B0002, nullptr, "CancelImportTmd"},
+ {0x080C0042, nullptr, "EndImportTmd"},
+ {0x080D0042, nullptr, "CreateImportContentContexts"},
+ {0x080E0040, nullptr, "BeginImportContent"},
+ {0x080F0002, nullptr, "StopImportContent"},
+ {0x08100040, nullptr, "ResumeImportContent"},
+ {0x08110002, nullptr, "CancelImportContent"},
+ {0x08120002, nullptr, "EndImportContent"},
+ {0x08130000, nullptr, "GetNumCurrentImportContentContexts"},
+ {0x08140042, nullptr, "GetCurrentImportContentContextList"},
+ {0x08150044, nullptr, "GetCurrentImportContentContexts"},
+ {0x08160146, nullptr, "Sign"},
+ {0x08170146, nullptr, "Verify"},
+ {0x08180042, nullptr, "GetDeviceCert"},
+ {0x08190108, nullptr, "ImportCertificates"},
+ {0x081A0042, nullptr, "ImportCertificate"},
+ {0x081B00C2, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"},
+ {0x081C0100, nullptr, "DeleteTicketId"},
+ {0x081D0080, nullptr, "GetNumTicketIds"},
+ {0x081E0102, nullptr, "GetTicketIdList"},
+ {0x081F0080, nullptr, "GetNumTicketsOfProgram"},
+ {0x08200102, nullptr, "ListTicketInfos"},
+ {0x08210142, nullptr, "GetRightsOnlyTicketData"},
+ {0x08220000, nullptr, "GetNumCurrentContentInfos"},
+ {0x08230044, nullptr, "FindCurrentContentInfos"},
+ {0x08240082, nullptr, "ListCurrentContentInfos"},
+ {0x08250102, nullptr, "CalculateContextRequiredSize"},
+ {0x08260042, nullptr, "UpdateImportContentContexts"},
+ {0x08270000, nullptr, "DeleteAllDemoLaunchInfos"},
+ {0x082800C0, nullptr, "BeginImportTitleForOverWrite"},
};
AM_NET_Interface::AM_NET_Interface() {
diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp
index a2268303c..949b3591d 100644
--- a/src/core/hle/service/am/am_sys.cpp
+++ b/src/core/hle/service/am/am_sys.cpp
@@ -9,27 +9,64 @@ namespace Service {
namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, GetTitleCount, "GetTitleCount"},
- {0x00020082, GetTitleList, "GetTitleList"},
- {0x00030084, GetTitleInfo, "GetTitleInfo"},
- {0x000400C0, nullptr, "DeleteApplicationTitle"},
- {0x000500C0, nullptr, "GetTitleProductCode"},
- {0x000600C0, nullptr, "GetTitleExtDataId"},
+ {0x00010040, GetNumPrograms, "GetNumPrograms"},
+ {0x00020082, GetProgramList, "GetProgramList"},
+ {0x00030084, GetProgramInfos, "GetProgramInfos"},
+ {0x000400C0, nullptr, "DeleteUserProgram"},
+ {0x000500C0, nullptr, "GetProductCode"},
+ {0x000600C0, nullptr, "GetStorageId"},
{0x00070080, DeleteTicket, "DeleteTicket"},
- {0x00080000, GetTicketCount, "GetTicketCount"},
+ {0x00080000, GetNumTickets, "GetNumTickets"},
{0x00090082, GetTicketList, "GetTicketList"},
{0x000A0000, nullptr, "GetDeviceID"},
- {0x000D0084, nullptr, "GetPendingTitleInfo"},
- {0x000E00C0, nullptr, "DeletePendingTitle"},
- {0x00140040, nullptr, "FinalizePendingTitles"},
- {0x00150040, nullptr, "DeleteAllPendingTitles"},
+ {0x000B0040, nullptr, "GetNumImportTitleContexts"},
+ {0x000C0082, nullptr, "GetImportTitleContextList"},
+ {0x000D0084, nullptr, "GetImportTitleContexts"},
+ {0x000E00C0, nullptr, "DeleteImportTitleContext"},
+ {0x000F00C0, nullptr, "GetNumImportContentContexts"},
+ {0x00100102, nullptr, "GetImportContentContextList"},
+ {0x00110104, nullptr, "GetImportContentContexts"},
+ {0x00120102, nullptr, "DeleteImportContentContexts"},
+ {0x00130040, nullptr, "NeedsCleanup"},
+ {0x00140040, nullptr, "DoCleanup"},
+ {0x00150040, nullptr, "DeleteAllImportContexts"},
+ {0x00160000, nullptr, "DeleteAllTemporaryPrograms"},
+ {0x00170044, nullptr, "ImportTwlBackupLegacy"},
{0x00180080, nullptr, "InitializeTitleDatabase"},
- {0x00190040, nullptr, "ReloadDBS"},
- {0x001A00C0, nullptr, "GetDSiWareExportSize"},
- {0x001B0144, nullptr, "ExportDSiWare"},
- {0x001C0084, nullptr, "ImportDSiWare"},
- {0x00230080, nullptr, "GetPendingTitleCount"},
- {0x002400C2, nullptr, "GetPendingTitleList"},
+ {0x00190040, nullptr, "QueryAvailableTitleDatabase"},
+ {0x001A00C0, nullptr, "CalcTwlBackupSize"},
+ {0x001B0144, nullptr, "ExportTwlBackup"},
+ {0x001C0084, nullptr, "ImportTwlBackup"},
+ {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"},
+ {0x001E00C8, nullptr, "ReadTwlBackupInfo"},
+ {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"},
+ {0x00200000, nullptr, "GetTwlArchiveResourceInfo"},
+ {0x00210042, nullptr, "GetPersonalizedTicketInfoList"},
+ {0x00220080, nullptr, "DeleteAllImportContextsFiltered"},
+ {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"},
+ {0x002400C2, nullptr, "GetImportTitleContextListFiltered"},
+ {0x002500C0, nullptr, "CheckContentRights"},
+ {0x00260044, nullptr, "GetTicketLimitInfos"},
+ {0x00270044, nullptr, "GetDemoLaunchInfos"},
+ {0x00280108, nullptr, "ReadTwlBackupInfoEx"},
+ {0x00290082, nullptr, "DeleteUserProgramsAtomically"},
+ {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"},
+ {0x002B0142, nullptr, "ListExistingContentInfosSystem"},
+ {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"},
+ {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"},
+ {0x100100C0, GetNumContentInfos, "GetNumContentInfos"},
+ {0x10020104, FindContentInfos, "FindContentInfos"},
+ {0x10030142, ListContentInfos, "ListContentInfos"},
+ {0x10040102, DeleteContents, "DeleteContents"},
+ {0x10050084, GetDataTitleInfos, "GetDataTitleInfos"},
+ {0x10060080, nullptr, "GetNumDataTitleTickets"},
+ {0x10070102, ListDataTitleTicketInfos, "ListDataTitleTicketInfos"},
+ {0x100801C2, nullptr, "GetItemRights"},
+ {0x100900C0, nullptr, "IsDataTitleInUse"},
+ {0x100A0000, nullptr, "IsExternalTitleDatabaseInitialized"},
+ {0x100B00C0, nullptr, "GetNumExistingContentInfos"},
+ {0x100C0142, nullptr, "ListExistingContentInfos"},
+ {0x100D0084, nullptr, "GetPatchTitleInfos"},
};
AM_SYS_Interface::AM_SYS_Interface() {
diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp
index 151b5e42b..354d51610 100644
--- a/src/core/hle/service/am/am_u.cpp
+++ b/src/core/hle/service/am/am_u.cpp
@@ -9,40 +9,76 @@ namespace Service {
namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, GetTitleCount, "GetTitleCount"},
- {0x00020082, GetTitleList, "GetTitleList"},
- {0x00030084, GetTitleInfo, "GetTitleInfo"},
- {0x000400C0, nullptr, "DeleteApplicationTitle"},
- {0x000500C0, nullptr, "GetTitleProductCode"},
- {0x000600C0, nullptr, "GetTitleExtDataId"},
+ {0x00010040, GetNumPrograms, "GetNumPrograms"},
+ {0x00020082, GetProgramList, "GetProgramList"},
+ {0x00030084, GetProgramInfos, "GetProgramInfos"},
+ {0x000400C0, nullptr, "DeleteUserProgram"},
+ {0x000500C0, nullptr, "GetProductCode"},
+ {0x000600C0, nullptr, "GetStorageId"},
{0x00070080, DeleteTicket, "DeleteTicket"},
- {0x00080000, GetTicketCount, "GetTicketCount"},
+ {0x00080000, GetNumTickets, "GetNumTickets"},
{0x00090082, GetTicketList, "GetTicketList"},
{0x000A0000, nullptr, "GetDeviceID"},
- {0x000D0084, nullptr, "GetPendingTitleInfo"},
- {0x000E00C0, nullptr, "DeletePendingTitle"},
- {0x00140040, nullptr, "FinalizePendingTitles"},
- {0x00150040, nullptr, "DeleteAllPendingTitles"},
+ {0x000B0040, nullptr, "GetNumImportTitleContexts"},
+ {0x000C0082, nullptr, "GetImportTitleContextList"},
+ {0x000D0084, nullptr, "GetImportTitleContexts"},
+ {0x000E00C0, nullptr, "DeleteImportTitleContext"},
+ {0x000F00C0, nullptr, "GetNumImportContentContexts"},
+ {0x00100102, nullptr, "GetImportContentContextList"},
+ {0x00110104, nullptr, "GetImportContentContexts"},
+ {0x00120102, nullptr, "DeleteImportContentContexts"},
+ {0x00130040, nullptr, "NeedsCleanup"},
+ {0x00140040, nullptr, "DoCleanup"},
+ {0x00150040, nullptr, "DeleteAllImportContexts"},
+ {0x00160000, nullptr, "DeleteAllTemporaryPrograms"},
+ {0x00170044, nullptr, "ImportTwlBackupLegacy"},
{0x00180080, nullptr, "InitializeTitleDatabase"},
- {0x00190040, nullptr, "ReloadDBS"},
- {0x001A00C0, nullptr, "GetDSiWareExportSize"},
- {0x001B0144, nullptr, "ExportDSiWare"},
- {0x001C0084, nullptr, "ImportDSiWare"},
- {0x00230080, nullptr, "TitleIDListGetTotal2"},
- {0x002400C2, nullptr, "GetTitleIDList2"},
- {0x04010080, nullptr, "InstallFIRM"},
- {0x04020040, nullptr, "StartInstallCIADB0"},
- {0x04030000, nullptr, "StartInstallCIADB1"},
- {0x04040002, nullptr, "AbortCIAInstall"},
- {0x04050002, nullptr, "CloseCIAFinalizeInstall"},
- {0x04060002, nullptr, "CloseCIA"},
- {0x040700C2, nullptr, "FinalizeTitlesInstall"},
- {0x04080042, nullptr, "GetCiaFileInfo"},
- {0x040E00C2, nullptr, "InstallTitlesFinish"},
- {0x040F0000, nullptr, "InstallNATIVEFIRM"},
- {0x041000C0, nullptr, "DeleteTitle"},
- {0x04120000, nullptr, "Initialize"},
- {0x041700C0, nullptr, "MigrateAGBtoSAV"},
+ {0x00190040, nullptr, "QueryAvailableTitleDatabase"},
+ {0x001A00C0, nullptr, "CalcTwlBackupSize"},
+ {0x001B0144, nullptr, "ExportTwlBackup"},
+ {0x001C0084, nullptr, "ImportTwlBackup"},
+ {0x001D0000, nullptr, "DeleteAllTwlUserPrograms"},
+ {0x001E00C8, nullptr, "ReadTwlBackupInfo"},
+ {0x001F0040, nullptr, "DeleteAllExpiredUserPrograms"},
+ {0x00200000, nullptr, "GetTwlArchiveResourceInfo"},
+ {0x00210042, nullptr, "GetPersonalizedTicketInfoList"},
+ {0x00220080, nullptr, "DeleteAllImportContextsFiltered"},
+ {0x00230080, nullptr, "GetNumImportTitleContextsFiltered"},
+ {0x002400C2, nullptr, "GetImportTitleContextListFiltered"},
+ {0x002500C0, nullptr, "CheckContentRights"},
+ {0x00260044, nullptr, "GetTicketLimitInfos"},
+ {0x00270044, nullptr, "GetDemoLaunchInfos"},
+ {0x00280108, nullptr, "ReadTwlBackupInfoEx"},
+ {0x00290082, nullptr, "DeleteUserProgramsAtomically"},
+ {0x002A00C0, nullptr, "GetNumExistingContentInfosSystem"},
+ {0x002B0142, nullptr, "ListExistingContentInfosSystem"},
+ {0x002C0084, nullptr, "GetProgramInfosIgnorePlatform"},
+ {0x002D00C0, nullptr, "CheckContentRightsIgnorePlatform"},
+ {0x04010080, nullptr, "UpdateFirmwareTo"},
+ {0x04020040, nullptr, "BeginImportProgram"},
+ {0x04030000, nullptr, "BeginImportProgramTemporarily"},
+ {0x04040002, nullptr, "CancelImportProgram"},
+ {0x04050002, nullptr, "EndImportProgram"},
+ {0x04060002, nullptr, "EndImportProgramWithoutCommit"},
+ {0x040700C2, nullptr, "CommitImportPrograms"},
+ {0x04080042, nullptr, "GetProgramInfoFromCia"},
+ {0x04090004, nullptr, "GetSystemMenuDataFromCia"},
+ {0x040A0002, nullptr, "GetDependencyListFromCia"},
+ {0x040B0002, nullptr, "GetTransferSizeFromCia"},
+ {0x040C0002, nullptr, "GetCoreVersionFromCia"},
+ {0x040D0042, nullptr, "GetRequiredSizeFromCia"},
+ {0x040E00C2, nullptr, "CommitImportProgramsAndUpdateFirmwareAuto"},
+ {0x040F0000, nullptr, "UpdateFirmwareAuto"},
+ {0x041000C0, nullptr, "DeleteProgram"},
+ {0x04110044, nullptr, "GetTwlProgramListForReboot"},
+ {0x04120000, nullptr, "GetSystemUpdaterMutex"},
+ {0x04130002, nullptr, "GetMetaSizeFromCia"},
+ {0x04140044, nullptr, "GetMetaDataFromCia"},
+ {0x04150080, nullptr, "CheckDemoLaunchRights"},
+ {0x041600C0, nullptr, "GetInternalTitleLocationInfo"},
+ {0x041700C0, nullptr, "PerpetuateAgbSaveData"},
+ {0x04180040, nullptr, "BeginImportProgramForOverWrite"},
+ {0x04190000, nullptr, "BeginImportSystemProgram"},
};
AM_U_Interface::AM_U_Interface() {
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index a7a0c8a41..6e35e1d29 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -9,34 +9,97 @@ namespace Service {
namespace APT {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, GetLockHandle, "GetLockHandle?"},
- {0x00020080, Initialize, "Initialize?"},
- {0x00030040, Enable, "Enable?"},
- {0x00040040, nullptr, "Finalize?"},
+ {0x00010040, GetLockHandle, "GetLockHandle"},
+ {0x00020080, Initialize, "Initialize"},
+ {0x00030040, Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
{0x00050040, GetAppletManInfo, "GetAppletManInfo"},
{0x00060040, GetAppletInfo, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
{0x00090040, IsRegistered, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
{0x000B0040, InquireNotification, "InquireNotification"},
{0x000C0104, SendParameter, "SendParameter"},
{0x000D0080, ReceiveParameter, "ReceiveParameter"},
{0x000E0080, GlanceParameter, "GlanceParameter"},
{0x000F0100, CancelParameter, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
{0x00150140, PrepareToStartApplication, "PrepareToStartApplication"},
{0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
{0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
{0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
{0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
{0x003E0080, nullptr, "ReplySleepQuery"},
- {0x00430040, NotifyToWait, "NotifyToWait?"},
- {0x00440000, GetSharedFont, "GetSharedFont?"},
- {0x004B00C2, AppletUtility, "AppletUtility?"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, NotifyToWait, "NotifyToWait"},
+ {0x00440000, GetSharedFont, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, AppletUtility, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
{0x00510080, GetStartupArgument, "GetStartupArgument"},
+ {0x00520104, nullptr, "Wrap1"},
+ {0x00530104, nullptr, "Unwrap1"},
{0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
{0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
+ {0x00570044, nullptr, "WakeupApplication2"},
+ {0x00580002, nullptr, "GetProgramID"},
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
{0x01020000, CheckNew3DS, "CheckNew3DS"},
+ {0x01040000, nullptr, "IsStandardMemoryLayout"},
+ {0x01050100, nullptr, "IsTitleAllowed"},
};
APT_A_Interface::APT_A_Interface() {
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index c4556a5de..84019e6e5 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -94,9 +94,12 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00530104, nullptr, "Unwrap1"},
{0x00550040, SetScreenCapPostPermission, "SetScreenCapPostPermission"},
{0x00560000, GetScreenCapPostPermission, "GetScreenCapPostPermission"},
+ {0x00570044, nullptr, "WakeupApplication2"},
{0x00580002, nullptr, "GetProgramID"},
{0x01010000, CheckNew3DSApp, "CheckNew3DSApp"},
{0x01020000, CheckNew3DS, "CheckNew3DS"},
+ {0x01040000, nullptr, "IsStandardMemoryLayout"},
+ {0x01050100, nullptr, "IsTitleAllowed"},
};
APT_S_Interface::APT_S_Interface() {
diff --git a/src/core/hle/service/boss/boss_p.cpp b/src/core/hle/service/boss/boss_p.cpp
index dfee8d055..ee941e228 100644
--- a/src/core/hle/service/boss/boss_p.cpp
+++ b/src/core/hle/service/boss/boss_p.cpp
@@ -2,16 +2,81 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/service/boss/boss.h"
#include "core/hle/service/boss/boss_p.h"
namespace Service {
namespace BOSS {
-// Empty arrays are illegal -- commented out until an entry is added.
-// const Interface::FunctionInfo FunctionTable[] = { };
+const Interface::FunctionInfo FunctionTable[] = {
+ // boss:u shared commands
+ {0x00010082, InitializeSession, "InitializeSession"},
+ {0x00020100, RegisterStorage, "RegisterStorage"},
+ {0x00030000, UnregisterStorage, "UnregisterStorage"},
+ {0x00040000, GetStorageInfo, "GetStorageInfo"},
+ {0x00050042, RegisterPrivateRootCa, "RegisterPrivateRootCa"},
+ {0x00060084, RegisterPrivateClientCert, "RegisterPrivateClientCert"},
+ {0x00070000, GetNewArrivalFlag, "GetNewArrivalFlag"},
+ {0x00080002, RegisterNewArrivalEvent, "RegisterNewArrivalEvent"},
+ {0x00090040, SetOptoutFlag, "SetOptoutFlag"},
+ {0x000A0000, GetOptoutFlag, "GetOptoutFlag"},
+ {0x000B00C2, RegisterTask, "RegisterTask"},
+ {0x000C0082, UnregisterTask, "UnregisterTask"},
+ {0x000D0082, ReconfigureTask, "ReconfigureTask"},
+ {0x000E0000, GetTaskIdList, "GetTaskIdList"},
+ {0x000F0042, GetStepIdList, "GetStepIdList"},
+ {0x00100102, GetNsDataIdList, "GetNsDataIdList"},
+ {0x00110102, GetOwnNsDataIdList, "GetOwnNsDataIdList"},
+ {0x00120102, GetNewDataNsDataIdList, "GetNewDataNsDataIdList"},
+ {0x00130102, GetOwnNewDataNsDataIdList, "GetOwnNewDataNsDataIdList"},
+ {0x00140082, SendProperty, "SendProperty"},
+ {0x00150042, SendPropertyHandle, "SendPropertyHandle"},
+ {0x00160082, ReceiveProperty, "ReceiveProperty"},
+ {0x00170082, UpdateTaskInterval, "UpdateTaskInterval"},
+ {0x00180082, UpdateTaskCount, "UpdateTaskCount"},
+ {0x00190042, GetTaskInterval, "GetTaskInterval"},
+ {0x001A0042, GetTaskCount, "GetTaskCount"},
+ {0x001B0042, GetTaskServiceStatus, "GetTaskServiceStatus"},
+ {0x001C0042, StartTask, "StartTask"},
+ {0x001D0042, StartTaskImmediate, "StartTaskImmediate"},
+ {0x001E0042, CancelTask, "CancelTask"},
+ {0x001F0000, GetTaskFinishHandle, "GetTaskFinishHandle"},
+ {0x00200082, GetTaskState, "GetTaskState"},
+ {0x00210042, GetTaskResult, "GetTaskResult"},
+ {0x00220042, GetTaskCommErrorCode, "GetTaskCommErrorCode"},
+ {0x002300C2, GetTaskStatus, "GetTaskStatus"},
+ {0x00240082, GetTaskError, "GetTaskError"},
+ {0x00250082, GetTaskInfo, "GetTaskInfo"},
+ {0x00260040, DeleteNsData, "DeleteNsData"},
+ {0x002700C2, GetNsDataHeaderInfo, "GetNsDataHeaderInfo"},
+ {0x00280102, ReadNsData, "ReadNsData"},
+ {0x00290080, SetNsDataAdditionalInfo, "SetNsDataAdditionalInfo"},
+ {0x002A0040, GetNsDataAdditionalInfo, "GetNsDataAdditionalInfo"},
+ {0x002B0080, SetNsDataNewFlag, "SetNsDataNewFlag"},
+ {0x002C0040, GetNsDataNewFlag, "GetNsDataNewFlag"},
+ {0x002D0040, GetNsDataLastUpdate, "GetNsDataLastUpdate"},
+ {0x002E0040, GetErrorCode, "GetErrorCode"},
+ {0x002F0140, RegisterStorageEntry, "RegisterStorageEntry"},
+ {0x00300000, GetStorageEntryInfo, "GetStorageEntryInfo"},
+ {0x00310100, SetStorageOption, "SetStorageOption"},
+ {0x00320000, GetStorageOption, "GetStorageOption"},
+ {0x00330042, StartBgImmediate, "StartBgImmediate"},
+ {0x00340042, GetTaskActivePriority, "GetTaskActivePriority"},
+ {0x003500C2, RegisterImmediateTask, "RegisterImmediateTask"},
+ {0x00360084, SetTaskQuery, "SetTaskQuery"},
+ {0x00370084, GetTaskQuery, "GetTaskQuery"},
+ // boss:p
+ {0x04040080, nullptr, "GetAppNewFlag"},
+ {0x04130082, nullptr, "SendPropertyPrivileged"},
+ {0x041500C0, nullptr, "DeleteNsDataPrivileged"},
+ {0x04160142, nullptr, "GetNsDataHeaderInfoPrivileged"},
+ {0x04170182, nullptr, "ReadNsDataPrivileged"},
+ {0x041A0100, nullptr, "SetNsDataNewFlagPrivileged"},
+ {0x041B00C0, nullptr, "GetNsDataNewFlagPrivileged"},
+};
BOSS_P_Interface::BOSS_P_Interface() {
- // Register(FunctionTable);
+ Register(FunctionTable);
}
} // namespace BOSS
diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp
index 4b747de7b..7d98ba6e9 100644
--- a/src/core/hle/service/cecd/cecd_u.cpp
+++ b/src/core/hle/service/cecd/cecd_u.cpp
@@ -9,10 +9,22 @@ namespace Service {
namespace CECD {
static const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C2, nullptr, "OpenRawFile"},
+ {0x00020042, nullptr, "ReadRawFile"},
+ {0x00030104, nullptr, "ReadMessage"},
+ {0x00040106, nullptr, "ReadMessageWithHMAC"},
+ {0x00050042, nullptr, "WriteRawFile"},
+ {0x00060104, nullptr, "WriteMessage"},
+ {0x00070106, nullptr, "WriteMessageWithHMAC"},
+ {0x00080102, nullptr, "Delete"},
+ {0x000A00C4, nullptr, "GetSystemInfo"},
+ {0x000B0040, nullptr, "RunCommand"},
+ {0x000C0040, nullptr, "RunCommandAlt"},
{0x000E0000, GetCecStateAbbreviated, "GetCecStateAbbreviated"},
{0x000F0000, GetCecInfoEventHandle, "GetCecInfoEventHandle"},
{0x00100000, GetChangeStateEventHandle, "GetChangeStateEventHandle"},
- {0x00120104, nullptr, "ReadSavedData"},
+ {0x00110104, nullptr, "OpenAndWrite"},
+ {0x00120104, nullptr, "OpenAndRead"},
};
CECD_U_Interface::CECD_U_Interface() {
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index d3d0f3b55..d554c3f54 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -360,7 +360,7 @@ ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const void* da
}
ResultCode DeleteConfigNANDSaveFile() {
- FileSys::Path path("config");
+ FileSys::Path path("/config");
return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path);
}
@@ -369,7 +369,7 @@ ResultCode UpdateConfigNANDSavegame() {
mode.write_flag.Assign(1);
mode.create_flag.Assign(1);
- FileSys::Path path("config");
+ FileSys::Path path("/config");
auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode);
ASSERT_MSG(config_result.Succeeded(), "could not open file");
@@ -383,8 +383,9 @@ ResultCode UpdateConfigNANDSavegame() {
ResultCode FormatConfig() {
ResultCode res = DeleteConfigNANDSaveFile();
// The delete command fails if the file doesn't exist, so we have to check that too
- if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound)
+ if (!res.IsSuccess() && res.description != ErrorDescription::FS_FileNotFound) {
return res;
+ }
// Delete the old data
cfg_config_file_buffer.fill(0);
// Create the header
@@ -510,7 +511,7 @@ ResultCode LoadConfigNANDSaveFile() {
cfg_system_save_data_archive = *archive_result;
- FileSys::Path config_path("config");
+ FileSys::Path config_path("/config");
FileSys::Mode open_mode = {};
open_mode.read_flag.Assign(1);
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp
index 2ff52c8b8..46312da4b 100644
--- a/src/core/hle/service/cfg/cfg_i.cpp
+++ b/src/core/hle/service/cfg/cfg_i.cpp
@@ -20,6 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080080, nullptr, "GoThroughTable"},
{0x00090040, GetCountryCodeString, "GetCountryCodeString"},
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
+ {0x000B0000, nullptr, "IsFangateSupported"},
// cfg:i
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
index eed26dec7..564a9bb08 100644
--- a/src/core/hle/service/cfg/cfg_s.cpp
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -20,6 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080080, nullptr, "GoThroughTable"},
{0x00090040, GetCountryCodeString, "GetCountryCodeString"},
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
+ {0x000B0000, nullptr, "IsFangateSupported"},
// cfg:s
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp
index f28217134..4c82846c0 100644
--- a/src/core/hle/service/cfg/cfg_u.cpp
+++ b/src/core/hle/service/cfg/cfg_u.cpp
@@ -20,6 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080080, nullptr, "GoThroughTable"},
{0x00090040, GetCountryCodeString, "GetCountryCodeString"},
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
+ {0x000B0000, nullptr, "IsFangateSupported"},
};
CFG_U_Interface::CFG_U_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 49d5b8d1c..25c07f401 100644
--- a/src/core/hle/service/dlp/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -22,7 +22,14 @@ static void unk_0x000E0040(Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010183, nullptr, "Initialize"},
{0x00020000, nullptr, "Finalize"},
+ {0x00030000, nullptr, "GetServerState"},
+ {0x00050080, nullptr, "StartAccepting"},
+ {0x00070000, nullptr, "StartDistribution"},
{0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
+ {0x00090040, nullptr, "AcceptClient"},
+ {0x000B0042, nullptr, "GetConnectingClients"},
+ {0x000C0040, nullptr, "GetClientInfo"},
+ {0x000D0040, nullptr, "GetClientState"},
{0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
};
diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp
index 1d16f8732..34fdf7f53 100644
--- a/src/core/hle/service/frd/frd.cpp
+++ b/src/core/hle/service/frd/frd.cpp
@@ -100,6 +100,18 @@ void GetMyScreenName(Service::Interface* self) {
LOG_WARNING(Service_FRD, "(STUBBED) called");
}
+void SetClientSdkVersion(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ const u32 version = cmd_buff[1];
+
+ self->SetVersion(version);
+
+ LOG_WARNING(Service_FRD, "(STUBBED) called, version: 0x%08X", version);
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+}
+
void Init() {
using namespace Kernel;
diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h
index c8283a7f3..e61940ea0 100644
--- a/src/core/hle/service/frd/frd.h
+++ b/src/core/hle/service/frd/frd.h
@@ -95,6 +95,15 @@ void GetMyFriendKey(Service::Interface* self);
*/
void GetMyScreenName(Service::Interface* self);
+/**
+ * FRD::SetClientSdkVersion service function
+ * Inputs:
+ * 1 : Used SDK Version
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void SetClientSdkVersion(Service::Interface* self);
+
/// Initialize FRD service(s)
void Init();
diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp
index bd1c9c16b..496f29ca9 100644
--- a/src/core/hle/service/frd/frd_u.cpp
+++ b/src/core/hle/service/frd/frd_u.cpp
@@ -58,7 +58,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x002F0040, nullptr, "AllowHalfAwake"},
{0x00300000, nullptr, "GetServerTypes"},
{0x00310082, nullptr, "GetFriendComment"},
- {0x00320042, nullptr, "SetClientSdkVersion"},
+ {0x00320042, SetClientSdkVersion, "SetClientSdkVersion"},
{0x00330000, nullptr, "GetMyApproachContext"},
{0x00340046, nullptr, "AddFriendWithApproach"},
{0x00350082, nullptr, "DecryptApproachContext"},
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 7f9696bfb..bef75f5df 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -15,9 +15,11 @@
#include "common/logging/log.h"
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/archive_extsavedata.h"
+#include "core/file_sys/archive_ncch.h"
+#include "core/file_sys/archive_other_savedata.h"
#include "core/file_sys/archive_savedata.h"
-#include "core/file_sys/archive_savedatacheck.h"
#include "core/file_sys/archive_sdmc.h"
+#include "core/file_sys/archive_sdmcwriteonly.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/directory_backend.h"
#include "core/file_sys/file_backend.h"
@@ -338,17 +340,11 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle,
return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) {
- if (src_archive->RenameFile(src_path, dest_path))
- return RESULT_SUCCESS;
+ return src_archive->RenameFile(src_path, dest_path);
} else {
// TODO: Implement renaming across archives
return UnimplementedFunction(ErrorModule::FS);
}
-
- // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
- // exist or similar. Verify.
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::NothingHappened, ErrorLevel::Status);
}
ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
@@ -356,10 +352,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- if (archive->DeleteDirectory(path))
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return archive->DeleteDirectory(path);
}
ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
@@ -368,10 +361,7 @@ ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- if (archive->DeleteDirectoryRecursively(path))
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return archive->DeleteDirectoryRecursively(path);
}
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path,
@@ -388,10 +378,7 @@ ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- if (archive->CreateDirectory(path))
- return RESULT_SUCCESS;
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return archive->CreateDirectory(path);
}
ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
@@ -404,17 +391,11 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) {
- if (src_archive->RenameDirectory(src_path, dest_path))
- return RESULT_SUCCESS;
+ return src_archive->RenameDirectory(src_path, dest_path);
} else {
// TODO: Implement renaming across archives
return UnimplementedFunction(ErrorModule::FS);
}
-
- // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
- // exist or similar. Verify.
- return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
- ErrorSummary::NothingHappened, ErrorLevel::Status);
}
ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle,
@@ -423,13 +404,11 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
if (archive == nullptr)
return ERR_INVALID_ARCHIVE_HANDLE;
- std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
- if (backend == nullptr) {
- return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound,
- ErrorLevel::Permanent);
- }
+ auto backend = archive->OpenDirectory(path);
+ if (backend.Failed())
+ return backend.Code();
- auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path));
+ auto directory = Kernel::SharedPtr<Directory>(new Directory(backend.MoveFrom(), path));
return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory));
}
@@ -549,9 +528,25 @@ void RegisterArchiveTypes() {
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s",
sdmc_directory.c_str());
+ auto sdmcwo_factory = std::make_unique<FileSys::ArchiveFactory_SDMCWriteOnly>(sdmc_directory);
+ if (sdmcwo_factory->Initialize())
+ RegisterArchiveType(std::move(sdmcwo_factory), ArchiveIdCode::SDMCWriteOnly);
+ else
+ LOG_ERROR(Service_FS, "Can't instantiate SDMCWriteOnly archive with path %s",
+ sdmc_directory.c_str());
+
// Create the SaveData archive
- auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory);
+ auto sd_savedata_source = std::make_shared<FileSys::ArchiveSource_SDSaveData>(sdmc_directory);
+ auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sd_savedata_source);
RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData);
+ auto other_savedata_permitted_factory =
+ std::make_unique<FileSys::ArchiveFactory_OtherSaveDataPermitted>(sd_savedata_source);
+ RegisterArchiveType(std::move(other_savedata_permitted_factory),
+ ArchiveIdCode::OtherSaveDataPermitted);
+ auto other_savedata_general_factory =
+ std::make_unique<FileSys::ArchiveFactory_OtherSaveDataGeneral>(sd_savedata_source);
+ RegisterArchiveType(std::move(other_savedata_general_factory),
+ ArchiveIdCode::OtherSaveDataGeneral);
auto extsavedata_factory =
std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false);
@@ -569,10 +564,9 @@ void RegisterArchiveTypes() {
LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s",
sharedextsavedata_factory->GetMountPoint().c_str());
- // Create the SaveDataCheck archive, basically a small variation of the RomFS archive
- auto savedatacheck_factory =
- std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory);
- RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck);
+ // Create the NCCH archive, basically a small variation of the RomFS archive
+ auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_NCCH>(nand_directory);
+ RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::NCCH);
auto systemsavedata_factory =
std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 41a76285c..87089bd92 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -33,11 +33,13 @@ enum class ArchiveIdCode : u32 {
SystemSaveData = 0x00000008,
SDMC = 0x00000009,
SDMCWriteOnly = 0x0000000A,
- SaveDataCheck = 0x2345678A,
+ NCCH = 0x2345678A,
+ OtherSaveDataGeneral = 0x567890B2,
+ OtherSaveDataPermitted = 0x567890B4,
};
/// Media types for the archives
-enum class MediaType : u32 { NAND = 0, SDMC = 1 };
+enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 };
typedef u64 ArchiveHandle;
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 00edc7622..d6ab5b065 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -763,23 +763,27 @@ static void CreateLegacySystemSaveData(Service::Interface* self) {
* FS_User::InitializeWithSdkVersion service function.
* Inputs:
* 0 : 0x08610042
- * 1 : Unknown
- * 2 : Unknown
- * 3 : Unknown
+ * 1 : Used SDK Version
+ * 2 : ProcessId Header
+ * 3 : placeholder for ProcessId
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void InitializeWithSdkVersion(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 unk1 = cmd_buff[1];
- u32 unk2 = cmd_buff[2];
- u32 unk3 = cmd_buff[3];
+ const u32 version = cmd_buff[1];
+ self->SetVersion(version);
- cmd_buff[1] = RESULT_SUCCESS.raw;
-
- LOG_WARNING(Service_FS, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, unk3=0x%08X", unk1, unk2,
- unk3);
+ if (cmd_buff[2] == IPC::CallingPidDesc()) {
+ LOG_WARNING(Service_FS, "(STUBBED) called, version: 0x%08X", version);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ } else {
+ LOG_ERROR(Service_FS, "ProcessId Header must be 0x20");
+ cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent)
+ .raw;
+ }
}
/**
@@ -999,6 +1003,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08680000, nullptr, "GetMediaType"},
{0x08690000, nullptr, "GetNandEraseCount"},
{0x086A0082, nullptr, "ReadNandReport"},
+ {0x087A0180, nullptr, "AddSeed"},
+ {0x088600C0, nullptr, "CheckUpdatedDat"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 710e0e485..78cb761be 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -346,7 +346,7 @@ static void SetAxiConfigQoSMode(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_GSP, "(STUBBED) called mode=0x%08X", mode);
+ LOG_DEBUG(Service_GSP, "(STUBBED) called mode=0x%08X", mode);
}
/**
diff --git a/src/core/hle/service/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp
index b916dd759..3fdf5cca4 100644
--- a/src/core/hle/service/gsp_lcd.cpp
+++ b/src/core/hle/service/gsp_lcd.cpp
@@ -11,11 +11,15 @@ namespace GSP_LCD {
const Interface::FunctionInfo FunctionTable[] = {
// clang-format off
+ {0x000A0080, nullptr, "SetBrightnessRaw"},
+ {0x000B0080, nullptr, "SetBrightness"},
{0x000F0000, nullptr, "PowerOnAllBacklights"},
{0x00100000, nullptr, "PowerOffAllBacklights"},
{0x00110040, nullptr, "PowerOnBacklight"},
{0x00120040, nullptr, "PowerOffBacklight"},
{0x00130040, nullptr, "SetLedForceOff"},
+ {0x00140000, nullptr, "GetVendor"},
+ {0x00150040, nullptr, "GetBrightness"},
// clang-format on
};
diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp
index 3cf62a4b8..65c7babe5 100644
--- a/src/core/hle/service/http_c.cpp
+++ b/src/core/hle/service/http_c.cpp
@@ -55,6 +55,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x002E0040, nullptr, "DestroyRootCertChain"},
{0x002F0082, nullptr, "RootCertChainAddCert"},
{0x00300080, nullptr, "RootCertChainAddDefaultCert"},
+ {0x00310080, nullptr, "RootCertChainRemoveCert"},
+ {0x00320084, nullptr, "OpenClientCertContext"},
+ {0x00330040, nullptr, "OpenDefaultClientCertContext"},
+ {0x00340040, nullptr, "CloseClientCertContext"},
{0x00350186, nullptr, "SetDefaultProxy"},
{0x00360000, nullptr, "ClearDNSCache"},
{0x00370080, nullptr, "SetKeepAlive"},
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index edd1ea97b..1f851d328 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/mic_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -9,23 +12,316 @@
namespace MIC_U {
+enum class Encoding : u8 {
+ PCM8 = 0,
+ PCM16 = 1,
+ PCM8Signed = 2,
+ PCM16Signed = 3,
+};
+
+enum class SampleRate : u8 {
+ SampleRate32730 = 0,
+ SampleRate16360 = 1,
+ SampleRate10910 = 2,
+ SampleRate8180 = 3
+};
+
+static Kernel::SharedPtr<Kernel::Event> buffer_full_event;
+static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
+static u8 mic_gain = 0;
+static bool mic_power = false;
+static bool is_sampling = false;
+static bool allow_shell_closed;
+static bool clamp = false;
+static Encoding encoding;
+static SampleRate sample_rate;
+static s32 audio_buffer_offset;
+static u32 audio_buffer_size;
+static bool audio_buffer_loop;
+
+/**
+ * MIC::MapSharedMem service function
+ * Inputs:
+ * 0 : Header Code[0x00010042]
+ * 1 : Shared-mem size
+ * 2 : CopyHandleDesc
+ * 3 : Shared-mem handle
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void MapSharedMem(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 size = cmd_buff[1];
+ Handle mem_handle = cmd_buff[3];
+ shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle);
+ if (shared_memory) {
+ shared_memory->name = "MIC_U:shared_memory";
+ }
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "called, size=0x%X, mem_handle=0x%08X", size, mem_handle);
+}
+
+/**
+ * MIC::UnmapSharedMem service function
+ * Inputs:
+ * 0 : Header Code[0x00020000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void UnmapSharedMem(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "called");
+}
+
+/**
+ * MIC::StartSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00030140]
+ * 1 : Encoding
+ * 2 : SampleRate
+ * 3 : Base offset for audio data in sharedmem
+ * 4 : Size of the audio data in sharedmem
+ * 5 : Loop at end of buffer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void StartSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF);
+ sample_rate = static_cast<SampleRate>(cmd_buff[2] & 0xFF);
+ audio_buffer_offset = cmd_buff[3];
+ audio_buffer_size = cmd_buff[4];
+ audio_buffer_loop = static_cast<bool>(cmd_buff[5] & 0xFF);
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ is_sampling = true;
+ LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, "
+ "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u",
+ encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop);
+}
+
+/**
+ * MIC::AdjustSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00040040]
+ * 1 : SampleRate
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void AdjustSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate);
+}
+
+/**
+ * MIC::StopSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00050000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void StopSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ is_sampling = false;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::IsSampling service function
+ * Inputs:
+ * 0 : Header Code[0x00060000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : 0 = sampling, non-zero = sampling
+ */
+static void IsSampling(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = is_sampling;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::GetBufferFullEvent service function
+ * Inputs:
+ * 0 : Header Code[0x00070000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 3 : Event handle
+ */
+static void GetBufferFullEvent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom();
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetGain service function
+ * Inputs:
+ * 0 : Header Code[0x00080040]
+ * 1 : Gain
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetGain(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ mic_gain = cmd_buff[1] & 0xFF;
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain=%u", mic_gain);
+}
+
+/**
+ * MIC::GetGain service function
+ * Inputs:
+ * 0 : Header Code[0x00090000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Gain
+ */
+static void GetGain(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = mic_gain;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetPower service function
+ * Inputs:
+ * 0 : Header Code[0x000A0040]
+ * 1 : Power (0 = off, 1 = on)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetPower(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ mic_power = static_cast<bool>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power=%u", mic_power);
+}
+
+/**
+ * MIC::GetPower service function
+ * Inputs:
+ * 0 : Header Code[0x000B0000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Power
+ */
+static void GetPower(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = mic_power;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetIirFilterMic service function
+ * Inputs:
+ * 0 : Header Code[0x000C0042]
+ * 1 : Size
+ * 2 : (Size << 4) | 0xA
+ * 3 : Pointer to IIR Filter Data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetIirFilterMic(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 size = cmd_buff[1];
+ VAddr buffer = cmd_buff[3];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, size=0x%X, buffer=0x%08X", size, buffer);
+}
+
+/**
+ * MIC::SetClamp service function
+ * Inputs:
+ * 0 : Header Code[0x000D0040]
+ * 1 : Clamp (0 = don't clamp, non-zero = clamp)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetClamp(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ clamp = static_cast<bool>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, clamp=%u", clamp);
+}
+
+/**
+ * MIC::GetClamp service function
+ * Inputs:
+ * 0 : Header Code[0x000E0000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Clamp (0 = don't clamp, non-zero = clamp)
+ */
+static void GetClamp(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = clamp;
+ LOG_WARNING(Service_MIC, "(STUBBED) called");
+}
+
+/**
+ * MIC::SetAllowShellClosed service function
+ * Inputs:
+ * 0 : Header Code[0x000D0040]
+ * 1 : Sampling allowed while shell closed (0 = disallow, non-zero = allow)
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetAllowShellClosed(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ allow_shell_closed = static_cast<bool>(cmd_buff[1] & 0xFF);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed);
+}
+
+/**
+ * MIC_U::SetClientVersion service function
+ * Inputs:
+ * 1 : Used SDK Version
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void SetClientVersion(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ const u32 version = cmd_buff[1];
+ self->SetVersion(version);
+
+ LOG_WARNING(Service_MIC, "(STUBBED) called, version: 0x%08X", version);
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+}
+
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010042, nullptr, "MapSharedMem"},
- {0x00020000, nullptr, "UnmapSharedMem"},
- {0x00030140, nullptr, "Initialize"},
- {0x00040040, nullptr, "AdjustSampling"},
- {0x00050000, nullptr, "StopSampling"},
- {0x00060000, nullptr, "IsSampling"},
- {0x00070000, nullptr, "GetEventHandle"},
- {0x00080040, nullptr, "SetGain"},
- {0x00090000, nullptr, "GetGain"},
- {0x000A0040, nullptr, "SetPower"},
- {0x000B0000, nullptr, "GetPower"},
- {0x000C0042, nullptr, "size"},
- {0x000D0040, nullptr, "SetClamp"},
- {0x000E0000, nullptr, "GetClamp"},
- {0x000F0040, nullptr, "SetAllowShellClosed"},
- {0x00100040, nullptr, "unknown_input2"},
+ {0x00010042, MapSharedMem, "MapSharedMem"},
+ {0x00020000, UnmapSharedMem, "UnmapSharedMem"},
+ {0x00030140, StartSampling, "StartSampling"},
+ {0x00040040, AdjustSampling, "AdjustSampling"},
+ {0x00050000, StopSampling, "StopSampling"},
+ {0x00060000, IsSampling, "IsSampling"},
+ {0x00070000, GetBufferFullEvent, "GetBufferFullEvent"},
+ {0x00080040, SetGain, "SetGain"},
+ {0x00090000, GetGain, "GetGain"},
+ {0x000A0040, SetPower, "SetPower"},
+ {0x000B0000, GetPower, "GetPower"},
+ {0x000C0042, SetIirFilterMic, "SetIirFilterMic"},
+ {0x000D0040, SetClamp, "SetClamp"},
+ {0x000E0000, GetClamp, "GetClamp"},
+ {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"},
+ {0x00100040, SetClientVersion, "SetClientVersion"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,6 +329,18 @@ const Interface::FunctionInfo FunctionTable[] = {
Interface::Interface() {
Register(FunctionTable);
+ shared_memory = nullptr;
+ buffer_full_event =
+ Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event");
+ mic_gain = 0;
+ mic_power = false;
+ is_sampling = false;
+ clamp = false;
+}
+
+Interface::~Interface() {
+ shared_memory = nullptr;
+ buffer_full_event = nullptr;
}
} // namespace
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h
index dc795d14c..1cff7390e 100644
--- a/src/core/hle/service/mic_u.h
+++ b/src/core/hle/service/mic_u.h
@@ -16,6 +16,7 @@ namespace MIC_U {
class Interface : public Service::Interface {
public:
Interface();
+ ~Interface();
std::string GetPortName() const override {
return "mic:u";
diff --git a/src/core/hle/service/mvd/mvd.cpp b/src/core/hle/service/mvd/mvd.cpp
new file mode 100644
index 000000000..9416fe5d6
--- /dev/null
+++ b/src/core/hle/service/mvd/mvd.cpp
@@ -0,0 +1,17 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/mvd/mvd.h"
+#include "core/hle/service/mvd/mvd_std.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace MVD {
+
+void Init() {
+ AddService(new MVD_STD());
+}
+
+} // namespace MVD
+} // namespace Service
diff --git a/src/core/hle/service/mvd/mvd.h b/src/core/hle/service/mvd/mvd.h
new file mode 100644
index 000000000..7b212e839
--- /dev/null
+++ b/src/core/hle/service/mvd/mvd.h
@@ -0,0 +1,14 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service {
+namespace MVD {
+
+/// Initializes all MVD services.
+void Init();
+
+} // namespace MVD
+} // namespace Service
diff --git a/src/core/hle/service/mvd/mvd_std.cpp b/src/core/hle/service/mvd/mvd_std.cpp
new file mode 100644
index 000000000..fd7ca87d3
--- /dev/null
+++ b/src/core/hle/service/mvd/mvd_std.cpp
@@ -0,0 +1,32 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/mvd/mvd_std.h"
+
+namespace Service {
+namespace MVD {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
+ {0x00010082, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Shutdown"},
+ {0x00030300, nullptr, "CalculateWorkBufSize"},
+ {0x000400C0, nullptr, "CalculateImageSize"},
+ {0x00080142, nullptr, "ProcessNALUnit"},
+ {0x00090042, nullptr, "ControlFrameRendering"},
+ {0x000A0000, nullptr, "GetStatus"},
+ {0x000B0000, nullptr, "GetStatusOther"},
+ {0x001D0042, nullptr, "GetConfig"},
+ {0x001E0044, nullptr, "SetConfig"},
+ {0x001F0902, nullptr, "SetOutputBuffer"},
+ {0x00210100, nullptr, "OverrideOutputBuffers"}
+ // clang-format on
+};
+
+MVD_STD::MVD_STD() {
+ Register(FunctionTable);
+}
+
+} // namespace MVD
+} // namespace Service
diff --git a/src/core/hle/service/mvd/mvd_std.h b/src/core/hle/service/mvd/mvd_std.h
new file mode 100644
index 000000000..7db9e2e50
--- /dev/null
+++ b/src/core/hle/service/mvd/mvd_std.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace MVD {
+
+class MVD_STD final : public Interface {
+public:
+ MVD_STD();
+
+ std::string GetPortName() const override {
+ return "mvd:std";
+ }
+};
+
+} // namespace MVD
+} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
new file mode 100644
index 000000000..d9738c6a1
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -0,0 +1,18 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/nfc/nfc.h"
+#include "core/hle/service/nfc/nfc_m.h"
+#include "core/hle/service/nfc/nfc_u.h"
+
+namespace Service {
+namespace NFC {
+
+void Init() {
+ AddService(new NFC_M());
+ AddService(new NFC_U());
+}
+
+} // namespace NFC
+} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h
new file mode 100644
index 000000000..cd65a5fdc
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc.h
@@ -0,0 +1,14 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service {
+namespace NFC {
+
+/// Initialize all NFC services.
+void Init();
+
+} // namespace NFC
+} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp
new file mode 100644
index 000000000..717335c11
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc_m.cpp
@@ -0,0 +1,44 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/nfc/nfc_m.h"
+
+namespace Service {
+namespace NFC {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
+ // nfc:u shared commands
+ {0x00010040, nullptr, "Initialize"},
+ {0x00020040, nullptr, "Shutdown"},
+ {0x00030000, nullptr, "StartCommunication"},
+ {0x00040000, nullptr, "StopCommunication"},
+ {0x00050040, nullptr, "StartTagScanning"},
+ {0x00060000, nullptr, "StopTagScanning"},
+ {0x00070000, nullptr, "LoadAmiiboData"},
+ {0x00080000, nullptr, "ResetTagScanState"},
+ {0x00090002, nullptr, "UpdateStoredAmiiboData"},
+ {0x000D0000, nullptr, "GetTagState"},
+ {0x000F0000, nullptr, "CommunicationGetStatus"},
+ {0x00100000, nullptr, "GetTagInfo2"},
+ {0x00110000, nullptr, "GetTagInfo"},
+ {0x00120000, nullptr, "CommunicationGetResult"},
+ {0x00130040, nullptr, "OpenAppData"},
+ {0x00140384, nullptr, "InitializeWriteAppData"},
+ {0x00150040, nullptr, "ReadAppData"},
+ {0x00160242, nullptr, "WriteAppData"},
+ {0x00170000, nullptr, "GetAmiiboSettings"},
+ {0x00180000, nullptr, "GetAmiiboConfig"},
+ {0x00190000, nullptr, "GetAppDataInitStruct"},
+ // nfc:m
+ {0x04040A40, nullptr, "SetAmiiboSettings"}
+ // clang-format on
+};
+
+NFC_M::NFC_M() {
+ Register(FunctionTable);
+}
+
+} // namespace NFC
+} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_m.h b/src/core/hle/service/nfc/nfc_m.h
new file mode 100644
index 000000000..fae75535b
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc_m.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace NFC {
+
+class NFC_M final : public Interface {
+public:
+ NFC_M();
+
+ std::string GetPortName() const override {
+ return "nfc:m";
+ }
+};
+
+} // namespace NFC
+} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp
new file mode 100644
index 000000000..deffb0b4f
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc_u.cpp
@@ -0,0 +1,41 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/nfc/nfc_u.h"
+
+namespace Service {
+namespace NFC {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
+ {0x00010040, nullptr, "Initialize"},
+ {0x00020040, nullptr, "Shutdown"},
+ {0x00030000, nullptr, "StartCommunication"},
+ {0x00040000, nullptr, "StopCommunication"},
+ {0x00050040, nullptr, "StartTagScanning"},
+ {0x00060000, nullptr, "StopTagScanning"},
+ {0x00070000, nullptr, "LoadAmiiboData"},
+ {0x00080000, nullptr, "ResetTagScanState"},
+ {0x00090002, nullptr, "UpdateStoredAmiiboData"},
+ {0x000D0000, nullptr, "GetTagState"},
+ {0x000F0000, nullptr, "CommunicationGetStatus"},
+ {0x00100000, nullptr, "GetTagInfo2"},
+ {0x00110000, nullptr, "GetTagInfo"},
+ {0x00120000, nullptr, "CommunicationGetResult"},
+ {0x00130040, nullptr, "OpenAppData"},
+ {0x00140384, nullptr, "InitializeWriteAppData"},
+ {0x00150040, nullptr, "ReadAppData"},
+ {0x00160242, nullptr, "WriteAppData"},
+ {0x00170000, nullptr, "GetAmiiboSettings"},
+ {0x00180000, nullptr, "GetAmiiboConfig"},
+ {0x00190000, nullptr, "GetAppDataInitStruct"},
+ // clang-format on
+};
+
+NFC_U::NFC_U() {
+ Register(FunctionTable);
+}
+
+} // namespace NFC
+} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_u.h b/src/core/hle/service/nfc/nfc_u.h
new file mode 100644
index 000000000..eb7507314
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc_u.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace NFC {
+
+class NFC_U final : public Interface {
+public:
+ NFC_U();
+
+ std::string GetPortName() const override {
+ return "nfc:u";
+ }
+};
+
+} // namespace NFC
+} // namespace Service
diff --git a/src/core/hle/service/nim/nim_s.cpp b/src/core/hle/service/nim/nim_s.cpp
index e2ba693c9..28b87e6f7 100644
--- a/src/core/hle/service/nim/nim_s.cpp
+++ b/src/core/hle/service/nim/nim_s.cpp
@@ -10,6 +10,7 @@ namespace NIM {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, nullptr, "CheckSysupdateAvailableSOAP"},
{0x0016020A, nullptr, "ListTitles"},
+ {0x00290000, nullptr, "AccountCheckBalanceSOAP"},
{0x002D0042, nullptr, "DownloadTickets"},
{0x00420240, nullptr, "StartDownload"},
};
diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp
index 7e07d02e8..7664bad60 100644
--- a/src/core/hle/service/nim/nim_u.cpp
+++ b/src/core/hle/service/nim/nim_u.cpp
@@ -15,6 +15,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00050000, nullptr, "CheckForSysUpdateEvent"},
{0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"},
{0x000A0000, nullptr, "GetState"},
+ {0x000B0000, nullptr, "GetSystemTitleHash"},
};
NIM_U_Interface::NIM_U_Interface() {
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp
index 80081aae2..8a2b00614 100644
--- a/src/core/hle/service/nwm_uds.cpp
+++ b/src/core/hle/service/nwm_uds.cpp
@@ -90,7 +90,7 @@ static void RecvBeaconBroadcastData(Service::Interface* self) {
* 2 : Value 0
* 3 : Output handle
*/
-static void Initialize(Service::Interface* self) {
+static void InitializeWithVersion(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 unk1 = cmd_buff[1];
u32 unk2 = cmd_buff[12];
@@ -120,24 +120,26 @@ static void Initialize(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00020000, nullptr, "Scrap"},
{0x00030000, Shutdown, "Shutdown"},
- {0x00040402, nullptr, "CreateNetwork"},
+ {0x00040402, nullptr, "CreateNetwork (deprecated)"},
{0x00050040, nullptr, "EjectClient"},
{0x00060000, nullptr, "EjectSpectator"},
{0x00070080, nullptr, "UpdateNetworkAttribute"},
{0x00080000, nullptr, "DestroyNetwork"},
+ {0x00090442, nullptr, "ConnectNetwork (deprecated)"},
{0x000A0000, nullptr, "DisconnectNetwork"},
{0x000B0000, nullptr, "GetConnectionStatus"},
{0x000D0040, nullptr, "GetNodeInformation"},
+ {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"},
{0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
- {0x00100042, nullptr, "SetBeaconAdditionalData"},
+ {0x00100042, nullptr, "SetApplicationData"},
{0x00110040, nullptr, "GetApplicationData"},
{0x00120100, nullptr, "Bind"},
{0x00130040, nullptr, "Unbind"},
- {0x001400C0, nullptr, "RecvBroadcastDataFrame"},
+ {0x001400C0, nullptr, "PullPacket"},
{0x00150080, nullptr, "SetMaxSendDelay"},
{0x00170182, nullptr, "SendTo"},
{0x001A0000, nullptr, "GetChannel"},
- {0x001B0302, Initialize, "Initialize"},
+ {0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
{0x001D0044, nullptr, "BeginHostingNetwork"},
{0x001E0084, nullptr, "ConnectToNetwork"},
{0x001F0006, nullptr, "DecryptBeaconData"},
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
index 7d91694f6..194d7c40d 100644
--- a/src/core/hle/service/pm_app.cpp
+++ b/src/core/hle/service/pm_app.cpp
@@ -10,18 +10,21 @@
namespace PM_APP {
const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
{0x00010140, nullptr, "LaunchTitle"},
- {0x00020082, nullptr, "LaunchFIRMSetParams"},
- {0x00030080, nullptr, "TerminateProcesse"},
- {0x00040100, nullptr, "TerminateProcessTID"},
- {0x000500C0, nullptr, "TerminateProcessTID_unknown"},
+ {0x00020082, nullptr, "LaunchFIRM"},
+ {0x00030080, nullptr, "TerminateApplication"},
+ {0x00040100, nullptr, "TerminateTitle"},
+ {0x000500C0, nullptr, "TerminateProcess"},
+ {0x00060082, nullptr, "PrepareForReboot"},
{0x00070042, nullptr, "GetFIRMLaunchParams"},
{0x00080100, nullptr, "GetTitleExheaderFlags"},
{0x00090042, nullptr, "SetFIRMLaunchParams"},
- {0x000A0140, nullptr, "SetResourceLimit"},
- {0x000B0140, nullptr, "GetResourceLimitMax"},
+ {0x000A0140, nullptr, "SetAppResourceLimit"},
+ {0x000B0140, nullptr, "GetAppResourceLimit"},
{0x000C0080, nullptr, "UnregisterProcess"},
{0x000D0240, nullptr, "LaunchTitleUpdate"},
+ // clang-format on
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 6e6b63329..4d374ba17 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -6,7 +6,9 @@
#include "core/file_sys/file_backend.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ptm_gets.h"
#include "core/hle/service/ptm/ptm_play.h"
+#include "core/hle/service/ptm/ptm_sets.h"
#include "core/hle/service/ptm/ptm_sysm.h"
#include "core/hle/service/ptm/ptm_u.h"
#include "core/hle/service/service.h"
@@ -81,7 +83,7 @@ void GetTotalStepCount(Service::Interface* self) {
LOG_WARNING(Service_PTM, "(STUBBED) called");
}
-void IsLegacyPowerOff(Service::Interface* self) {
+void GetSoftwareClosedFlag(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -106,9 +108,12 @@ void CheckNew3DS(Service::Interface* self) {
}
void Init() {
- AddService(new PTM_Play_Interface);
- AddService(new PTM_Sysm_Interface);
- AddService(new PTM_U_Interface);
+ AddService(new PTM_Gets);
+ AddService(new PTM_Play);
+ AddService(new PTM_S);
+ AddService(new PTM_Sets);
+ AddService(new PTM_Sysm);
+ AddService(new PTM_U);
shell_open = true;
battery_is_charging = true;
@@ -128,7 +133,7 @@ void Init() {
Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
- FileSys::Path gamecoin_path("gamecoin.dat");
+ FileSys::Path gamecoin_path("/gamecoin.dat");
FileSys::Mode open_mode = {};
open_mode.write_flag.Assign(1);
open_mode.create_flag.Assign(1);
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
index 6e163a6f9..a1a628012 100644
--- a/src/core/hle/service/ptm/ptm.h
+++ b/src/core/hle/service/ptm/ptm.h
@@ -82,12 +82,13 @@ void GetBatteryChargeState(Interface* self);
void GetTotalStepCount(Interface* self);
/**
- * PTM::IsLegacyPowerOff service function
+ * PTM::GetSoftwareClosedFlag service function
* Outputs:
* 1: Result code, 0 on success, otherwise error code
- * 2: Whether the system is going through a power off
+ * 2: Whether or not the "software closed" dialog was requested by the last FIRM
+ * and should be displayed.
*/
-void IsLegacyPowerOff(Interface* self);
+void GetSoftwareClosedFlag(Interface* self);
/**
* PTM::CheckNew3DS service function
diff --git a/src/core/hle/service/ptm/ptm_gets.cpp b/src/core/hle/service/ptm/ptm_gets.cpp
new file mode 100644
index 000000000..b23e508d6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm_gets.cpp
@@ -0,0 +1,37 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ptm_gets.h"
+
+namespace Service {
+namespace PTM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // ptm:u common commands
+ {0x00010002, nullptr, "RegisterAlarmClient"},
+ {0x00020080, nullptr, "SetRtcAlarm"},
+ {0x00030000, nullptr, "GetRtcAlarm"},
+ {0x00040000, nullptr, "CancelRtcAlarm"},
+ {0x00050000, GetAdapterState, "GetAdapterState"},
+ {0x00060000, GetShellState, "GetShellState"},
+ {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
+ {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
+ {0x00090000, nullptr, "GetPedometerState"},
+ {0x000A0042, nullptr, "GetStepHistoryEntry"},
+ {0x000B00C2, nullptr, "GetStepHistory"},
+ {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
+ {0x000D0040, nullptr, "SetPedometerRecordingMode"},
+ {0x000E0000, nullptr, "GetPedometerRecordingMode"},
+ {0x000F0084, nullptr, "GetStepHistoryAll"},
+ // ptm:gets
+ {0x04010000, nullptr, "GetSystemTime"},
+};
+
+PTM_Gets::PTM_Gets() {
+ Register(FunctionTable);
+}
+
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_gets.h b/src/core/hle/service/ptm/ptm_gets.h
new file mode 100644
index 000000000..5552c9eff
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm_gets.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace PTM {
+
+class PTM_Gets final : public Interface {
+public:
+ PTM_Gets();
+
+ std::string GetPortName() const override {
+ return "ptm:gets";
+ }
+};
+
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp
index 2e0c6e1a3..bcb00e0d4 100644
--- a/src/core/hle/service/ptm/ptm_play.cpp
+++ b/src/core/hle/service/ptm/ptm_play.cpp
@@ -2,19 +2,37 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/ptm/ptm_play.h"
namespace Service {
namespace PTM {
const Interface::FunctionInfo FunctionTable[] = {
+ // ptm:u common commands
+ {0x00010002, nullptr, "RegisterAlarmClient"},
+ {0x00020080, nullptr, "SetRtcAlarm"},
+ {0x00030000, nullptr, "GetRtcAlarm"},
+ {0x00040000, nullptr, "CancelRtcAlarm"},
+ {0x00050000, GetAdapterState, "GetAdapterState"},
+ {0x00060000, GetShellState, "GetShellState"},
+ {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
+ {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
+ {0x00090000, nullptr, "GetPedometerState"},
+ {0x000A0042, nullptr, "GetStepHistoryEntry"},
+ {0x000B00C2, nullptr, "GetStepHistory"},
+ {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
+ {0x000D0040, nullptr, "SetPedometerRecordingMode"},
+ {0x000E0000, nullptr, "GetPedometerRecordingMode"},
+ {0x000F0084, nullptr, "GetStepHistoryAll"},
+ // ptm:play
{0x08070082, nullptr, "GetPlayHistory"},
{0x08080000, nullptr, "GetPlayHistoryStart"},
{0x08090000, nullptr, "GetPlayHistoryLength"},
{0x080B0080, nullptr, "CalcPlayHistoryStart"},
};
-PTM_Play_Interface::PTM_Play_Interface() {
+PTM_Play::PTM_Play() {
Register(FunctionTable);
}
diff --git a/src/core/hle/service/ptm/ptm_play.h b/src/core/hle/service/ptm/ptm_play.h
index 47f229581..663faabee 100644
--- a/src/core/hle/service/ptm/ptm_play.h
+++ b/src/core/hle/service/ptm/ptm_play.h
@@ -9,9 +9,9 @@
namespace Service {
namespace PTM {
-class PTM_Play_Interface : public Service::Interface {
+class PTM_Play final : public Interface {
public:
- PTM_Play_Interface();
+ PTM_Play();
std::string GetPortName() const override {
return "ptm:play";
diff --git a/src/core/hle/service/ptm/ptm_sets.cpp b/src/core/hle/service/ptm/ptm_sets.cpp
new file mode 100644
index 000000000..a8c6cf227
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm_sets.cpp
@@ -0,0 +1,20 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/ptm/ptm_sets.h"
+
+namespace Service {
+namespace PTM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // Note that this service does not have access to ptm:u's common commands
+ {0x00010080, nullptr, "SetSystemTime"},
+};
+
+PTM_Sets::PTM_Sets() {
+ Register(FunctionTable);
+}
+
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_sets.h b/src/core/hle/service/ptm/ptm_sets.h
new file mode 100644
index 000000000..d33b047e5
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm_sets.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace PTM {
+
+class PTM_Sets final : public Interface {
+public:
+ PTM_Sets();
+
+ std::string GetPortName() const override {
+ return "ptm:sets";
+ }
+};
+
+} // namespace PTM
+} // namespace Service
diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp
index 693158dbf..f95dfdbb1 100644
--- a/src/core/hle/service/ptm/ptm_sysm.cpp
+++ b/src/core/hle/service/ptm/ptm_sysm.cpp
@@ -9,6 +9,23 @@ namespace Service {
namespace PTM {
const Interface::FunctionInfo FunctionTable[] = {
+ // ptm:u common commands
+ {0x00010002, nullptr, "RegisterAlarmClient"},
+ {0x00020080, nullptr, "SetRtcAlarm"},
+ {0x00030000, nullptr, "GetRtcAlarm"},
+ {0x00040000, nullptr, "CancelRtcAlarm"},
+ {0x00050000, GetAdapterState, "GetAdapterState"},
+ {0x00060000, GetShellState, "GetShellState"},
+ {0x00070000, GetBatteryLevel, "GetBatteryLevel"},
+ {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"},
+ {0x00090000, nullptr, "GetPedometerState"},
+ {0x000A0042, nullptr, "GetStepHistoryEntry"},
+ {0x000B00C2, nullptr, "GetStepHistory"},
+ {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
+ {0x000D0040, nullptr, "SetPedometerRecordingMode"},
+ {0x000E0000, nullptr, "GetPedometerRecordingMode"},
+ {0x000F0084, nullptr, "GetStepHistoryAll"},
+ // ptm:sysm
{0x040100C0, nullptr, "SetRtcAlarmEx"},
{0x04020042, nullptr, "ReplySleepQuery"},
{0x04030042, nullptr, "NotifySleepPreparationComplete"},
@@ -33,8 +50,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x080C0080, nullptr, "SetUserTime"},
{0x080D0000, nullptr, "InvalidateSystemTime"},
{0x080E0140, nullptr, "NotifyPlayEvent"},
- {0x080F0000, IsLegacyPowerOff, "IsLegacyPowerOff"},
- {0x08100000, nullptr, "ClearLegacyPowerOff"},
+ {0x080F0000, GetSoftwareClosedFlag, "GetSoftwareClosedFlag"},
+ {0x08100000, nullptr, "ClearSoftwareClosedFlag"},
{0x08110000, GetShellState, "GetShellState"},
{0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
{0x08130000, nullptr, "FormatSavedata"},
@@ -42,7 +59,11 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08180040, nullptr, "ConfigureNew3DSCPU"},
};
-PTM_Sysm_Interface::PTM_Sysm_Interface() {
+PTM_S::PTM_S() {
+ Register(FunctionTable);
+}
+
+PTM_Sysm::PTM_Sysm() {
Register(FunctionTable);
}
diff --git a/src/core/hle/service/ptm/ptm_sysm.h b/src/core/hle/service/ptm/ptm_sysm.h
index e37f20546..8afcebbba 100644
--- a/src/core/hle/service/ptm/ptm_sysm.h
+++ b/src/core/hle/service/ptm/ptm_sysm.h
@@ -9,9 +9,18 @@
namespace Service {
namespace PTM {
-class PTM_Sysm_Interface : public Interface {
+class PTM_S final : public Interface {
public:
- PTM_Sysm_Interface();
+ PTM_S();
+
+ std::string GetPortName() const override {
+ return "ptm:s";
+ }
+};
+
+class PTM_Sysm final : public Interface {
+public:
+ PTM_Sysm();
std::string GetPortName() const override {
return "ptm:sysm";
diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp
index 65e868393..e0b65ba89 100644
--- a/src/core/hle/service/ptm/ptm_u.cpp
+++ b/src/core/hle/service/ptm/ptm_u.cpp
@@ -26,7 +26,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000F0084, nullptr, "GetStepHistoryAll"},
};
-PTM_U_Interface::PTM_U_Interface() {
+PTM_U::PTM_U() {
Register(FunctionTable);
}
diff --git a/src/core/hle/service/ptm/ptm_u.h b/src/core/hle/service/ptm/ptm_u.h
index bf132f610..7b75d6e49 100644
--- a/src/core/hle/service/ptm/ptm_u.h
+++ b/src/core/hle/service/ptm/ptm_u.h
@@ -9,9 +9,9 @@
namespace Service {
namespace PTM {
-class PTM_U_Interface : public Interface {
+class PTM_U final : public Interface {
public:
- PTM_U_Interface();
+ PTM_U();
std::string GetPortName() const override {
return "ptm:u";
diff --git a/src/core/hle/service/qtm/qtm.cpp b/src/core/hle/service/qtm/qtm.cpp
new file mode 100644
index 000000000..f11542263
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm.cpp
@@ -0,0 +1,21 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/qtm/qtm.h"
+#include "core/hle/service/qtm/qtm_s.h"
+#include "core/hle/service/qtm/qtm_sp.h"
+#include "core/hle/service/qtm/qtm_u.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace QTM {
+
+void Init() {
+ AddService(new QTM_S());
+ AddService(new QTM_SP());
+ AddService(new QTM_U());
+}
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm.h b/src/core/hle/service/qtm/qtm.h
new file mode 100644
index 000000000..33b774c79
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm.h
@@ -0,0 +1,14 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service {
+namespace QTM {
+
+/// Initializes all QTM services.
+void Init();
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_s.cpp b/src/core/hle/service/qtm/qtm_s.cpp
new file mode 100644
index 000000000..ad7df24a0
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm_s.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/qtm/qtm_s.h"
+
+namespace Service {
+namespace QTM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
+ // qtm common commands
+ {0x00010080, nullptr, "GetHeadtrackingInfoRaw"},
+ {0x00020080, nullptr, "GetHeadtrackingInfo"},
+ // clang-format on
+};
+
+QTM_S::QTM_S() {
+ Register(FunctionTable);
+}
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_s.h b/src/core/hle/service/qtm/qtm_s.h
new file mode 100644
index 000000000..e66138ed0
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm_s.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace QTM {
+
+class QTM_S final : public Interface {
+public:
+ QTM_S();
+
+ std::string GetPortName() const override {
+ return "qtm:s";
+ }
+};
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_sp.cpp b/src/core/hle/service/qtm/qtm_sp.cpp
new file mode 100644
index 000000000..6e0695d34
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm_sp.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/qtm/qtm_sp.h"
+
+namespace Service {
+namespace QTM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
+ // qtm common commands
+ {0x00010080, nullptr, "GetHeadtrackingInfoRaw"},
+ {0x00020080, nullptr, "GetHeadtrackingInfo"},
+ // clang-format on
+};
+
+QTM_SP::QTM_SP() {
+ Register(FunctionTable);
+}
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_sp.h b/src/core/hle/service/qtm/qtm_sp.h
new file mode 100644
index 000000000..0ae0618fc
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm_sp.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace QTM {
+
+class QTM_SP final : public Interface {
+public:
+ QTM_SP();
+
+ std::string GetPortName() const override {
+ return "qtm:sp";
+ }
+};
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_u.cpp b/src/core/hle/service/qtm/qtm_u.cpp
new file mode 100644
index 000000000..a0f808432
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm_u.cpp
@@ -0,0 +1,23 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/qtm/qtm_u.h"
+
+namespace Service {
+namespace QTM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ // clang-format off
+ // qtm common commands
+ {0x00010080, nullptr, "GetHeadtrackingInfoRaw"},
+ {0x00020080, nullptr, "GetHeadtrackingInfo"},
+ // clang-format on
+};
+
+QTM_U::QTM_U() {
+ Register(FunctionTable);
+}
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/qtm/qtm_u.h b/src/core/hle/service/qtm/qtm_u.h
new file mode 100644
index 000000000..1ed4c0adc
--- /dev/null
+++ b/src/core/hle/service/qtm/qtm_u.h
@@ -0,0 +1,22 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace QTM {
+
+class QTM_U final : public Interface {
+public:
+ QTM_U();
+
+ std::string GetPortName() const override {
+ return "qtm:u";
+ }
+};
+
+} // namespace QTM
+} // namespace Service
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index ca7eeac8a..407ca4139 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -26,13 +26,16 @@
#include "core/hle/service/ir/ir.h"
#include "core/hle/service/ldr_ro/ldr_ro.h"
#include "core/hle/service/mic_u.h"
+#include "core/hle/service/mvd/mvd.h"
#include "core/hle/service/ndm/ndm.h"
#include "core/hle/service/news/news.h"
+#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/ns_s.h"
#include "core/hle/service/nwm_uds.h"
#include "core/hle/service/pm_app.h"
#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/qtm/qtm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/soc_u.h"
#include "core/hle/service/srv.h"
@@ -109,21 +112,24 @@ void Init() {
AddNamedPort(new SRV::Interface);
AddNamedPort(new ERR_F::Interface);
- Service::FS::ArchiveInit();
- Service::AM::Init();
- Service::APT::Init();
- Service::BOSS::Init();
- Service::CAM::Init();
- Service::CECD::Init();
- Service::CFG::Init();
- Service::DLP::Init();
- Service::FRD::Init();
- Service::HID::Init();
- Service::IR::Init();
- Service::NEWS::Init();
- Service::NDM::Init();
- Service::NIM::Init();
- Service::PTM::Init();
+ FS::ArchiveInit();
+ AM::Init();
+ APT::Init();
+ BOSS::Init();
+ CAM::Init();
+ CECD::Init();
+ CFG::Init();
+ DLP::Init();
+ FRD::Init();
+ HID::Init();
+ IR::Init();
+ MVD::Init();
+ NDM::Init();
+ NEWS::Init();
+ NFC::Init();
+ NIM::Init();
+ PTM::Init();
+ QTM::Init();
AddService(new AC_U::Interface);
AddService(new ACT_A::Interface);
@@ -148,21 +154,21 @@ void Init() {
/// Shutdown ServiceManager
void Shutdown() {
- Service::PTM::Shutdown();
- Service::NDM::Shutdown();
- Service::NIM::Shutdown();
- Service::NEWS::Shutdown();
- Service::IR::Shutdown();
- Service::HID::Shutdown();
- Service::FRD::Shutdown();
- Service::DLP::Shutdown();
- Service::CFG::Shutdown();
- Service::CECD::Shutdown();
- Service::CAM::Shutdown();
- Service::BOSS::Shutdown();
- Service::APT::Shutdown();
- Service::AM::Shutdown();
- Service::FS::ArchiveShutdown();
+ PTM::Shutdown();
+ NIM::Shutdown();
+ NEWS::Shutdown();
+ NDM::Shutdown();
+ IR::Shutdown();
+ HID::Shutdown();
+ FRD::Shutdown();
+ DLP::Shutdown();
+ CFG::Shutdown();
+ CECD::Shutdown();
+ CAM::Shutdown();
+ BOSS::Shutdown();
+ APT::Shutdown();
+ AM::Shutdown();
+ FS::ArchiveShutdown();
g_srv_services.clear();
g_kernel_named_ports.clear();
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 39b5ffaae..29daacfc4 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -29,6 +29,10 @@ public:
return GetPortName();
}
+ virtual void SetVersion(u32 raw_version) {
+ version.raw = raw_version;
+ }
+
typedef void (*Function)(Interface*);
struct FunctionInfo {
@@ -58,6 +62,14 @@ protected:
void Register(const FunctionInfo* functions, size_t n);
+ union {
+ u32 raw;
+ BitField<0, 8, u32> major;
+ BitField<8, 8, u32> minor;
+ BitField<16, 8, u32> build;
+ BitField<24, 8, u32> revision;
+ } version = {};
+
private:
boost::container::flat_map<u32, FunctionInfo> m_functions;
};
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index abab1d271..6d36e5a24 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -66,6 +66,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00050082, nullptr, "AddTrustedRootCA"},
{0x00060080, nullptr, "RootCertChainAddDefaultCert"},
{0x00070080, nullptr, "RootCertChainRemoveCert"},
+ {0x000D0084, nullptr, "OpenClientCertContext"},
{0x000E0040, nullptr, "OpenDefaultClientCertContext"},
{0x000F0040, nullptr, "CloseClientCertContext"},
{0x00110042, GenerateRandomData, "GenerateRandomData"},
@@ -73,10 +74,12 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00130040, nullptr, "StartConnection"},
{0x00140040, nullptr, "StartConnectionGetOut"},
{0x00150082, nullptr, "Read"},
+ {0x00160082, nullptr, "ReadPeek"},
{0x00170082, nullptr, "Write"},
{0x00180080, nullptr, "ContextSetRootCertChain"},
{0x00190080, nullptr, "ContextSetClientCert"},
{0x001B0080, nullptr, "ContextClearOpt"},
+ {0x001C00C4, nullptr, "ContextGetProtocolCipher"},
{0x001E0040, nullptr, "DestroyContext"},
{0x001F0082, nullptr, "ContextInitSharedmem"},
};
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 28cb97d8e..1503b45da 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -8,7 +8,10 @@
#include "common/color.h"
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "common/math_util.h"
#include "common/microprofile.h"
+#include "common/thread.h"
+#include "common/timer.h"
#include "common/vector_math.h"
#include "core/core_timing.h"
#include "core/hle/service/gsp_gpu.h"
@@ -29,16 +32,20 @@ namespace GPU {
Regs g_regs;
-/// True if the current frame was skipped
-bool g_skip_frame;
/// 268MHz CPU clocks / 60Hz frames per second
const u64 frame_ticks = 268123480ull / 60;
/// Event id for CoreTiming
static int vblank_event;
/// Total number of frames drawn
static u64 frame_count;
-/// True if the last frame was skipped
-static bool last_skip_frame;
+/// Start clock for frame limiter
+static u32 time_point;
+/// Total delay caused by slow frames
+static float time_delay;
+constexpr float FIXED_FRAME_TIME = 1000.0f / 60;
+// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
+// values increases time needed to limit frame rate after spikes
+constexpr float MAX_LAG_TIME = 18;
template <typename T>
inline void Read(T& var, const u32 raw_addr) {
@@ -480,8 +487,8 @@ inline void Write(u32 addr, const T data) {
u32* buffer = (u32*)Memory::GetPhysicalPointer(config.GetPhysicalAddress());
if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
- Pica::g_debug_context->recorder->MemoryAccessed(
- (u8*)buffer, config.size * sizeof(u32), config.GetPhysicalAddress());
+ Pica::g_debug_context->recorder->MemoryAccessed((u8*)buffer, config.size,
+ config.GetPhysicalAddress());
}
Pica::CommandProcessor::ProcessCommandList(buffer, config.size);
@@ -516,23 +523,25 @@ template void Write<u32>(u32 addr, const u32 data);
template void Write<u16>(u32 addr, const u16 data);
template void Write<u8>(u32 addr, const u8 data);
+static void FrameLimiter() {
+ time_delay += FIXED_FRAME_TIME;
+ time_delay = MathUtil::Clamp(time_delay, -MAX_LAG_TIME, MAX_LAG_TIME);
+ s32 desired_time = static_cast<s32>(time_delay);
+ s32 elapsed_time = static_cast<s32>(Common::Timer::GetTimeMs() - time_point);
+
+ if (elapsed_time < desired_time) {
+ Common::SleepCurrentThread(desired_time - elapsed_time);
+ }
+
+ u32 frame_time = Common::Timer::GetTimeMs() - time_point;
+
+ time_delay -= frame_time;
+}
+
/// Update hardware
static void VBlankCallback(u64 userdata, int cycles_late) {
frame_count++;
- last_skip_frame = g_skip_frame;
- g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
-
- // Swap buffers based on the frameskip mode, which is a little bit tricky. When
- // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
- // So, we should only swap frames if the last frame was rendered. The rules are:
- // - If frameskip == 0 (disabled), always swap buffers
- // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
- // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
- if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) &&
- last_skip_frame != g_skip_frame) ||
- Settings::values.frame_skip == 0) {
- VideoCore::g_renderer->SwapBuffers();
- }
+ VideoCore::g_renderer->SwapBuffers();
// Signal to GSP that GPU interrupt has occurred
// TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
@@ -545,6 +554,12 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
// Check for user input updates
Service::HID::Update();
+ if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) {
+ FrameLimiter();
+ }
+
+ time_point = Common::Timer::GetTimeMs();
+
// Reschedule recurrent event
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
}
@@ -579,9 +594,8 @@ void Init() {
framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8);
framebuffer_sub.active_fb = 0;
- last_skip_frame = false;
- g_skip_frame = false;
frame_count = 0;
+ time_point = Common::Timer::GetTimeMs();
vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
CoreTiming::ScheduleEvent(frame_ticks, vblank_event);
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 32ddc5697..d53381216 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -316,7 +316,6 @@ ASSERT_REG_POSITION(command_processor_config, 0x00638);
static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set");
extern Regs g_regs;
-extern bool g_skip_frame;
template <typename T>
void Read(T& var, const u32 addr);
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 9652d7ac5..5e3d46638 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -9,6 +9,7 @@
#include <memory>
#include <string>
#include <vector>
+#include <boost/optional.hpp>
#include "common/common_types.h"
#include "common/file_util.h"
@@ -96,6 +97,17 @@ public:
virtual ResultStatus Load() = 0;
/**
+ * Loads the system mode that this application needs.
+ * This function defaults to 2 (96MB allocated to the application) if it can't read the
+ * information.
+ * @returns Optional with the kernel system mode
+ */
+ virtual boost::optional<u32> LoadKernelSystemMode() {
+ // 96MB allocated to the application.
+ return 2;
+ }
+
+ /**
* Get the code (typically .code section) of the application
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index fadd7b16b..d4be61e0e 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -117,6 +117,14 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
return FileType::Error;
}
+boost::optional<u32> AppLoader_NCCH::LoadKernelSystemMode() {
+ if (!is_loaded) {
+ if (LoadExeFS() != ResultStatus::Success)
+ return boost::none;
+ }
+ return exheader_header.arm11_system_local_caps.system_mode.Value();
+}
+
ResultStatus AppLoader_NCCH::LoadExec() {
using Kernel::SharedPtr;
using Kernel::CodeSet;
@@ -277,6 +285,8 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
LOG_DEBUG(Loader, "Core version: %d", core_version);
LOG_DEBUG(Loader, "Thread priority: 0x%X", priority);
LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category);
+ LOG_DEBUG(Loader, "System Mode: %d",
+ exheader_header.arm11_system_local_caps.system_mode);
if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index f8718d063..bcf3ae6e3 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -186,6 +186,12 @@ public:
ResultStatus Load() override;
/**
+ * Loads the Exheader and returns the system mode for this application.
+ * @return Optional with the kernel system mode
+ */
+ boost::optional<u32> LoadKernelSystemMode();
+
+ /**
* Get the code (typically .code section) of the application
* @param buffer Reference to buffer to store data
* @return ResultStatus result of function
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 05f41f798..626e06cd9 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -21,6 +21,7 @@ void Apply() {
VideoCore::g_hw_renderer_enabled = values.use_hw_renderer;
VideoCore::g_shader_jit_enabled = values.use_shader_jit;
VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution;
+ VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
if (VideoCore::g_emu_window) {
auto layout = VideoCore::g_emu_window->GetFramebufferLayout();
diff --git a/src/core/settings.h b/src/core/settings.h
index e931953d7..db4c8fada 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -78,7 +78,6 @@ struct Values {
// Core
bool use_cpu_jit;
- int frame_skip;
// Data Storage
bool use_virtual_sd;
@@ -91,6 +90,7 @@ struct Values {
bool use_shader_jit;
bool use_scaled_resolution;
bool use_vsync;
+ bool toggle_framelimit;
LayoutOption layout_option;
bool swap_screen;
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 7d54811a0..a5f763805 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -17,12 +17,12 @@ namespace System {
static bool is_powered_on{false};
-Result Init(EmuWindow* emu_window) {
+Result Init(EmuWindow* emu_window, u32 system_mode) {
Core::Init();
CoreTiming::Init();
Memory::Init();
HW::Init();
- Kernel::Init();
+ Kernel::Init(system_mode);
HLE::Init();
if (!VideoCore::Init(emu_window)) {
return Result::ErrorInitVideoCore;
diff --git a/src/core/system.h b/src/core/system.h
index 6d672b1b5..b41fc088a 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -15,7 +15,7 @@ enum class Result {
ErrorInitVideoCore, ///< Something went wrong during video core init
};
-Result Init(EmuWindow* emu_window);
+Result Init(EmuWindow* emu_window, u32 system_mode);
bool IsPoweredOn();
void Shutdown();
}
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 457c55571..89237e477 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,5 +1,7 @@
set(SRCS
+ glad.cpp
tests.cpp
+ core/file_sys/path_parser.cpp
)
set(HEADERS
diff --git a/src/tests/core/file_sys/path_parser.cpp b/src/tests/core/file_sys/path_parser.cpp
new file mode 100644
index 000000000..2b543e438
--- /dev/null
+++ b/src/tests/core/file_sys/path_parser.cpp
@@ -0,0 +1,38 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <catch.hpp>
+#include "common/file_util.h"
+#include "core/file_sys/path_parser.h"
+
+namespace FileSys {
+
+TEST_CASE("PathParser", "[core][file_sys]") {
+ REQUIRE(!PathParser(Path(std::vector<u8>{})).IsValid());
+ REQUIRE(!PathParser(Path("a")).IsValid());
+ REQUIRE(!PathParser(Path("/|")).IsValid());
+ REQUIRE(PathParser(Path("/a")).IsValid());
+ REQUIRE(!PathParser(Path("/a/b/../../c/../../d")).IsValid());
+ REQUIRE(PathParser(Path("/a/b/../c/../../d")).IsValid());
+ REQUIRE(PathParser(Path("/")).IsRootDirectory());
+ REQUIRE(!PathParser(Path("/a")).IsRootDirectory());
+ REQUIRE(PathParser(Path("/a/..")).IsRootDirectory());
+}
+
+TEST_CASE("PathParser - Host file system", "[core][file_sys]") {
+ std::string test_dir = "./test";
+ FileUtil::CreateDir(test_dir);
+ FileUtil::CreateDir(test_dir + "/z");
+ FileUtil::CreateEmptyFile(test_dir + "/a");
+
+ REQUIRE(PathParser(Path("/a")).GetHostStatus(test_dir) == PathParser::FileFound);
+ REQUIRE(PathParser(Path("/b")).GetHostStatus(test_dir) == PathParser::NotFound);
+ REQUIRE(PathParser(Path("/z")).GetHostStatus(test_dir) == PathParser::DirectoryFound);
+ REQUIRE(PathParser(Path("/a/c")).GetHostStatus(test_dir) == PathParser::FileInPath);
+ REQUIRE(PathParser(Path("/b/c")).GetHostStatus(test_dir) == PathParser::PathNotFound);
+
+ FileUtil::DeleteDirRecursively(test_dir);
+}
+
+} // namespace FileSys
diff --git a/src/tests/glad.cpp b/src/tests/glad.cpp
new file mode 100644
index 000000000..b0b016440
--- /dev/null
+++ b/src/tests/glad.cpp
@@ -0,0 +1,14 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <catch.hpp>
+#include <glad/glad.h>
+
+// This is not an actual test, but a work-around for issue #2183.
+// If tests uses functions in core but doesn't explicitly use functions in glad, the linker of macOS
+// will error about undefined references from video_core to glad. So we explicitly use a glad
+// function here to shut up the linker.
+TEST_CASE("glad fake test", "[dummy]") {
+ REQUIRE(&gladLoadGL != nullptr);
+}
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index b7c32035e..c80c96762 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -52,10 +52,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
if (id >= regs.NumIds())
return;
- // If we're skipping this frame, only allow trigger IRQ
- if (GPU::g_skip_frame && id != PICA_REG_INDEX(trigger_irq))
- return;
-
// TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value
u32 old_value = regs[id];
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 99bd59a69..b2db609ec 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -40,7 +40,7 @@ namespace Pica {
// field offset. Otherwise, the compiler will fail to compile this code.
#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \
((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \
- size_t>::type) PICA_REG_INDEX(field_name))
+ size_t>::type)PICA_REG_INDEX(field_name))
#endif // _MSC_VER
struct Regs {
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index 6c4bbed33..b9f5d4533 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -562,9 +562,9 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
};
if ((texture.config.wrap_s == Regs::TextureConfig::ClampToBorder &&
- (s < 0 || s >= texture.config.width)) ||
+ (s < 0 || static_cast<u32>(s) >= texture.config.width)) ||
(texture.config.wrap_t == Regs::TextureConfig::ClampToBorder &&
- (t < 0 || t >= texture.config.height))) {
+ (t < 0 || static_cast<u32>(t) >= texture.config.height))) {
auto border_color = texture.config.border_color;
texture_color[i] = {border_color.r, border_color.g, border_color.b,
border_color.a};
@@ -946,8 +946,8 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
// Blend the fog
for (unsigned i = 0; i < 3; i++) {
- combiner_output[i] =
- fog_factor * combiner_output[i] + (1.0f - fog_factor) * fog_color[i];
+ combiner_output[i] = static_cast<u8>(fog_factor * combiner_output[i] +
+ (1.0f - fog_factor) * fog_color[i]);
}
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 7cc3b407a..cc7e782a4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -189,10 +189,6 @@ void RasterizerOpenGL::DrawTriangles() {
GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
(has_stencil && depth_surface != nullptr) ? depth_surface->texture.handle : 0, 0);
- if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- return;
- }
-
// Sync the viewport
// These registers hold half-width and half-height, so must be multiplied by 2
GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2;
@@ -213,12 +209,16 @@ void RasterizerOpenGL::DrawTriangles() {
// Scissor checks are window-, not viewport-relative, which means that if the cached texture
// sub-rect changes, the scissor bounds also need to be updated.
- GLint scissor_x1 = rect.left + regs.scissor_test.x1 * color_surface->res_scale_width;
- GLint scissor_y1 = rect.bottom + regs.scissor_test.y1 * color_surface->res_scale_height;
+ GLint scissor_x1 =
+ static_cast<GLint>(rect.left + regs.scissor_test.x1 * color_surface->res_scale_width);
+ GLint scissor_y1 =
+ static_cast<GLint>(rect.bottom + regs.scissor_test.y1 * color_surface->res_scale_height);
// x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
// scaling or doing multisampling.
- GLint scissor_x2 = rect.left + (regs.scissor_test.x2 + 1) * color_surface->res_scale_width;
- GLint scissor_y2 = rect.bottom + (regs.scissor_test.y2 + 1) * color_surface->res_scale_height;
+ GLint scissor_x2 =
+ static_cast<GLint>(rect.left + (regs.scissor_test.x2 + 1) * color_surface->res_scale_width);
+ GLint scissor_y2 = static_cast<GLint>(
+ rect.bottom + (regs.scissor_test.y2 + 1) * color_surface->res_scale_height);
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
@@ -711,7 +711,11 @@ bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransfe
CachedSurface src_params;
src_params.addr = config.GetPhysicalInputAddress();
- src_params.width = config.output_width;
+ // It's important to use the correct source input width to properly skip over parts of the input
+ // image which will be cropped from the output but still affect the stride of the input image.
+ src_params.width = config.input_width;
+ // Using the output's height is fine because we don't read or skip over the remaining part of
+ // the image, and it allows for smaller texture cache lookup rectangles.
src_params.height = config.output_height;
src_params.is_tiled = !config.input_linear;
src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.input_format);
@@ -732,6 +736,11 @@ bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransfe
return false;
}
+ // Adjust the source rectangle to take into account parts of the input lines being cropped
+ if (config.input_width > config.output_width) {
+ src_rect.right -= (config.input_width - config.output_width) * src_surface->res_scale_width;
+ }
+
// Require destination surface to have same resolution scale as source to preserve scaling
dst_params.res_scale_width = src_surface->res_scale_width;
dst_params.res_scale_height = src_surface->res_scale_height;
@@ -794,10 +803,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
- if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- return false;
- }
-
GLfloat color_values[4] = {0.0f, 0.0f, 0.0f, 0.0f};
// TODO: Handle additional pixel format and fill value size combinations to accelerate more
@@ -882,10 +887,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
dst_surface->texture.handle, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
- if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- return false;
- }
-
GLfloat value_float;
if (dst_surface->pixel_format == CachedSurface::PixelFormat::D16) {
value_float = config.value_32bit / 65535.0f; // 2^16 - 1
@@ -893,7 +894,7 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
value_float = config.value_32bit / 16777215.0f; // 2^24 - 1
}
- cur_state.depth.write_mask = true;
+ cur_state.depth.write_mask = GL_TRUE;
cur_state.Apply();
glClearBufferfv(GL_DEPTH, 0, &value_float);
} else if (dst_type == SurfaceType::DepthStencil) {
@@ -901,15 +902,11 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config)
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
dst_surface->texture.handle, 0);
- if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- return false;
- }
-
GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1
GLint value_int = (config.value_32bit >> 24);
- cur_state.depth.write_mask = true;
- cur_state.stencil.write_mask = true;
+ cur_state.depth.write_mask = GL_TRUE;
+ cur_state.stencil.write_mask = 0xFF;
cur_state.Apply();
glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int);
}
@@ -934,7 +931,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
src_params.addr = framebuffer_addr;
src_params.width = config.width;
src_params.height = config.height;
- src_params.stride = pixel_stride;
+ src_params.pixel_stride = pixel_stride;
src_params.is_tiled = false;
src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.color_format);
@@ -1070,7 +1067,9 @@ void RasterizerOpenGL::SetShader() {
GLint block_size;
glGetActiveUniformBlockiv(current_shader->shader.handle, block_index,
GL_UNIFORM_BLOCK_DATA_SIZE, &block_size);
- ASSERT_MSG(block_size == sizeof(UniformData), "Uniform block size did not match!");
+ ASSERT_MSG(block_size == sizeof(UniformData),
+ "Uniform block size did not match! Got %d, expected %zu",
+ static_cast<int>(block_size), sizeof(UniformData));
glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
// Update uniforms
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 5cbad9b43..0b2e48407 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -103,7 +103,7 @@ static void MortonCopyPixels(CachedSurface::PixelFormat pixel_format, u32 width,
}
}
-bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex,
+void RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex,
CachedSurface::SurfaceType type,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect) {
@@ -158,14 +158,6 @@ bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex,
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
}
- if (OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- return false;
- }
-
- if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- return false;
- }
-
glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
dst_rect.top, dst_rect.right, dst_rect.bottom, buffers,
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
@@ -174,8 +166,6 @@ bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex,
cur_state.draw.read_framebuffer = old_fbs[0];
cur_state.draw.draw_framebuffer = old_fbs[1];
cur_state.Apply();
-
- return true;
}
bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface,
@@ -189,9 +179,9 @@ bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface,
return false;
}
- return BlitTextures(src_surface->texture.handle, dst_surface->texture.handle,
- CachedSurface::GetFormatType(src_surface->pixel_format), src_rect,
- dst_rect);
+ BlitTextures(src_surface->texture.handle, dst_surface->texture.handle,
+ CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, dst_rect);
+ return true;
}
static void AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pixel_format,
@@ -291,6 +281,9 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
MICROPROFILE_SCOPE(OpenGL_SurfaceUpload);
+ // Stride only applies to linear images.
+ ASSERT(params.pixel_stride == 0 || !params.is_tiled);
+
std::shared_ptr<CachedSurface> new_surface = std::make_shared<CachedSurface>();
new_surface->addr = params.addr;
@@ -299,7 +292,7 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
new_surface->texture.Create();
new_surface->width = params.width;
new_surface->height = params.height;
- new_surface->stride = params.stride;
+ new_surface->pixel_stride = params.pixel_stride;
new_surface->res_scale_width = params.res_scale_width;
new_surface->res_scale_height = params.res_scale_height;
@@ -325,14 +318,15 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
cur_state.Apply();
glActiveTexture(GL_TEXTURE0);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->stride);
if (!new_surface->is_tiled) {
// TODO: Ensure this will always be a color format, not a depth or other format
ASSERT((size_t)new_surface->pixel_format < fb_format_tuples.size());
const FormatTuple& tuple = fb_format_tuples[(unsigned int)params.pixel_format];
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->pixel_stride);
glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, 0,
tuple.format, tuple.type, texture_src_data);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
} else {
SurfaceType type = CachedSurface::GetFormatType(new_surface->pixel_format);
if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) {
@@ -391,7 +385,6 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
0, tuple.format, tuple.type, temp_fb_depth_buffer.data());
}
}
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
// If not 1x scale, blit 1x texture to a new scaled texture and replace texture in surface
if (new_surface->res_scale_width != 1.f || new_surface->res_scale_height != 1.f) {
@@ -701,13 +694,14 @@ void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) {
cur_state.Apply();
glActiveTexture(GL_TEXTURE0);
- glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->stride);
if (!surface->is_tiled) {
// TODO: Ensure this will always be a color format, not a depth or other format
ASSERT((size_t)surface->pixel_format < fb_format_tuples.size());
const FormatTuple& tuple = fb_format_tuples[(unsigned int)surface->pixel_format];
+ glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->pixel_stride);
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, dst_buffer);
+ glPixelStorei(GL_PACK_ROW_LENGTH, 0);
} else {
SurfaceType type = CachedSurface::GetFormatType(surface->pixel_format);
if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) {
@@ -750,7 +744,6 @@ void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) {
false);
}
}
- glPixelStorei(GL_PACK_ROW_LENGTH, 0);
surface->dirty = false;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 849530d86..b50e8292b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -171,7 +171,8 @@ struct CachedSurface {
OGLTexture texture;
u32 width;
u32 height;
- u32 stride = 0;
+ /// Stride between lines, in pixels. Only valid for images in linear format.
+ u32 pixel_stride = 0;
float res_scale_width = 1.f;
float res_scale_height = 1.f;
@@ -186,7 +187,7 @@ public:
~RasterizerCacheOpenGL();
/// Blits one texture to another
- bool BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type,
+ void BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect);
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index fe07aa6eb..4da241d83 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -4,6 +4,7 @@
#include <vector>
#include <glad/glad.h>
+#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -31,7 +32,7 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
if (info_log_length > 1) {
std::vector<char> vertex_shader_error(info_log_length);
glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
- if (result) {
+ if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]);
@@ -51,7 +52,7 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
if (info_log_length > 1) {
std::vector<char> fragment_shader_error(info_log_length);
glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
- if (result) {
+ if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s",
@@ -75,13 +76,20 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
if (info_log_length > 1) {
std::vector<char> program_error(info_log_length);
glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error[0]);
- if (result) {
+ if (result == GL_TRUE) {
LOG_DEBUG(Render_OpenGL, "%s", &program_error[0]);
} else {
LOG_ERROR(Render_OpenGL, "Error linking shader:\n%s", &program_error[0]);
}
}
+ // If the program linking failed at least one of the shaders was probably bad
+ if (result == GL_FALSE) {
+ LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader);
+ LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader);
+ }
+ ASSERT_MSG(result == GL_TRUE, "Shader not linked");
+
glDeleteShader(vertex_shader_id);
glDeleteShader(fragment_shader_id);
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ed84cadea..3c03b424a 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -27,8 +27,8 @@ OpenGLState::OpenGLState() {
stencil.test_enabled = false;
stencil.test_func = GL_ALWAYS;
stencil.test_ref = 0;
- stencil.test_mask = -1;
- stencil.write_mask = -1;
+ stencil.test_mask = 0xFF;
+ stencil.write_mask = 0xFF;
stencil.action_depth_fail = GL_KEEP;
stencil.action_depth_pass = GL_KEEP;
stencil.action_stencil_fail = GL_KEEP;
@@ -232,19 +232,6 @@ void OpenGLState::Apply() const {
cur_state = *this;
}
-GLenum OpenGLState::CheckFBStatus(GLenum target) {
- GLenum fb_status = glCheckFramebufferStatus(target);
- if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
- const char* fb_description =
- (target == GL_READ_FRAMEBUFFER ? "READ"
- : (target == GL_DRAW_FRAMEBUFFER ? "DRAW" : "UNK"));
- LOG_CRITICAL(Render_OpenGL, "OpenGL %s framebuffer check failed, status %X", fb_description,
- fb_status);
- }
-
- return fb_status;
-}
-
void OpenGLState::ResetTexture(GLuint handle) {
for (auto& unit : cur_state.texture_units) {
if (unit.texture_2d == handle) {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 01dead883..aee3c2946 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -90,9 +90,6 @@ public:
/// Apply this state as the current OpenGL state
void Apply() const;
- /// Check the status of the current OpenGL read or draw framebuffer configuration
- static GLenum CheckFBStatus(GLenum target);
-
/// Resets and unbinds any references to the given resource in the current OpenGL state
static void ResetTexture(GLuint handle);
static void ResetSampler(GLuint handle);
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp
index 211c703ab..c96110bb2 100644
--- a/src/video_core/shader/shader_jit_x64.cpp
+++ b/src/video_core/shader/shader_jit_x64.cpp
@@ -102,11 +102,11 @@ static const X64Reg SETUP = R9;
/// The two 32-bit VS address offset registers set by the MOVA instruction
static const X64Reg ADDROFFS_REG_0 = R10;
static const X64Reg ADDROFFS_REG_1 = R11;
-/// VS loop count register
+/// VS loop count register (Multiplied by 16)
static const X64Reg LOOPCOUNT_REG = R12;
/// Current VS loop iteration number (we could probably use LOOPCOUNT_REG, but this quicker)
static const X64Reg LOOPCOUNT = RSI;
-/// Number to increment LOOPCOUNT_REG by on each loop iteration
+/// Number to increment LOOPCOUNT_REG by on each loop iteration (Multiplied by 16)
static const X64Reg LOOPINC = RDI;
/// Result of the previous CMP instruction for the X-component comparison
static const X64Reg COND0 = R13;
@@ -491,7 +491,7 @@ void JitShader::Compile_FLR(Instruction instr) {
if (Common::GetCPUCaps().sse4_1) {
ROUNDFLOORPS(SRC1, R(SRC1));
} else {
- CVTPS2DQ(SRC1, R(SRC1));
+ CVTTPS2DQ(SRC1, R(SRC1));
CVTDQ2PS(SRC1, R(SRC1));
}
@@ -718,15 +718,18 @@ void JitShader::Compile_LOOP(Instruction instr) {
looping = true;
+ // This decodes the fields from the integer uniform at index instr.flow_control.int_uniform_id.
+ // The Y (LOOPCOUNT_REG) and Z (LOOPINC) component are kept multiplied by 16 (Left shifted by
+ // 4 bits) to be used as an offset into the 16-byte vector registers later
int offset =
ShaderSetup::UniformOffset(RegisterType::IntUniform, instr.flow_control.int_uniform_id);
MOV(32, R(LOOPCOUNT), MDisp(SETUP, offset));
MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT));
- SHR(32, R(LOOPCOUNT_REG), Imm8(8));
- AND(32, R(LOOPCOUNT_REG), Imm32(0xff)); // Y-component is the start
+ SHR(32, R(LOOPCOUNT_REG), Imm8(4));
+ AND(32, R(LOOPCOUNT_REG), Imm32(0xFF0)); // Y-component is the start
MOV(32, R(LOOPINC), R(LOOPCOUNT));
- SHR(32, R(LOOPINC), Imm8(16));
- MOVZX(32, 8, LOOPINC, R(LOOPINC)); // Z-component is the incrementer
+ SHR(32, R(LOOPINC), Imm8(12));
+ AND(32, R(LOOPINC), Imm32(0xFF0)); // Z-component is the incrementer
MOVZX(32, 8, LOOPCOUNT, R(LOOPCOUNT)); // X-component is iteration count
ADD(32, R(LOOPCOUNT), Imm8(1)); // Iteration count is X-component + 1
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 83e33dfc2..8db882f59 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -21,6 +21,7 @@ std::atomic<bool> g_hw_renderer_enabled;
std::atomic<bool> g_shader_jit_enabled;
std::atomic<bool> g_scaled_resolution_enabled;
std::atomic<bool> g_vsync_enabled;
+std::atomic<bool> g_toggle_framelimit_enabled;
/// Initialize the video core
bool Init(EmuWindow* emu_window) {
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index e2d725ab1..c397c1974 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -38,6 +38,7 @@ extern EmuWindow* g_emu_window; ///< Emu window
extern std::atomic<bool> g_hw_renderer_enabled;
extern std::atomic<bool> g_shader_jit_enabled;
extern std::atomic<bool> g_scaled_resolution_enabled;
+extern std::atomic<bool> g_toggle_framelimit_enabled;
/// Start the video core
void Start();