summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/hle/applets/applet.cpp5
-rw-r--r--src/core/hle/applets/erreula.cpp72
-rw-r--r--src/core/hle/applets/erreula.h31
-rw-r--r--src/core/hle/result.h26
-rw-r--r--src/core/hle/service/apt/apt.h8
-rw-r--r--src/core/hle/service/cfg/cfg.cpp155
-rw-r--r--src/core/hle/service/cfg/cfg.h99
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp4
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp2
-rw-r--r--src/core/hle/service/fs/archive.cpp48
-rw-r--r--src/core/hle/service/fs/archive.h6
-rw-r--r--src/core/hle/service/fs/fs_user.cpp53
-rw-r--r--src/core/hle/shared_page.cpp57
-rw-r--r--src/core/hle/shared_page.h5
16 files changed, 508 insertions, 67 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 02d902bb5..0773339a9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -27,6 +27,7 @@ set(SRCS
hle/config_mem.cpp
hle/hle.cpp
hle/applets/applet.cpp
+ hle/applets/erreula.cpp
hle/applets/mii_selector.cpp
hle/applets/swkbd.cpp
hle/kernel/address_arbiter.cpp
@@ -168,6 +169,7 @@ set(HEADERS
hle/function_wrappers.h
hle/hle.h
hle/applets/applet.h
+ hle/applets/erreula.h
hle/applets/mii_selector.h
hle/applets/swkbd.h
hle/kernel/address_arbiter.h
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 64f5b06d9..3d8a7d0c0 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -26,7 +26,7 @@
extern int g_clock_rate_arm11;
inline s64 msToCycles(int ms) {
- return g_clock_rate_arm11 / 1000 * ms;
+ return (s64)g_clock_rate_arm11 / 1000 * ms;
}
inline s64 msToCycles(float ms) {
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp
index 90e134437..ccf35fa07 100644
--- a/src/core/hle/applets/applet.cpp
+++ b/src/core/hle/applets/applet.cpp
@@ -12,6 +12,7 @@
#include "core/core_timing.h"
#include "core/hle/applets/applet.h"
+#include "core/hle/applets/erreula.h"
#include "core/hle/applets/mii_selector.h"
#include "core/hle/applets/swkbd.h"
#include "core/hle/result.h"
@@ -52,6 +53,10 @@ ResultCode Applet::Create(Service::APT::AppletId id) {
case Service::APT::AppletId::Ed2:
applets[id] = std::make_shared<MiiSelector>(id);
break;
+ case Service::APT::AppletId::Error:
+ case Service::APT::AppletId::Error2:
+ applets[id] = std::make_shared<ErrEula>(id);
+ break;
default:
LOG_ERROR(Service_APT, "Could not create applet %u", id);
// TODO(Subv): Find the right error code
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp
new file mode 100644
index 000000000..92a4b2323
--- /dev/null
+++ b/src/core/hle/applets/erreula.cpp
@@ -0,0 +1,72 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/string_util.h"
+
+#include "core/hle/applets/erreula.h"
+#include "core/hle/service/apt/apt.h"
+
+namespace HLE {
+namespace Applets {
+
+ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
+ if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) {
+ LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
+ UNIMPLEMENTED();
+ // TODO(Subv): Find the right error code
+ return ResultCode(-1);
+ }
+
+ // The LibAppJustStarted message contains a buffer with the size of the framebuffer shared memory.
+ // Create the SharedMemory that will hold the framebuffer data
+ Service::APT::CaptureBufferInfo capture_info;
+ ASSERT(sizeof(capture_info) == parameter.buffer.size());
+
+ memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info));
+
+ // TODO: allocated memory never released
+ using Kernel::MemoryPermission;
+ // Allocate a heap block of the required size for this applet.
+ heap_memory = std::make_shared<std::vector<u8>>(capture_info.size);
+ // Create a SharedMemory that directly points to this heap block.
+ framebuffer_memory = Kernel::SharedMemory::CreateForApplet(heap_memory, 0, heap_memory->size(),
+ MemoryPermission::ReadWrite, MemoryPermission::ReadWrite,
+ "ErrEula Memory");
+
+ // Send the response message with the newly created SharedMemory
+ Service::APT::MessageParameter result;
+ result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished);
+ result.buffer.clear();
+ result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
+ result.sender_id = static_cast<u32>(id);
+ result.object = framebuffer_memory;
+
+ Service::APT::SendParameter(result);
+ return RESULT_SUCCESS;
+}
+
+ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parameter) {
+ started = true;
+
+ // TODO(Subv): Set the expected fields in the response buffer before resending it to the application.
+ // TODO(Subv): Reverse the parameter format for the ErrEula applet
+
+ // Let the application know that we're closing
+ Service::APT::MessageParameter message;
+ message.buffer.resize(parameter.buffer.size());
+ std::fill(message.buffer.begin(), message.buffer.end(), 0);
+ message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed);
+ message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
+ message.sender_id = static_cast<u32>(id);
+ Service::APT::SendParameter(message);
+
+ started = false;
+ return RESULT_SUCCESS;
+}
+
+void ErrEula::Update() {
+}
+
+} // namespace Applets
+} // namespace HLE
diff --git a/src/core/hle/applets/erreula.h b/src/core/hle/applets/erreula.h
new file mode 100644
index 000000000..9fe72ae07
--- /dev/null
+++ b/src/core/hle/applets/erreula.h
@@ -0,0 +1,31 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/applets/applet.h"
+#include "core/hle/kernel/shared_memory.h"
+
+namespace HLE {
+namespace Applets {
+
+class ErrEula final : public Applet {
+public:
+ explicit ErrEula(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; }
+
+ /// 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
+} // namespace HLE
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 57dedcb22..268a8dad2 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -20,6 +20,7 @@ enum class ErrorDescription : u32 {
WrongPermission = 46,
OS_InvalidBufferDescriptor = 48,
WrongAddress = 53,
+ FS_ArchiveNotMounted = 101,
FS_NotFound = 120,
FS_AlreadyExists = 190,
FS_InvalidOpenFlags = 230,
@@ -135,15 +136,28 @@ enum class ErrorModule : u32 {
MCU = 72,
NS = 73,
News = 74,
- RO_1 = 75,
+ RO = 75,
GD = 76,
CardSPI = 77,
EC = 78,
- RO_2 = 79,
- WebBrowser = 80,
- Test = 81,
- ENC = 82,
- PIA = 83,
+ WebBrowser = 79,
+ Test = 80,
+ ENC = 81,
+ PIA = 82,
+ ACT = 83,
+ VCTL = 84,
+ OLV = 85,
+ NEIA = 86,
+ NPNS = 87,
+
+ AVD = 90,
+ L2B = 91,
+ MVD = 92,
+ NFC = 93,
+ UART = 94,
+ SPM = 95,
+ QTM = 96,
+ NFP = 97,
Application = 254,
InvalidResult = 255
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 76b3a3807..53cee4867 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -66,6 +66,8 @@ enum class AppletId : u32 {
InstructionManual = 0x115,
Notifications = 0x116,
Miiverse = 0x117,
+ MiiversePost = 0x118,
+ AmiiboSettings = 0x119,
SoftwareKeyboard1 = 0x201,
Ed1 = 0x202,
PnoteApp = 0x204,
@@ -78,6 +80,12 @@ enum class AppletId : u32 {
AnyLibraryApplet = 0x400,
SoftwareKeyboard2 = 0x401,
Ed2 = 0x402,
+ PnoteApp2 = 0x404,
+ SnoteApp2 = 0x405,
+ Error2 = 0x406,
+ Mint2 = 0x407,
+ Extrapad2 = 0x408,
+ Memolib2 = 0x409,
};
enum class StartupArgumentType : u32 {
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index e067db645..a5dc47322 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -40,6 +40,20 @@ struct SaveFileConfig {
};
static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes");
+enum ConfigBlockID {
+ StereoCameraSettingsBlockID = 0x00050005,
+ SoundOutputModeBlockID = 0x00070001,
+ ConsoleUniqueIDBlockID = 0x00090001,
+ UsernameBlockID = 0x000A0000,
+ BirthdayBlockID = 0x000A0001,
+ LanguageBlockID = 0x000A0002,
+ CountryInfoBlockID = 0x000B0000,
+ CountryNameBlockID = 0x000B0001,
+ StateNameBlockID = 0x000B0002,
+ EULAVersionBlockID = 0x000D0000,
+ ConsoleModelBlockID = 0x000F0004,
+};
+
struct UsernameBlock {
char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
u32 zero;
@@ -73,8 +87,7 @@ static const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
-/// TODO(Subv): Find out what this actually is
-static const u8 SOUND_OUTPUT_MODE = 2;
+static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND;
static const u8 UNITED_STATES_COUNTRY_ID = 49;
/// TODO(Subv): Find what the other bytes are
static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID };
@@ -224,6 +237,22 @@ void GetConfigInfoBlk8(Service::Interface* self) {
Memory::WriteBlock(data_pointer, data.data(), data.size());
}
+void SetConfigInfoBlk4(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 block_id = cmd_buff[1];
+ u32 size = cmd_buff[2];
+ VAddr data_pointer = cmd_buff[4];
+
+ if (!Memory::IsValidVirtualAddress(data_pointer)) {
+ cmd_buff[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ std::vector<u8> data(size);
+ Memory::ReadBlock(data_pointer, data.data(), data.size());
+ cmd_buff[1] = Service::CFG::SetConfigInfoBlock(block_id, size, 0x4, data.data()).raw;
+}
+
void UpdateConfigNANDSavegame(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
@@ -234,13 +263,13 @@ void FormatConfig(Service::Interface* self) {
cmd_buff[1] = Service::CFG::FormatConfig().raw;
}
-ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
+static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 flag) {
// Read the header
SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
[&](const SaveConfigBlockEntry& entry) {
- return entry.block_id == block_id && (entry.flags & flag);
+ return entry.block_id == block_id;
});
if (itr == std::end(config->block_entries)) {
@@ -248,17 +277,38 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
return ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
+ if ((itr->flags & flag) == 0) {
+ LOG_ERROR(Service_CFG, "Invalid flag %u for config block 0x%X with size %u", flag, block_id, size);
+ return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+ }
+
if (itr->size != size) {
LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag);
return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent);
}
+ void* pointer;
+
// The data is located in the block header itself if the size is less than 4 bytes
if (itr->size <= 4)
- memcpy(output, &itr->offset_or_data, itr->size);
+ pointer = &itr->offset_or_data;
else
- memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size);
+ pointer = &cfg_config_file_buffer[itr->offset_or_data];
+
+ return MakeResult<void*>(pointer);
+}
+
+ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
+ void* pointer;
+ CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
+ memcpy(output, pointer, size);
+ return RESULT_SUCCESS;
+}
+ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input) {
+ void* pointer;
+ CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
+ memcpy(pointer, input, size);
return RESULT_SUCCESS;
}
@@ -336,25 +386,25 @@ ResultCode FormatConfig() {
res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
+ res = CreateConfigInfoBlk(StereoCameraSettingsBlockID, sizeof(STEREO_CAMERA_SETTINGS), 0xE, STEREO_CAMERA_SETTINGS.data());
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
+ res = CreateConfigInfoBlk(SoundOutputModeBlockID, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
+ res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
+ res = CreateConfigInfoBlk(UsernameBlockID, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x000A0001, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
+ res = CreateConfigInfoBlk(BirthdayBlockID, sizeof(PROFILE_BIRTHDAY), 0xE, &PROFILE_BIRTHDAY);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
+ res = CreateConfigInfoBlk(LanguageBlockID, sizeof(CONSOLE_LANGUAGE), 0xE, &CONSOLE_LANGUAGE);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
+ res = CreateConfigInfoBlk(CountryInfoBlockID, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
if (!res.IsSuccess()) return res;
u16_le country_name_buffer[16][0x40] = {};
@@ -363,10 +413,10 @@ ResultCode FormatConfig() {
std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
}
// 0x000B0001 - Localized names for the profile Country
- res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer);
+ res = CreateConfigInfoBlk(CountryNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer);
if (!res.IsSuccess()) return res;
// 0x000B0002 - Localized names for the profile State/Province
- res = CreateConfigInfoBlk(0x000B0002, sizeof(country_name_buffer), 0xE, country_name_buffer);
+ res = CreateConfigInfoBlk(StateNameBlockID, sizeof(country_name_buffer), 0xE, country_name_buffer);
if (!res.IsSuccess()) return res;
// 0x000B0003 - Unknown, related to country/address (zip code?)
@@ -382,10 +432,10 @@ ResultCode FormatConfig() {
if (!res.IsSuccess()) return res;
// 0x000D0000 - Accepted EULA version
- res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer);
+ res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer);
if (!res.IsSuccess()) return res;
- res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL);
+ res = CreateConfigInfoBlk(ConsoleModelBlockID, sizeof(CONSOLE_MODEL), 0xC, &CONSOLE_MODEL);
if (!res.IsSuccess()) return res;
// 0x00170000 - Unknown
@@ -399,11 +449,7 @@ ResultCode FormatConfig() {
return RESULT_SUCCESS;
}
-void Init() {
- AddService(new CFG_I_Interface);
- AddService(new CFG_S_Interface);
- AddService(new CFG_U_Interface);
-
+ResultCode LoadConfigNANDSaveFile() {
// Open the SystemSaveData archive 0x00010017
FileSys::Path archive_path(cfg_system_savedata_id);
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
@@ -431,14 +477,75 @@ void Init() {
if (config_result.Succeeded()) {
auto config = config_result.MoveFrom();
config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
- return;
+ return RESULT_SUCCESS;
}
- FormatConfig();
+ return FormatConfig();
+}
+
+void Init() {
+ AddService(new CFG_I_Interface);
+ AddService(new CFG_S_Interface);
+ AddService(new CFG_U_Interface);
+
+ LoadConfigNANDSaveFile();
}
void Shutdown() {
}
+void SetUsername(const std::u16string& name) {
+ ASSERT(name.size() <= 10);
+ UsernameBlock block{};
+ name.copy(block.username, name.size());
+ SetConfigInfoBlock(UsernameBlockID, sizeof(block), 4, &block);
+}
+
+std::u16string GetUsername() {
+ UsernameBlock block;
+ GetConfigInfoBlock(UsernameBlockID, sizeof(block), 8, &block);
+
+ // the username string in the block isn't null-terminated,
+ // so we need to find the end manually.
+ std::u16string username(block.username, ARRAY_SIZE(block.username));
+ const size_t pos = username.find(u'\0');
+ if (pos != std::u16string::npos)
+ username.erase(pos);
+ return username;
+}
+
+void SetBirthday(u8 month, u8 day) {
+ BirthdayBlock block = { month, day };
+ SetConfigInfoBlock(BirthdayBlockID, sizeof(block), 4, &block);
+}
+
+std::tuple<u8, u8> GetBirthday() {
+ BirthdayBlock block;
+ GetConfigInfoBlock(BirthdayBlockID, sizeof(block), 8, &block);
+ return std::make_tuple(block.month, block.day);
+}
+
+void SetSystemLanguage(SystemLanguage language) {
+ u8 block = language;
+ SetConfigInfoBlock(LanguageBlockID, sizeof(block), 4, &block);
+}
+
+SystemLanguage GetSystemLanguage() {
+ u8 block;
+ GetConfigInfoBlock(LanguageBlockID, sizeof(block), 8, &block);
+ return static_cast<SystemLanguage>(block);
+}
+
+void SetSoundOutputMode(SoundOutputMode mode) {
+ u8 block = mode;
+ SetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 4, &block);
+}
+
+SoundOutputMode GetSoundOutputMode() {
+ u8 block;
+ GetConfigInfoBlock(SoundOutputModeBlockID, sizeof(block), 8, &block);
+ return static_cast<SoundOutputMode>(block);
+}
+
} // namespace CFG
} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index c01806836..18f60f4ca 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include <string>
#include "common/common_types.h"
@@ -35,7 +36,14 @@ enum SystemLanguage {
LANGUAGE_KO = 7,
LANGUAGE_NL = 8,
LANGUAGE_PT = 9,
- LANGUAGE_RU = 10
+ LANGUAGE_RU = 10,
+ LANGUAGE_TW = 11
+};
+
+enum SoundOutputMode {
+ SOUND_MONO = 0,
+ SOUND_STEREO = 1,
+ SOUND_SURROUND = 2
};
/// Block header in the config savedata file
@@ -178,6 +186,22 @@ void GetConfigInfoBlk2(Service::Interface* self);
void GetConfigInfoBlk8(Service::Interface* self);
/**
+ * CFG::SetConfigInfoBlk4 service function
+ * Inputs:
+ * 0 : 0x04020082 / 0x08020082
+ * 1 : Block ID
+ * 2 : Size
+ * 3 : Descriptor for the output buffer
+ * 4 : Output buffer pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * Note:
+ * The parameters order is different from GetConfigInfoBlk2/8's,
+ * where Block ID and Size are switched.
+ */
+void SetConfigInfoBlk4(Service::Interface* self);
+
+/**
* CFG::UpdateConfigNANDSavegame service function
* Inputs:
* 0 : 0x04030000 / 0x08030000
@@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self);
* @param output A pointer where we will write the read data
* @returns ResultCode indicating the result of the operation, 0 on success
*/
-ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output);
+ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output);
+
+/**
+ * Reads data from input and writes to a block with the specified id and flag
+ * in the Config savegame buffer.
+ * The input size must match exactly the size of the target block
+ * @param block_id The id of the block we want to write
+ * @param size The size of the block we want to write
+ * @param flag The target block must have this flag set
+ * @param input A pointer where we will read data and write to Config savegame buffer
+ * @returns ResultCode indicating the result of the operation, 0 on success
+ */
+ResultCode SetConfigInfoBlock(u32 block_id, u32 size, u32 flag, const void* input);
/**
* Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory.
@@ -236,11 +272,70 @@ ResultCode UpdateConfigNANDSavegame();
*/
ResultCode FormatConfig();
+/**
+ * Open the config savegame file and load it to the memory buffer
+ * @returns ResultCode indicating the result of the operation, 0 on success
+ */
+ResultCode LoadConfigNANDSaveFile();
+
/// Initialize the config service
void Init();
/// Shutdown the config service
void Shutdown();
+// Utilities for frontend to set config data.
+// Note: before calling these functions, LoadConfigNANDSaveFile should be called,
+// and UpdateConfigNANDSavegame should be called after making changes to config data.
+
+/**
+ * Sets the username in config savegame.
+ * @param name the username to set. The maximum size is 10 in char16_t.
+ */
+void SetUsername(const std::u16string& name);
+
+/**
+ * Gets the username from config savegame.
+ * @returns the username
+ */
+std::u16string GetUsername();
+
+/**
+ * Sets the profile birthday in config savegame.
+ * @param month the month of birthday.
+ * @param day the day of the birthday.
+ */
+void SetBirthday(u8 month, u8 day);
+
+/**
+ * Gets the profile birthday from the config savegame.
+ * @returns a tuple of (month, day) of birthday
+ */
+std::tuple<u8, u8> GetBirthday();
+
+/**
+ * Sets the system language in config savegame.
+ * @param language the system language to set.
+ */
+void SetSystemLanguage(SystemLanguage language);
+
+/**
+ * Gets the system language from config savegame.
+ * @returns the system language
+ */
+SystemLanguage GetSystemLanguage();
+
+/**
+ * Sets the sound output mode in config savegame.
+ * @param mode the sound output mode to set
+ */
+void SetSoundOutputMode(SoundOutputMode mode);
+
+/**
+ * Gets the sound output mode from config savegame.
+ * @returns the sound output mode
+ */
+SoundOutputMode GetSoundOutputMode();
+
} // namespace CFG
} // namespace Service
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp
index b18060f6d..8b0db785f 100644
--- a/src/core/hle/service/cfg/cfg_i.cpp
+++ b/src/core/hle/service/cfg/cfg_i.cpp
@@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
// cfg:i
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
- {0x04020082, nullptr, "SetConfigInfoBlk4"},
+ {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
@@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x04080042, nullptr, "SecureInfoGetSerialNo"},
{0x04090000, nullptr, "UpdateConfigBlk00040003"},
{0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
- {0x08020082, nullptr, "SetConfigInfoBlk4"},
+ {0x08020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
{0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x080400C2, nullptr, "CreateConfigInfoBlk"},
{0x08050000, nullptr, "DeleteConfigNANDSavefile"},
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
index e001f7687..12b458783 100644
--- a/src/core/hle/service/cfg/cfg_s.cpp
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
// cfg:s
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
- {0x04020082, nullptr, "SetConfigInfoBlk4"},
+ {0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 81b9abe4c..4c7aaa7f2 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -58,6 +58,10 @@ namespace FS {
const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+/// Returned when a function is passed an invalid archive handle.
+const ResultCode ERR_INVALID_ARCHIVE_HANDLE(ErrorDescription::FS_ArchiveNotMounted, ErrorModule::FS,
+ ErrorSummary::NotFound, ErrorLevel::Status); // 0xC8804465
+
// Command to access archive file
enum class FileCommand : u32 {
Dummy1 = 0x000100C6,
@@ -255,7 +259,7 @@ using FileSys::ArchiveFactory;
/**
* Map of registered archives, identified by id code. Once an archive is registered here, it is
- * never removed until the FS service is shut down.
+ * never removed until UnregisterArchiveTypes is called.
*/
static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map;
@@ -292,7 +296,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi
ResultCode CloseArchive(ArchiveHandle handle) {
if (handle_map.erase(handle) == 0)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
else
return RESULT_SUCCESS;
}
@@ -314,7 +318,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
const FileSys::Path& path, const FileSys::Mode mode) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
auto backend = archive->OpenFile(path, mode);
if (backend.Failed())
@@ -327,7 +331,7 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
return archive->DeleteFile(path);
}
@@ -337,7 +341,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil
ArchiveBackend* src_archive = GetArchive(src_archive_handle);
ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
if (src_archive == nullptr || dest_archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) {
if (src_archive->RenameFile(src_path, dest_path))
@@ -356,7 +360,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil
ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
if (archive->DeleteDirectory(path))
return RESULT_SUCCESS;
@@ -367,7 +371,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
return archive->CreateFile(path, file_size);
}
@@ -375,7 +379,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path
ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
if (archive->CreateDirectory(path))
return RESULT_SUCCESS;
@@ -388,7 +392,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
ArchiveBackend* src_archive = GetArchive(src_archive_handle);
ArchiveBackend* dest_archive = GetArchive(dest_archive_handle);
if (src_archive == nullptr || dest_archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
if (src_archive == dest_archive) {
if (src_archive->RenameDirectory(src_path, dest_path))
@@ -408,7 +412,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
const FileSys::Path& path) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
if (backend == nullptr) {
@@ -423,7 +427,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
ArchiveBackend* archive = GetArchive(archive_handle);
if (archive == nullptr)
- return ERR_INVALID_HANDLE;
+ return ERR_INVALID_ARCHIVE_HANDLE;
return MakeResult<u64>(archive->GetFreeBytes());
}
@@ -516,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) {
return RESULT_SUCCESS;
}
-/// Initialize archives
-void ArchiveInit() {
- next_handle = 1;
-
- AddService(new FS::Interface);
-
+void RegisterArchiveTypes() {
// TODO(Subv): Add the other archive types (see here for the known types:
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
@@ -558,10 +557,23 @@ void ArchiveInit() {
RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
}
+void UnregisterArchiveTypes() {
+ id_code_map.clear();
+}
+
+/// Initialize archives
+void ArchiveInit() {
+ next_handle = 1;
+
+ AddService(new FS::Interface);
+
+ RegisterArchiveTypes();
+}
+
/// Shutdown archives
void ArchiveShutdown() {
handle_map.clear();
- id_code_map.clear();
+ UnregisterArchiveTypes();
}
} // namespace FS
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 006606740..f7a50a3a7 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -235,5 +235,11 @@ void ArchiveInit();
/// Shutdown archives
void ArchiveShutdown();
+/// Register all archive types
+void RegisterArchiveTypes();
+
+/// Unregister all archive types
+void UnregisterArchiveTypes();
+
} // namespace FS
} // namespace Service
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 7df7da5a4..937868747 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -645,20 +645,19 @@ static void DeleteSystemSaveData(Service::Interface* self) {
* FS_User::CreateSystemSaveData service function.
* Inputs:
* 0 : 0x08560240
- * 1 : High word of the SystemSaveData id to create
- * 2 : Low word of the SystemSaveData id to create
- * 3 : Unknown
- * 4 : Unknown
- * 5 : Unknown
- * 6 : Unknown
- * 7 : Unknown
- * 8 : Unknown
- * 9 : Unknown (Memory address)
+ * 1 : u8 MediaType of the system save data
+ * 2 : SystemSaveData id to create
+ * 3 : Total size
+ * 4 : Block size
+ * 5 : Number of directories
+ * 6 : Number of files
+ * 7 : Directory bucket count
+ * 8 : File bucket count
+ * 9 : u8 Whether to duplicate data or not
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void CreateSystemSaveData(Service::Interface* self) {
- // TODO(Subv): Figure out the other parameters.
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 savedata_high = cmd_buff[1];
u32 savedata_low = cmd_buff[2];
@@ -672,6 +671,38 @@ static void CreateSystemSaveData(Service::Interface* self) {
}
/**
+ * FS_User::CreateLegacySystemSaveData service function.
+ * This function appears to be obsolete and seems to have been replaced by
+ * command 0x08560240 (CreateSystemSaveData).
+ *
+ * Inputs:
+ * 0 : 0x08100200
+ * 1 : SystemSaveData id to create
+ * 2 : Total size
+ * 3 : Block size
+ * 4 : Number of directories
+ * 5 : Number of files
+ * 6 : Directory bucket count
+ * 7 : File bucket count
+ * 8 : u8 Duplicate data
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void CreateLegacySystemSaveData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 savedata_id = cmd_buff[1];
+
+ LOG_WARNING(Service_FS, "(STUBBED) savedata_id=%08X cmd_buff[3]=%08X "
+ "cmd_buff[4]=%08X cmd_buff[5]=%08X cmd_buff[6]=%08X cmd_buff[7]=%08X cmd_buff[8]=%08X "
+ "cmd_buff[9]=%08X", savedata_id, cmd_buff[3], cmd_buff[4], cmd_buff[5],
+ cmd_buff[6], cmd_buff[7], cmd_buff[8], cmd_buff[9]);
+
+ cmd_buff[0] = IPC::MakeHeader(0x810, 0x1, 0);
+ // With this command, the SystemSaveData always has save_high = 0 (Always created in the NAND)
+ cmd_buff[1] = CreateSystemSaveData(0, savedata_id).raw;
+}
+
+/**
* FS_User::InitializeWithSdkVersion service function.
* Inputs:
* 0 : 0x08610042
@@ -820,7 +851,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x080D0144, nullptr, "ControlArchive"},
{0x080E0080, CloseArchive, "CloseArchive"},
{0x080F0180, FormatThisUserSaveData, "FormatThisUserSaveData"},
- {0x08100200, nullptr, "CreateSystemSaveData"},
+ {0x08100200, CreateLegacySystemSaveData, "CreateLegacySystemSaveData"},
{0x08110040, nullptr, "DeleteSystemSaveData"},
{0x08120080, GetFreeBytes, "GetFreeBytes"},
{0x08130000, nullptr, "GetCardType"},
diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp
index 2a1caeaac..4d9272923 100644
--- a/src/core/hle/shared_page.cpp
+++ b/src/core/hle/shared_page.cpp
@@ -2,8 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <chrono>
#include <cstring>
+#include <ctime>
+#include "core/core_timing.h"
#include "core/hle/shared_page.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,6 +15,57 @@ namespace SharedPage {
SharedPageDef shared_page;
+static int update_time_event;
+
+/// Gets system time in 3DS format. The epoch is Jan 1900, and the unit is millisecond.
+static u64 GetSystemTime() {
+ auto now = std::chrono::system_clock::now();
+
+ // 3DS system does't allow user to set a time before Jan 1 2000,
+ // so we use it as an auxiliary epoch to calculate the console time.
+ std::tm epoch_tm;
+ epoch_tm.tm_sec = 0;
+ epoch_tm.tm_min = 0;
+ epoch_tm.tm_hour = 0;
+ epoch_tm.tm_mday = 1;
+ epoch_tm.tm_mon = 0;
+ epoch_tm.tm_year = 100;
+ epoch_tm.tm_isdst = 0;
+ auto epoch = std::chrono::system_clock::from_time_t(std::mktime(&epoch_tm));
+
+ // 3DS console time uses Jan 1 1900 as internal epoch,
+ // so we use the milliseconds between 1900 and 2000 as base console time
+ u64 console_time = 3155673600000ULL;
+
+ // Only when system time is after 2000, we set it as 3DS system time
+ if (now > epoch) {
+ console_time += std::chrono::duration_cast<std::chrono::milliseconds>(now - epoch).count();
+ }
+
+ // If the system time is in daylight saving, we give an additional hour to console time
+ std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
+ std::tm* now_tm = std::localtime(&now_time_t);
+ if (now_tm && now_tm->tm_isdst > 0)
+ console_time += 60 * 60 * 1000;
+
+ return console_time;
+}
+
+static void UpdateTimeCallback(u64 userdata, int cycles_late) {
+ DateTime& date_time = shared_page.date_time_counter % 2 ?
+ shared_page.date_time_0 : shared_page.date_time_1;
+
+ date_time.date_time = GetSystemTime();
+ date_time.update_tick = CoreTiming::GetTicks();
+ date_time.tick_to_second_coefficient = g_clock_rate_arm11;
+ date_time.tick_offset = 0;
+
+ ++shared_page.date_time_counter;
+
+ // system time is updated hourly
+ CoreTiming::ScheduleEvent(msToCycles(60 * 60 * 1000) - cycles_late, update_time_event);
+}
+
void Init() {
std::memset(&shared_page, 0, sizeof(shared_page));
@@ -19,6 +73,9 @@ void Init() {
// Some games wait until this value becomes 0x1, before asking running_hw
shared_page.unknown_value = 0x1;
+
+ update_time_event = CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback);
+ CoreTiming::ScheduleEvent(0, update_time_event);
}
} // namespace
diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h
index 35a07c685..cd9246726 100644
--- a/src/core/hle/shared_page.h
+++ b/src/core/hle/shared_page.h
@@ -25,13 +25,14 @@ namespace SharedPage {
struct DateTime {
u64_le date_time; // 0
u64_le update_tick; // 8
- INSERT_PADDING_BYTES(0x20 - 0x10); // 10
+ u64_le tick_to_second_coefficient; // 10
+ u64_le tick_offset; // 18
};
static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong");
struct SharedPageDef {
// Most of these names are taken from the 3dbrew page linked above.
- u32_le date_time_selector; // 0
+ u32_le date_time_counter; // 0
u8 running_hw; // 4
/// "Microcontroller hardware info"
u8 mcu_hw_info; // 5