diff --git a/source/ui/section/action/action.h b/source/ui/section/action/action.h index a5144a6..a3c7554 100644 --- a/source/ui/section/action/action.h +++ b/source/ui/section/action/action.h @@ -27,6 +27,7 @@ void action_install_cdn(ticket_info* info, bool* populated); void action_delete_title(title_info* info, bool* populated); void action_launch_title(title_info* info, bool* populated); +void action_extract_smdh(title_info* info, bool* populated); void action_browse_title_save_data(title_info* info, bool* populated); void action_import_secure_value(title_info* info, bool* populated); void action_export_secure_value(title_info* info, bool* populated); diff --git a/source/ui/section/action/extractsmdh.c b/source/ui/section/action/extractsmdh.c new file mode 100644 index 0000000..351295e --- /dev/null +++ b/source/ui/section/action/extractsmdh.c @@ -0,0 +1,78 @@ +#include +#include + +#include <3ds.h> + +#include "action.h" +#include "../../error.h" +#include "../../info.h" +#include "../../prompt.h" +#include "../../../util.h" +#include "../../../screen.h" + +static void action_extract_smdh_update(ui_view* view, void* data, float* progress, char* text) { + title_info* info = (title_info*) data; + + Result res = 0; + + static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000}; + static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData}; + u32 archivePath[] = {(u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF), info->mediaType, 0x00000000}; + FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}}; + + Handle fileHandle; + if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) { + SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); + if(smdh != NULL) { + u32 bytesRead; + if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { + FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}}; + if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive))) { + if(R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/smdh/"))) { + char pathBuf[64]; + snprintf(pathBuf, 64, "/fbi/smdh/%016llX.smdh", info->titleId); + + FS_Path* fsPath = util_make_path_utf8(pathBuf); + if(fsPath != NULL) { + Handle smdhHandle = 0; + if(R_SUCCEEDED(res = FSUSER_OpenFile(&smdhHandle, sdmcArchive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) { + u32 bytesWritten = 0; + res = FSFILE_Write(smdhHandle, &bytesWritten, 0, smdh, sizeof(SMDH), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME); + FSFILE_Close(smdhHandle); + } + + util_free_path_utf8(fsPath); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + } + + FSUSER_CloseArchive(&sdmcArchive); + } + } + + free(smdh); + } + + FSFILE_Close(fileHandle); + } + + ui_pop(); + info_destroy(view); + + if(R_SUCCEEDED(res)) { + prompt_display("Success", "SMDH extracted.", COLOR_TEXT, false, info, NULL, ui_draw_title_info, NULL); + } else { + error_display_res(NULL, info, ui_draw_title_info, res, "Failed to extract SMDH."); + } +} + +static void action_extract_smdh_onresponse(ui_view* view, void* data, bool response) { + if(response) { + info_display("Extracting SMDH", "", false, data, action_extract_smdh_update, ui_draw_title_info); + } +} + +void action_extract_smdh(title_info* info, bool* populated) { + prompt_display("Confirmation", "Extract SMDH of the selected title?", COLOR_TEXT, true, info, NULL, ui_draw_title_info, action_extract_smdh_onresponse); +} \ No newline at end of file diff --git a/source/ui/section/titles.c b/source/ui/section/titles.c index a88945f..6c63b91 100644 --- a/source/ui/section/titles.c +++ b/source/ui/section/titles.c @@ -12,6 +12,7 @@ static list_item launch_title = {"Launch Title", COLOR_TEXT, action_launch_title}; static list_item delete_title = {"Delete Title", COLOR_TEXT, action_delete_title}; +static list_item extract_smdh = {"Extract SMDH", COLOR_TEXT, action_extract_smdh}; static list_item browse_save_data = {"Browse Save Data", COLOR_TEXT, action_browse_title_save_data}; static list_item import_secure_value = {"Import Secure Value", COLOR_TEXT, action_import_secure_value}; static list_item export_secure_value = {"Export Secure Value", COLOR_TEXT, action_export_secure_value}; @@ -64,6 +65,7 @@ static void titles_action_update(ui_view* view, void* data, linked_list* items, } if(!actionData->info->twl) { + linked_list_add(items, &extract_smdh); linked_list_add(items, &browse_save_data); if(actionData->info->mediaType != MEDIATYPE_GAME_CARD) {