loadouts: Add a feature to backup/restore custom loadouts

This commit is contained in:
Joel16 2021-04-11 14:32:15 -04:00
parent e781361a6c
commit a8bb47d116
17 changed files with 466 additions and 80 deletions

@ -44,6 +44,8 @@ add_executable(${PROJECT_NAME}
source/applist.cpp
source/fs.cpp
source/gui.cpp
source/keyboard.cpp
source/loadouts.cpp
source/log.cpp
source/main.cpp
source/textures.cpp
@ -84,6 +86,7 @@ vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self
FILE sce_sys/livearea/contents/startup.png sce_sys/livearea/contents/startup.png
FILE sce_sys/livearea/contents/template.xml sce_sys/livearea/contents/template.xml
FILE res/app.png res/app.png
FILE res/db.png res/db.png
FILE res/folder.png res/folder.png
FILE res/splashscreen.png res/splashscreen.png
)

@ -1,5 +1,5 @@
#ifndef _VITA_HB_SORTER_APP_LIST
#define _VITA_HB_SORTER_APP_LIST
#ifndef _VITA_HB_SORTER_APP_LIST_H_
#define _VITA_HB_SORTER_APP_LIST_H_
#include <vector>
#include <string>

@ -1,8 +1,10 @@
#ifndef _VITA_HB_SORTER_FS_H_
#define _VITA_HB_SORTER_FS_H_
#include <psp2/io/dirent.h>
#include <psp2/types.h>
#include <string>
#include <vector>
namespace FS {
bool FileExists(const std::string &path);
@ -11,6 +13,7 @@ namespace FS {
int ReadFile(const std::string &path, unsigned char **buffer, SceOff *size);
int WriteFile(const std::string &path, const void *data, SceSize size);
int RemoveFile(const std::string &path);
int GetDirList(const std::string &path, std::vector<SceIoDirent> &entries);
}
#endif

10
include/keyboard.h Normal file

@ -0,0 +1,10 @@
#ifndef _VITA_HB_SORTER_KEYBOARD_H_
#define _VITA_HB_SORTER_KEYBOARD_H_
#include <string>
namespace Keyboard {
std::string GetText(const std::string &title, const std::string &initial_text);
}
#endif

9
include/loadouts.h Normal file

@ -0,0 +1,9 @@
#ifndef _VITA_HB_SORTER_LOADOUTS_H_
#define _VITA_HB_SORTER_LOADOUTS_H_
namespace Loadouts {
int Backup(void);
int Restore(const char *name);
}
#endif

@ -2,6 +2,7 @@
#define _VITA_HB_SORTER_UTILS_H_
#include <psp2/ctrl.h>
#include <psp2/system_param.h>
/// Checks whether a result code indicates success.
#define R_SUCCEEDED(res) ((res)>=0)
@ -16,6 +17,8 @@ namespace Utils {
int EndAppUtil(void);
int GetEnterButton(void);
int GetCancelButton(void);
int GetDateFormat(void);
void GetDateString(char string[24], SceSystemParamDateFormat format, SceDateTime *time);
}
#endif

BIN
res/db.png Normal file

Binary file not shown.

After

(image error) Size: 9.5 KiB

Binary file not shown.

Before

(image error) Size: 13 KiB

After

(image error) Size: 12 KiB

Binary file not shown.

Before

(image error) Size: 169 KiB

After

(image error) Size: 121 KiB

Binary file not shown.

Before

(image error) Size: 29 KiB

After

(image error) Size: 26 KiB

@ -1,7 +1,10 @@
#include <algorithm>
#include <filesystem>
#include <psp2/io/dirent.h>
#include <psp2/io/fcntl.h>
#include <psp2/io/stat.h>
#include <string>
#include <vector>
#include "log.h"
#include "utils.h"
@ -96,7 +99,7 @@ namespace FS {
int RemoveFile(const std::string &path) {
int ret = 0;
if (R_FAILED(ret = sceIoRemove(path.c_str()))) {
Log::Error("sceIoRemove(%s) failed: 0x%lx\n", path.c_str(), ret);
return ret;
@ -104,4 +107,72 @@ namespace FS {
return 0;
}
static std::string GetFileExt(const std::string &filename) {
std::string ext = std::filesystem::path(filename).extension();
std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper);
return ext;
}
static bool IsDBFile(const std::string &filename) {
std::string ext = FS::GetFileExt(filename);
if (!ext.compare(".DB"))
return true;
return false;
}
static bool Sort(const SceIoDirent &entryA, const SceIoDirent &entryB) {
if ((SCE_S_ISDIR(entryA.d_stat.st_mode)) && !(SCE_S_ISDIR(entryB.d_stat.st_mode)))
return true;
else if (!(SCE_S_ISDIR(entryA.d_stat.st_mode)) && (SCE_S_ISDIR(entryB.d_stat.st_mode)))
return false;
else {
std::u16string entryA_name = reinterpret_cast<const char16_t *>(entryA.d_name);
std::u16string entryB_name = reinterpret_cast<const char16_t *>(entryB.d_name);
std::transform(entryA_name.begin(), entryA_name.end(), entryA_name.begin(), [](unsigned char c){ return std::tolower(c); });
std::transform(entryB_name.begin(), entryB_name.end(), entryB_name.begin(), [](unsigned char c){ return std::tolower(c); });
if (entryA_name.compare(entryB_name) < 0)
return true;
}
return false;
}
int GetDirList(const std::string &path, std::vector<SceIoDirent> &entries) {
int ret = 0, i = 0;
SceUID dir = 0;
entries.clear();
if (R_FAILED(dir = sceIoDopen(path.c_str()))) {
Log::Error("sceIoDopen(%s) failed: 0x%lx\n", path.c_str(), ret);
return dir;
}
do {
SceIoDirent dirent;
if (R_FAILED(ret = sceIoDread(dir, &dirent)))
Log::Error("sceIoDread(%s) failed: 0x%lx\n", path.c_str(), ret);
if (!FS::IsDBFile(dirent.d_name))
continue;
if (SCE_S_ISDIR(dirent.d_stat.st_mode))
continue;
entries.push_back(dirent);
i++;
} while (ret > 0);
std::sort(entries.begin(), entries.end(), FS::Sort);
if (R_FAILED(ret = sceIoDclose(dir))) {
Log::Error("sceIoDclose(%s) failed: 0x%lx\n", path.c_str(), ret);
return ret;
}
return 0;
}
}

@ -7,7 +7,9 @@
#include "fs.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"
#include "loadouts.h"
#include "textures.h"
#include "utils.h"
namespace Renderer {
static void Start(void) {
@ -29,6 +31,7 @@ namespace GUI {
StateNone,
StateConfirm,
StateRestore,
StateLoadoutRestore,
StateDone
};
@ -97,13 +100,29 @@ namespace GUI {
}
}
static void ConfirmRestorePopup(State *state, std::vector<AppInfoIcon> &entries) {
static void ConfirmRestorePopup(State *state, std::vector<AppInfoIcon> &entries, const char *name) {
ImGui::OpenPopup(*state == StateConfirm? "Confirmation" : "Restoration");
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, 20));
ImGui::SetNextWindowPos(ImVec2(480.0f, 272.0f), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal(*state == StateConfirm? "Confirmation" : "Restoration", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text(*state == StateConfirm? "Are you sure you want to apply this sorting method?" : "Are you sure you want to restore this backup?");
switch (*state) {
case StateConfirm:
ImGui::Text("Are you sure you want to apply this sorting method?");
break;
case StateRestore:
ImGui::Text("Are you sure you want to restore this backup?");
break;
case StateLoadoutRestore:
ImGui::Text("Are you sure you want to apply this loadout?");
break;
default:
break;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f));
ImGui::Text("You must reboot your device for the changes to take effect.");
ImGui::Dummy(ImVec2(0.0f, 5.0f));
@ -119,6 +138,8 @@ namespace GUI {
}
else if (*state == StateRestore)
AppList::Restore();
else if (*state == StateLoadoutRestore)
Loadouts::Restore(name);
ImGui::CloseCurrentPopup();
*state = StateDone;
@ -165,10 +186,14 @@ namespace GUI {
int RenderLoop(void) {
bool done = false;
backupExists = FS::FileExists("ux0:/data/VITAHomebrewSorter/backup/app.db");
std::vector<AppInfoIcon> entries;
std::vector<AppInfoIcon> apps;
std::vector<AppInfoPage> pages;
std::vector<AppInfoFolder> folders;
int ret = AppList::Get(entries, pages, folders);
std::vector<SceIoDirent> loadouts;
int ret = AppList::Get(apps, pages, folders);
ret = FS::GetDirList("ux0:data/VITAHomebrewSorter/loadouts", loadouts);
int date_format = Utils::GetDateFormat();
std::string loadout_name;
enum SortMode {
SortDefault,
@ -178,7 +203,8 @@ namespace GUI {
enum IconType {
App,
Folder,
DB,
Folder
};
static SortMode sort = SortDefault;
@ -192,76 +218,130 @@ namespace GUI {
GUI::SetupWindow();
if (ImGui::Begin("VITA Homebrew Sorter", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) {
if (ImGui::RadioButton("Default", sort == SortDefault)) {
sort = SortDefault;
ret = AppList::Get(entries, pages, folders);
}
if (ImGui::BeginTabBar("VITA Homebrew Sorter tabs", ImGuiTabBarFlags_None)) {
if (ImGui::BeginTabItem("Sort/Backup")) {
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
ImGui::SameLine();
if (ImGui::RadioButton("Asc", sort == SortAsc)) {
sort = SortAsc;
ret = AppList::Get(entries, pages, folders);
std::sort(entries.begin(), entries.end(), AppList::SortAlphabeticalAsc);
AppList::Sort(entries, pages, folders);
}
ImGui::SameLine();
if (ImGui::RadioButton("Desc", sort == SortDesc)) {
sort = SortDesc;
ret = AppList::Get(entries, pages, folders);
std::sort(entries.begin(), entries.end(), AppList::SortAlphabeticalDesc);
AppList::Sort(entries, pages, folders);
}
ImGui::SameLine();
DisableButtonInit(sort == SortDefault);
if (ImGui::Button("Apply Sort"))
state = StateConfirm;
DisableButtonExit(sort == SortDefault);
if (backupExists) {
ImGui::SameLine();
if (ImGui::Button("Restore Backup"))
state = StateRestore;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::BeginTable("AppEntries", 5, tableFlags)) {
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("Title");
ImGui::TableSetupColumn("Page ID");
ImGui::TableSetupColumn("Page Number");
ImGui::TableSetupColumn("Position");
ImGui::TableHeadersRow();
for (int i = 0; i < entries.size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Image(reinterpret_cast<ImTextureID>(entries[i].folder? icons[Folder].id : icons[App].id), ImVec2(20, 20));
ImGui::TableNextColumn();
if (ImGui::RadioButton("Default", sort == SortDefault)) {
sort = SortDefault;
ret = AppList::Get(apps, pages, folders);
}
ImGui::Selectable(entries[i].title.c_str(), false, ImGuiSelectableFlags_SpanAllColumns);
ImGui::SameLine();
ImGui::TableNextColumn();
ImGui::Text("%d", entries[i].pageId);
if (ImGui::RadioButton("Asc", sort == SortAsc)) {
sort = SortAsc;
ret = AppList::Get(apps, pages, folders);
std::sort(apps.begin(), apps.end(), AppList::SortAlphabeticalAsc);
AppList::Sort(apps, pages, folders);
}
ImGui::TableNextColumn();
if (entries[i].pageNo < 0)
ImGui::Text("Inside folder");
else
ImGui::Text("%d", entries[i].pageNo);
ImGui::SameLine();
ImGui::TableNextColumn();
ImGui::Text("%d", entries[i].pos);
if (ImGui::RadioButton("Desc", sort == SortDesc)) {
sort = SortDesc;
ret = AppList::Get(apps, pages, folders);
std::sort(apps.begin(), apps.end(), AppList::SortAlphabeticalDesc);
AppList::Sort(apps, pages, folders);
}
ImGui::SameLine();
DisableButtonInit(sort == SortDefault);
if (ImGui::Button("Apply Sort"))
state = StateConfirm;
DisableButtonExit(sort == SortDefault);
if (backupExists) {
ImGui::SameLine();
if (ImGui::Button("Restore Backup"))
state = StateRestore;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::BeginTable("AppList", 5, tableFlags)) {
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("Title");
ImGui::TableSetupColumn("Page ID");
ImGui::TableSetupColumn("Page Number");
ImGui::TableSetupColumn("Position");
ImGui::TableHeadersRow();
for (int i = 0; i < apps.size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Image(reinterpret_cast<ImTextureID>(apps[i].folder? icons[Folder].id : icons[App].id), ImVec2(20, 20));
ImGui::TableNextColumn();
ImGui::Selectable(apps[i].title.c_str(), false, ImGuiSelectableFlags_SpanAllColumns);
ImGui::TableNextColumn();
ImGui::Text("%d", apps[i].pageId);
ImGui::TableNextColumn();
if (apps[i].pageNo < 0)
ImGui::Text("Inside folder");
else
ImGui::Text("%d", apps[i].pageNo);
ImGui::TableNextColumn();
ImGui::Text("%d", apps[i].pos);
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
ImGui::EndTable();
if (ImGui::BeginTabItem("Loadouts")) {
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button("Backup current loadout")) {
if (R_SUCCEEDED(Loadouts::Backup()))
FS::GetDirList("ux0:data/VITAHomebrewSorter/loadouts", loadouts);
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::BeginTable("LoadoutList", 3, tableFlags)) {
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("Title");
ImGui::TableSetupColumn("Date");
ImGui::TableHeadersRow();
if (loadouts.empty()) {
ImGui::Text("No loadouts found");
}
else {
for (int i = 0; i < loadouts.size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Image(reinterpret_cast<ImTextureID>(icons[DB].id), ImVec2(20, 20));
ImGui::TableNextColumn();
if (ImGui::Selectable(loadouts[i].d_name, false, ImGuiSelectableFlags_SpanAllColumns)) {
loadout_name = loadouts[i].d_name;
state = StateLoadoutRestore;
}
ImGui::TableNextColumn();
char date[16];
Utils::GetDateString(date, static_cast<SceSystemParamDateFormat>(date_format), &loadouts[i].d_stat.st_mtime);
ImGui::Text(date);
}
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
@ -272,11 +352,15 @@ namespace GUI {
break;
case StateConfirm:
GUI::ConfirmRestorePopup(&state, entries);
GUI::ConfirmRestorePopup(&state, apps, loadout_name.c_str());
break;
case StateRestore:
GUI::ConfirmRestorePopup(&state, entries);
GUI::ConfirmRestorePopup(&state, apps, loadout_name.c_str());
break;
case StateLoadoutRestore:
GUI::ConfirmRestorePopup(&state, apps, loadout_name.c_str());
break;
case StateDone:

99
source/keyboard.cpp Normal file

@ -0,0 +1,99 @@
#include <codecvt>
#include <cstring>
#include <locale>
#include <psp2/ime_dialog.h>
#include <vitaGL.h>
#include "keyboard.h"
#include "log.h"
#include "utils.h"
/*
Based off of libkdbvita by usineur -> https://github.com/usineur/libkbdvita/blob/master/kbdvita.c
*/
namespace Keyboard {
static bool running = false;
static const int SCE_COMMON_DIALOG_STATUS_CANCELLED = 3;
static uint16_t buffer[SCE_IME_DIALOG_MAX_TEXT_LENGTH];
std::string text = std::string();
int Init(const std::string &title, const std::string &initial_text) {
if (running)
return -1;
// Clear our text buffer
text.clear();
// UTF8 -> UTF16
std::u16string title_u16 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(title.data());
std::u16string initial_text_u16 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(initial_text.data());
SceImeDialogParam param;
sceImeDialogParamInit(&param);
param.supportedLanguages = SCE_IME_LANGUAGE_ENGLISH;
param.languagesForced = SCE_TRUE;
param.type = SCE_IME_TYPE_DEFAULT;
param.option = 0;
param.title = (const SceWChar16 *)title_u16.c_str();
param.maxTextLength = SCE_IME_DIALOG_MAX_TEXT_LENGTH;
param.initialText = (SceWChar16 *)initial_text_u16.c_str();
param.inputTextBuffer = buffer;
int ret = 0;
if (R_FAILED(ret = sceImeDialogInit(&param))) {
Log::Error("sceImeDialogInit failed: 0x%lx\n", ret);
return ret;
}
running = true;
return 0;
}
SceCommonDialogStatus Update(void) {
if (!running)
return SCE_COMMON_DIALOG_STATUS_NONE;
SceCommonDialogStatus status = sceImeDialogGetStatus();
if (status == SCE_COMMON_DIALOG_STATUS_FINISHED) {
SceImeDialogResult result;
std::memset(&result, 0, sizeof(SceImeDialogResult));
sceImeDialogGetResult(&result);
if ((result.button == SCE_IME_DIALOG_BUTTON_CLOSE) || (result.button == SCE_IME_DIALOG_BUTTON_ENTER)) {
std::u16string buffer_u16 = (char16_t *)buffer;
text = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(buffer_u16.data());
}
else
status = (SceCommonDialogStatus)SCE_COMMON_DIALOG_STATUS_CANCELLED;
sceImeDialogTerm();
running = false;
}
return status;
}
std::string GetText(const std::string &title, const std::string &initial_text) {
if (R_FAILED(Init(title, initial_text)))
return std::string();
bool done = false;
do {
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0, 0, 0, 1);
SceCommonDialogStatus status = Update();
if (status == SCE_COMMON_DIALOG_STATUS_FINISHED)
done = true;
else if (status != SCE_COMMON_DIALOG_STATUS_CANCELLED)
done = false;
vglSwapBuffers(GL_TRUE);
} while(!done);
return text;
}
}

51
source/loadouts.cpp Normal file

@ -0,0 +1,51 @@
#include "fs.h"
#include "keyboard.h"
#include "utils.h"
namespace Loadouts {
constexpr char path[] = "ur0:/shell/db/app.db";
int Backup(void) {
std::string filename = Keyboard::GetText("Enter loadout name", "");
if (filename.empty())
return -1;
std::string loadout_path = "ux0:data/VITAHomebrewSorter/loadouts/" + filename + ".db";
unsigned char *data = nullptr;
SceOff size = 0;
int ret = 0;
if (R_FAILED(ret = FS::ReadFile(path, &data, &size)))
return ret;
if (FS::FileExists(loadout_path)) {
if (R_FAILED(ret = FS::RemoveFile(loadout_path)))
return ret;
}
if (R_FAILED(ret = FS::WriteFile(loadout_path, data, size)))
return ret;
return 0;
}
int Restore(const char *name) {
std::string loadout_path = "ux0:data/VITAHomebrewSorter/loadouts/" + std::string(name);
unsigned char *data = nullptr;
SceOff size = 0;
int ret = 0;
if (R_FAILED(ret = FS::ReadFile(loadout_path, &data, &size)))
return ret;
if (FS::FileExists(path)) {
if (R_FAILED(ret = FS::RemoveFile(path)))
return ret;
}
if (R_FAILED(ret = FS::WriteFile(path, data, size)))
return ret;
return 0;
}
}

@ -16,6 +16,8 @@ namespace Log {
sceIoMkdir("ux0:data/VITAHomebrewSorter", 0777);
if (!FS::DirExists("ux0:data/VITAHomebrewSorter/backup"))
sceIoMkdir("ux0:data/VITAHomebrewSorter/backup", 0777);
if (!FS::DirExists("ux0:data/VITAHomebrewSorter/loadouts"))
sceIoMkdir("ux0:data/VITAHomebrewSorter/loadouts", 0777);
if (!FS::FileExists("ux0:data/VITAHomebrewSorter/debug.log"))
FS::CreateFile("ux0:data/VITAHomebrewSorter/debug.log");

@ -66,12 +66,15 @@ namespace Textures {
}
bool Init(void) {
icons.resize(2);
icons.resize(3);
bool ret = Textures::LoadImagePNG("app0:res/app.png", &icons[0]);
IM_ASSERT(ret);
ret = Textures::LoadImagePNG("app0:res/folder.png", &icons[1]);
ret = Textures::LoadImagePNG("app0:res/db.png", &icons[1]);
IM_ASSERT(ret);
ret = Textures::LoadImagePNG("app0:res/folder.png", &icons[2]);
IM_ASSERT(ret);
ret = Textures::LoadImagePNG("app0:res/splashscreen.png", &splash);
@ -81,8 +84,9 @@ namespace Textures {
}
void Exit(void) {
glDeleteTextures(1, &icons[0].id);
glDeleteTextures(1, &icons[1].id);
glDeleteTextures(1, &splash.id);
glDeleteTextures(1, &icons[2].id);
glDeleteTextures(1, &icons[1].id);
glDeleteTextures(1, &icons[0].id);
}
}

@ -1,7 +1,7 @@
#include <psp2/apputil.h>
#include <psp2/kernel/clib.h>
#include <psp2/common_dialog.h>
#include <psp2/system_param.h>
#include <psp2/rtc.h>
#include <psp2/kernel/clib.h>
#include <cstdio>
#include "log.h"
@ -86,4 +86,51 @@ namespace Utils {
return 0;
}
int GetDateFormat(void) {
int format = 0, ret = 0;
if (R_FAILED(ret = sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_DATE_FORMAT, &format))) {
Log::Error("sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_DATE_FORMAT) failed: 0x%lx\n", ret);
return ret;
}
return format;
}
// From VitaShell by TheOfficialFloW -> https://github.com/TheOfficialFloW/VitaShell/blob/master/utils.c#L349
static void ConvertLocalTimeToUTC(SceDateTime *time_utc, SceDateTime *time_local) {
// sceRtcGetTick and other sceRtc functions fails with year > 9999
int year_local = time_local->year;
int year_delta = year_local < 9999 ? 0 : year_local - 9998;
time_local->year -= year_delta;
SceRtcTick tick;
sceRtcGetTick(time_local, &tick);
time_local->year = year_local;
sceRtcConvertLocalTimeToUtc(&tick, &tick);
sceRtcSetTick(time_utc, &tick);
time_utc->year += year_delta;
}
// From VitaShell by TheOfficialFloW -> https://github.com/TheOfficialFloW/VitaShell/blob/master/utils.c#L364
void GetDateString(char string[24], SceSystemParamDateFormat format, SceDateTime *time) {
SceDateTime local_time;
Utils::ConvertLocalTimeToUTC(&local_time, time);
switch (format) {
case SCE_SYSTEM_PARAM_DATE_FORMAT_YYYYMMDD:
std::snprintf(string, 24, "%04d/%02d/%02d", local_time.year, local_time.month, local_time.day);
break;
case SCE_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY:
std::snprintf(string, 24, "%02d/%02d/%04d", local_time.day, local_time.month, local_time.year);
break;
case SCE_SYSTEM_PARAM_DATE_FORMAT_MMDDYYYY:
std::snprintf(string, 24, "%02d/%02d/%04d", local_time.month, local_time.day, local_time.year);
break;
}
}
}