mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-15 22:54:00 +00:00
Merge pull request #1894 from wwylele/set-config-block
Implement config savegame editing & clean up
This commit is contained in:
commit
ffda82eea5
14 changed files with 703 additions and 41 deletions
|
@ -22,6 +22,7 @@ set(SRCS
|
||||||
configure_debug.cpp
|
configure_debug.cpp
|
||||||
configure_dialog.cpp
|
configure_dialog.cpp
|
||||||
configure_general.cpp
|
configure_general.cpp
|
||||||
|
configure_system.cpp
|
||||||
game_list.cpp
|
game_list.cpp
|
||||||
hotkeys.cpp
|
hotkeys.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
@ -52,6 +53,7 @@ set(HEADERS
|
||||||
configure_debug.h
|
configure_debug.h
|
||||||
configure_dialog.h
|
configure_dialog.h
|
||||||
configure_general.h
|
configure_general.h
|
||||||
|
configure_system.h
|
||||||
game_list.h
|
game_list.h
|
||||||
game_list_p.h
|
game_list_p.h
|
||||||
hotkeys.h
|
hotkeys.h
|
||||||
|
@ -69,6 +71,7 @@ set(UIS
|
||||||
configure_audio.ui
|
configure_audio.ui
|
||||||
configure_debug.ui
|
configure_debug.ui
|
||||||
configure_general.ui
|
configure_general.ui
|
||||||
|
configure_system.ui
|
||||||
hotkeys.ui
|
hotkeys.ui
|
||||||
main.ui
|
main.ui
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
<string>General</string>
|
<string>General</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="ConfigureSystem" name="systemTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>System</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="inputTab">
|
<widget class="QWidget" name="inputTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Input</string>
|
<string>Input</string>
|
||||||
|
@ -57,6 +62,12 @@
|
||||||
<header>configure_general.h</header>
|
<header>configure_general.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>ConfigureSystem</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>configure_system.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>ConfigureAudio</class>
|
<class>ConfigureAudio</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
|
|
|
@ -9,9 +9,10 @@
|
||||||
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
ConfigureDialog::ConfigureDialog(QWidget *parent) :
|
ConfigureDialog::ConfigureDialog(QWidget *parent, bool running) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
ui(new Ui::ConfigureDialog)
|
ui(new Ui::ConfigureDialog),
|
||||||
|
emulation_running(running)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
this->setConfiguration();
|
this->setConfiguration();
|
||||||
|
@ -21,10 +22,14 @@ ConfigureDialog::~ConfigureDialog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureDialog::setConfiguration() {
|
void ConfigureDialog::setConfiguration() {
|
||||||
|
// System tab needs set manually
|
||||||
|
// depending on whether emulation is running
|
||||||
|
ui->systemTab->setConfiguration(emulation_running);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureDialog::applyConfiguration() {
|
void ConfigureDialog::applyConfiguration() {
|
||||||
ui->generalTab->applyConfiguration();
|
ui->generalTab->applyConfiguration();
|
||||||
|
ui->systemTab->applyConfiguration();
|
||||||
ui->audioTab->applyConfiguration();
|
ui->audioTab->applyConfiguration();
|
||||||
ui->debugTab->applyConfiguration();
|
ui->debugTab->applyConfiguration();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ConfigureDialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConfigureDialog(QWidget *parent = nullptr);
|
explicit ConfigureDialog(QWidget *parent, bool emulation_running);
|
||||||
~ConfigureDialog();
|
~ConfigureDialog();
|
||||||
|
|
||||||
void applyConfiguration();
|
void applyConfiguration();
|
||||||
|
@ -26,4 +26,5 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::ConfigureDialog> ui;
|
std::unique_ptr<Ui::ConfigureDialog> ui;
|
||||||
|
bool emulation_running;
|
||||||
};
|
};
|
||||||
|
|
136
src/citra_qt/configure_system.cpp
Normal file
136
src/citra_qt/configure_system.cpp
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "citra_qt/configure_system.h"
|
||||||
|
#include "citra_qt/ui_settings.h"
|
||||||
|
#include "ui_configure_system.h"
|
||||||
|
|
||||||
|
#include "core/hle/service/fs/archive.h"
|
||||||
|
#include "core/hle/service/cfg/cfg.h"
|
||||||
|
|
||||||
|
static const std::array<int, 12> days_in_month = {{
|
||||||
|
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||||
|
}};
|
||||||
|
|
||||||
|
ConfigureSystem::ConfigureSystem(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
ui(new Ui::ConfigureSystem) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(ui->combo_birthmonth, SIGNAL(currentIndexChanged(int)), SLOT(updateBirthdayComboBox(int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigureSystem::~ConfigureSystem() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::setConfiguration(bool emulation_running) {
|
||||||
|
enabled = !emulation_running;
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
ReadSystemSettings();
|
||||||
|
ui->group_system_settings->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
// This tab is enabled only when game is not running (i.e. all service are not initialized).
|
||||||
|
// Temporarily register archive types and load the config savegame file to memory.
|
||||||
|
Service::FS::RegisterArchiveTypes();
|
||||||
|
ResultCode result = Service::CFG::LoadConfigNANDSaveFile();
|
||||||
|
Service::FS::UnregisterArchiveTypes();
|
||||||
|
|
||||||
|
if (result.IsError()) {
|
||||||
|
ui->label_disable_info->setText(tr("Failed to load system settings data."));
|
||||||
|
ui->group_system_settings->setEnabled(false);
|
||||||
|
enabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadSystemSettings();
|
||||||
|
ui->label_disable_info->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::ReadSystemSettings() {
|
||||||
|
// set username
|
||||||
|
username = Service::CFG::GetUsername();
|
||||||
|
// ui->edit_username->setText(QString::fromStdU16String(username)); // TODO(wwylele): Use this when we move to Qt 5.5
|
||||||
|
ui->edit_username->setText(QString::fromUtf16(reinterpret_cast<const ushort*>(username.data())));
|
||||||
|
|
||||||
|
// set birthday
|
||||||
|
std::tie(birthmonth, birthday) = Service::CFG::GetBirthday();
|
||||||
|
ui->combo_birthmonth->setCurrentIndex(birthmonth - 1);
|
||||||
|
ui->combo_birthday->setCurrentIndex(birthday - 1);
|
||||||
|
|
||||||
|
// set system language
|
||||||
|
language_index = Service::CFG::GetSystemLanguage();
|
||||||
|
ui->combo_language->setCurrentIndex(language_index);
|
||||||
|
|
||||||
|
// set sound output mode
|
||||||
|
sound_index = Service::CFG::GetSoundOutputMode();
|
||||||
|
ui->combo_sound->setCurrentIndex(sound_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::applyConfiguration() {
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
|
// apply username
|
||||||
|
// std::u16string new_username = ui->edit_username->text().toStdU16String(); // TODO(wwylele): Use this when we move to Qt 5.5
|
||||||
|
std::u16string new_username(reinterpret_cast<const char16_t*>(ui->edit_username->text().utf16()));
|
||||||
|
if (new_username != username) {
|
||||||
|
Service::CFG::SetUsername(new_username);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply birthday
|
||||||
|
int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1;
|
||||||
|
int new_birthday = ui->combo_birthday->currentIndex() + 1;
|
||||||
|
if (birthmonth != new_birthmonth || birthday != new_birthday) {
|
||||||
|
Service::CFG::SetBirthday(new_birthmonth, new_birthday);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply language
|
||||||
|
int new_language = ui->combo_language->currentIndex();
|
||||||
|
if (language_index != new_language) {
|
||||||
|
Service::CFG::SetSystemLanguage(static_cast<Service::CFG::SystemLanguage>(new_language));
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply sound
|
||||||
|
int new_sound = ui->combo_sound->currentIndex();
|
||||||
|
if (sound_index != new_sound) {
|
||||||
|
Service::CFG::SetSoundOutputMode(static_cast<Service::CFG::SoundOutputMode>(new_sound));
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the config savegame if any item is modified.
|
||||||
|
if (modified)
|
||||||
|
Service::CFG::UpdateConfigNANDSavegame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) {
|
||||||
|
if (birthmonth_index < 0 || birthmonth_index >= 12)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// store current day selection
|
||||||
|
int birthday_index = ui->combo_birthday->currentIndex();
|
||||||
|
|
||||||
|
// get number of days in the new selected month
|
||||||
|
int days = days_in_month[birthmonth_index];
|
||||||
|
|
||||||
|
// if the selected day is out of range,
|
||||||
|
// reset it to 1st
|
||||||
|
if (birthday_index < 0 || birthday_index >= days)
|
||||||
|
birthday_index = 0;
|
||||||
|
|
||||||
|
// update the day combo box
|
||||||
|
ui->combo_birthday->clear();
|
||||||
|
for (int i = 1; i <= days; ++i) {
|
||||||
|
ui->combo_birthday->addItem(QString::number(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore the day selection
|
||||||
|
ui->combo_birthday->setCurrentIndex(birthday_index);
|
||||||
|
}
|
38
src/citra_qt/configure_system.h
Normal file
38
src/citra_qt/configure_system.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ConfigureSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigureSystem : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ConfigureSystem(QWidget *parent = nullptr);
|
||||||
|
~ConfigureSystem();
|
||||||
|
|
||||||
|
void applyConfiguration();
|
||||||
|
void setConfiguration(bool emulation_running);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateBirthdayComboBox(int birthmonth_index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ReadSystemSettings();
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::ConfigureSystem> ui;
|
||||||
|
bool enabled;
|
||||||
|
|
||||||
|
std::u16string username;
|
||||||
|
int birthmonth, birthday;
|
||||||
|
int language_index;
|
||||||
|
int sound_index;
|
||||||
|
};
|
252
src/citra_qt/configure_system.ui
Normal file
252
src/citra_qt/configure_system.ui
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ConfigureSystem</class>
|
||||||
|
<widget class="QWidget" name="ConfigureSystem">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>360</width>
|
||||||
|
<height>377</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="group_system_settings">
|
||||||
|
<property name="title">
|
||||||
|
<string>System Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_username">
|
||||||
|
<property name="text">
|
||||||
|
<string>Username</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="edit_username">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maxLength">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_birthday">
|
||||||
|
<property name="text">
|
||||||
|
<string>Birthday</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_birthday2">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="combo_birthmonth">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>January</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>February</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>March</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>April</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>May</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>June</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>July</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>August</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>September</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>October</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>November</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>December</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="combo_birthday"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_language">
|
||||||
|
<property name="text">
|
||||||
|
<string>Language</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="combo_language">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Japanese (日本語)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>English</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>French (français)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>German (Deutsch)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Italian (italiano)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Spanish (español)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Simplified Chinese (简体中文)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Korean (한국어)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Dutch (Nederlands)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Portuguese (português)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Russian (Русский)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Traditional Chinese (正體中文)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_sound">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sound output mode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QComboBox" name="combo_sound">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Mono</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Stereo</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Surround</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_disable_info">
|
||||||
|
<property name="text">
|
||||||
|
<string>System settings are available only when game is not running.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -508,7 +508,7 @@ void GMainWindow::ToggleWindowMode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnConfigure() {
|
void GMainWindow::OnConfigure() {
|
||||||
ConfigureDialog configureDialog(this);
|
ConfigureDialog configureDialog(this, emulation_running);
|
||||||
auto result = configureDialog.exec();
|
auto result = configureDialog.exec();
|
||||||
if (result == QDialog::Accepted)
|
if (result == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,6 +40,20 @@ struct SaveFileConfig {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SaveFileConfig) == 0x455C, "SaveFileConfig header must be exactly 0x455C bytes");
|
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 {
|
struct UsernameBlock {
|
||||||
char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
|
char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
|
||||||
u32 zero;
|
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 u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
|
||||||
static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
|
static const UsernameBlock CONSOLE_USERNAME_BLOCK = { u"CITRA", 0, 0 };
|
||||||
static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
|
static const BirthdayBlock PROFILE_BIRTHDAY = { 3, 25 }; // March 25th, 2014
|
||||||
/// TODO(Subv): Find out what this actually is
|
static const u8 SOUND_OUTPUT_MODE = SOUND_SURROUND;
|
||||||
static const u8 SOUND_OUTPUT_MODE = 2;
|
|
||||||
static const u8 UNITED_STATES_COUNTRY_ID = 49;
|
static const u8 UNITED_STATES_COUNTRY_ID = 49;
|
||||||
/// TODO(Subv): Find what the other bytes are
|
/// TODO(Subv): Find what the other bytes are
|
||||||
static const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID };
|
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());
|
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) {
|
void UpdateConfigNANDSavegame(Service::Interface* self) {
|
||||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
|
cmd_buff[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
|
||||||
|
@ -234,13 +263,13 @@ void FormatConfig(Service::Interface* self) {
|
||||||
cmd_buff[1] = Service::CFG::FormatConfig().raw;
|
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
|
// Read the header
|
||||||
SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
|
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),
|
auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
|
||||||
[&](const SaveConfigBlockEntry& entry) {
|
[&](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)) {
|
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);
|
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) {
|
if (itr->size != size) {
|
||||||
LOG_ERROR(Service_CFG, "Invalid size %u for config block 0x%X with flags %u", size, block_id, flag);
|
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);
|
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
|
// The data is located in the block header itself if the size is less than 4 bytes
|
||||||
if (itr->size <= 4)
|
if (itr->size <= 4)
|
||||||
memcpy(output, &itr->offset_or_data, itr->size);
|
pointer = &itr->offset_or_data;
|
||||||
else
|
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;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,25 +386,25 @@ ResultCode FormatConfig() {
|
||||||
res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer);
|
res = CreateConfigInfoBlk(0x00030001, 0x8, 0xE, zero_buffer);
|
||||||
if (!res.IsSuccess()) return res;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
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;
|
if (!res.IsSuccess()) return res;
|
||||||
|
|
||||||
u16_le country_name_buffer[16][0x40] = {};
|
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]);
|
std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
|
||||||
}
|
}
|
||||||
// 0x000B0001 - Localized names for the profile Country
|
// 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;
|
if (!res.IsSuccess()) return res;
|
||||||
// 0x000B0002 - Localized names for the profile State/Province
|
// 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;
|
if (!res.IsSuccess()) return res;
|
||||||
|
|
||||||
// 0x000B0003 - Unknown, related to country/address (zip code?)
|
// 0x000B0003 - Unknown, related to country/address (zip code?)
|
||||||
|
@ -382,10 +432,10 @@ ResultCode FormatConfig() {
|
||||||
if (!res.IsSuccess()) return res;
|
if (!res.IsSuccess()) return res;
|
||||||
|
|
||||||
// 0x000D0000 - Accepted EULA version
|
// 0x000D0000 - Accepted EULA version
|
||||||
res = CreateConfigInfoBlk(0x000D0000, 0x4, 0xE, zero_buffer);
|
res = CreateConfigInfoBlk(EULAVersionBlockID, 0x4, 0xE, zero_buffer);
|
||||||
if (!res.IsSuccess()) return res;
|
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;
|
if (!res.IsSuccess()) return res;
|
||||||
|
|
||||||
// 0x00170000 - Unknown
|
// 0x00170000 - Unknown
|
||||||
|
@ -399,11 +449,7 @@ ResultCode FormatConfig() {
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init() {
|
ResultCode LoadConfigNANDSaveFile() {
|
||||||
AddService(new CFG_I_Interface);
|
|
||||||
AddService(new CFG_S_Interface);
|
|
||||||
AddService(new CFG_U_Interface);
|
|
||||||
|
|
||||||
// Open the SystemSaveData archive 0x00010017
|
// Open the SystemSaveData archive 0x00010017
|
||||||
FileSys::Path archive_path(cfg_system_savedata_id);
|
FileSys::Path archive_path(cfg_system_savedata_id);
|
||||||
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
|
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
|
||||||
|
@ -431,14 +477,75 @@ void Init() {
|
||||||
if (config_result.Succeeded()) {
|
if (config_result.Succeeded()) {
|
||||||
auto config = config_result.MoveFrom();
|
auto config = config_result.MoveFrom();
|
||||||
config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
|
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 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 CFG
|
||||||
} // namespace Service
|
} // namespace Service
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
@ -35,7 +36,14 @@ enum SystemLanguage {
|
||||||
LANGUAGE_KO = 7,
|
LANGUAGE_KO = 7,
|
||||||
LANGUAGE_NL = 8,
|
LANGUAGE_NL = 8,
|
||||||
LANGUAGE_PT = 9,
|
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
|
/// Block header in the config savedata file
|
||||||
|
@ -177,6 +185,22 @@ void GetConfigInfoBlk2(Service::Interface* self);
|
||||||
*/
|
*/
|
||||||
void GetConfigInfoBlk8(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
|
* CFG::UpdateConfigNANDSavegame service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
|
@ -205,7 +229,19 @@ void FormatConfig(Service::Interface* self);
|
||||||
* @param output A pointer where we will write the read data
|
* @param output A pointer where we will write the read data
|
||||||
* @returns ResultCode indicating the result of the operation, 0 on success
|
* @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.
|
* 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();
|
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
|
/// Initialize the config service
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
/// Shutdown the config service
|
/// Shutdown the config service
|
||||||
void Shutdown();
|
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 CFG
|
||||||
} // namespace Service
|
} // namespace Service
|
||||||
|
|
|
@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
|
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
|
||||||
// cfg:i
|
// cfg:i
|
||||||
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
||||||
{0x04020082, nullptr, "SetConfigInfoBlk4"},
|
{0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
|
||||||
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||||
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
|
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
|
||||||
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
|
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
|
||||||
|
@ -31,7 +31,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x04080042, nullptr, "SecureInfoGetSerialNo"},
|
{0x04080042, nullptr, "SecureInfoGetSerialNo"},
|
||||||
{0x04090000, nullptr, "UpdateConfigBlk00040003"},
|
{0x04090000, nullptr, "UpdateConfigBlk00040003"},
|
||||||
{0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
{0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
||||||
{0x08020082, nullptr, "SetConfigInfoBlk4"},
|
{0x08020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
|
||||||
{0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
{0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||||
{0x080400C2, nullptr, "CreateConfigInfoBlk"},
|
{0x080400C2, nullptr, "CreateConfigInfoBlk"},
|
||||||
{0x08050000, nullptr, "DeleteConfigNANDSavefile"},
|
{0x08050000, nullptr, "DeleteConfigNANDSavefile"},
|
||||||
|
|
|
@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
|
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
|
||||||
// cfg:s
|
// cfg:s
|
||||||
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
|
||||||
{0x04020082, nullptr, "SetConfigInfoBlk4"},
|
{0x04020082, SetConfigInfoBlk4, "SetConfigInfoBlk4"},
|
||||||
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||||
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
|
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
|
||||||
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
|
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
|
||||||
|
|
|
@ -259,7 +259,7 @@ using FileSys::ArchiveFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of registered archives, identified by id code. Once an archive is registered here, it is
|
* 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;
|
static boost::container::flat_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map;
|
||||||
|
|
||||||
|
@ -520,12 +520,7 @@ ResultCode CreateSystemSaveData(u32 high, u32 low) {
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize archives
|
void RegisterArchiveTypes() {
|
||||||
void ArchiveInit() {
|
|
||||||
next_handle = 1;
|
|
||||||
|
|
||||||
AddService(new FS::Interface);
|
|
||||||
|
|
||||||
// TODO(Subv): Add the other archive types (see here for the known types:
|
// TODO(Subv): Add the other archive types (see here for the known types:
|
||||||
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
|
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
|
||||||
|
|
||||||
|
@ -562,10 +557,23 @@ void ArchiveInit() {
|
||||||
RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
|
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
|
/// Shutdown archives
|
||||||
void ArchiveShutdown() {
|
void ArchiveShutdown() {
|
||||||
handle_map.clear();
|
handle_map.clear();
|
||||||
id_code_map.clear();
|
UnregisterArchiveTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
|
|
|
@ -235,5 +235,11 @@ void ArchiveInit();
|
||||||
/// Shutdown archives
|
/// Shutdown archives
|
||||||
void ArchiveShutdown();
|
void ArchiveShutdown();
|
||||||
|
|
||||||
|
/// Register all archive types
|
||||||
|
void RegisterArchiveTypes();
|
||||||
|
|
||||||
|
/// Unregister all archive types
|
||||||
|
void UnregisterArchiveTypes();
|
||||||
|
|
||||||
} // namespace FS
|
} // namespace FS
|
||||||
} // namespace Service
|
} // namespace Service
|
||||||
|
|
Loading…
Reference in a new issue