mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-06 03:58:02 +08:00
Add NAND browsing and dumping, fix system save data ID size, cancel population on list exit.
This commit is contained in:
parent
1f7f33390d
commit
c1021e327e
@ -1 +1 @@
|
||||
Subproject commit 3547b54340ed59f670dfe6e97abc19f2c66fc999
|
||||
Subproject commit b26c7e3ffa4c368de99fe47e9b294ca47264edf7
|
@ -6,6 +6,7 @@
|
||||
#include "screen.h"
|
||||
#include "util.h"
|
||||
#include "libkhax/khax.h"
|
||||
#include "patcher/patcher.h"
|
||||
#include "ui/mainmenu.h"
|
||||
#include "ui/section/action/clipboard.h"
|
||||
#include "ui/section/task/task.h"
|
||||
@ -44,6 +45,8 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
patch_fs();
|
||||
|
||||
aptOpenSession();
|
||||
Result setCpuTimeRes = APT_SetAppCpuTimeLimit(30);
|
||||
aptCloseSession();
|
||||
|
111
source/patcher/patcher.c
Normal file
111
source/patcher/patcher.c
Normal file
@ -0,0 +1,111 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include "patcher.h"
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct KBlockInfo {
|
||||
u32 section_start;
|
||||
u32 page_count;
|
||||
} KBlockInfo;
|
||||
|
||||
typedef struct KLinkedListNode {
|
||||
struct KLinkedListNode* next;
|
||||
struct KLinkedListNode* prev;
|
||||
void* data;
|
||||
} KLinkedListNode;
|
||||
|
||||
typedef struct MemSectionInfo {
|
||||
u8 padding[0x0C - 0x00];
|
||||
KLinkedListNode* first_node;
|
||||
KLinkedListNode* last_node;
|
||||
} MemSectionInfo;
|
||||
|
||||
typedef struct KCodeSet {
|
||||
u8 padding0[0x08 - 0x00];
|
||||
MemSectionInfo text_info;
|
||||
u8 padding1[0x64 - 0x1C];
|
||||
} KCodeSet;
|
||||
#pragma pack(0)
|
||||
|
||||
static u32 kernel_version = 0;
|
||||
static bool n3ds = false;
|
||||
|
||||
s32 kernel_patch_fs() {
|
||||
asm volatile("cpsid aif");
|
||||
|
||||
u32 processSize = 0;
|
||||
u32 processCodeSetOffset = 0;
|
||||
u32 processPidOffset = 0;
|
||||
|
||||
if(kernel_version < 0x022C0600) {
|
||||
processSize = 0x260;
|
||||
processCodeSetOffset = 0xA8;
|
||||
processPidOffset = 0xAC;
|
||||
} else if(n3ds) {
|
||||
processSize = 0x270;
|
||||
processCodeSetOffset = 0xB8;
|
||||
processPidOffset = 0xBC;
|
||||
} else {
|
||||
processSize = 0x268;
|
||||
processCodeSetOffset = 0xB0;
|
||||
processPidOffset = 0xB4;
|
||||
}
|
||||
|
||||
u32 currProcessPtr = *(u32*) 0xFFFF9004;
|
||||
u32 vtablePtr = *(u32*) currProcessPtr;
|
||||
|
||||
for(u32 processPtr = currProcessPtr; *(u32*) processPtr == vtablePtr; processPtr -= processSize) {
|
||||
if(*(u32*) (processPtr + processPidOffset) == 0) {
|
||||
KCodeSet* codeSet = *(KCodeSet**) (processPtr + processCodeSetOffset);
|
||||
if(codeSet != NULL) {
|
||||
// Patches out an archive access check.
|
||||
u8 original[] = {0x0C, 0x05, 0x0C, 0x33, 0x46, 0xF7, 0xF7, 0x49, 0xF8, 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF8};
|
||||
u8 patched[] = {0x0C, 0x05, 0x0C, 0x33, 0x46, 0x01, 0x20, 0x00, 0x00, 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF8};
|
||||
|
||||
for(KLinkedListNode* node = codeSet->text_info.first_node; node != NULL; node = node->next) {
|
||||
KBlockInfo* blockInfo = (KBlockInfo*) node->data;
|
||||
u32 blockSize = blockInfo->page_count * 0x1000;
|
||||
|
||||
bool done = false;
|
||||
for(u32 i = 0; i <= blockSize - sizeof(original); i++) {
|
||||
u8* dst = (u8*) (blockInfo->section_start + i);
|
||||
|
||||
bool equal = true;
|
||||
for(u32 b = 0; b < sizeof(original); b++) {
|
||||
if(dst[b] != original[b]) {
|
||||
equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(equal) {
|
||||
for(u32 b = 0; b < sizeof(patched); b++) {
|
||||
dst[b] = patched[b];
|
||||
}
|
||||
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(done || node == codeSet->text_info.last_node) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void patch_fs() {
|
||||
kernel_version = *(vu32*) 0x1FF80000;
|
||||
APT_CheckNew3DS((u8*) &n3ds);
|
||||
|
||||
svcBackdoor(kernel_patch_fs);
|
||||
}
|
4
source/patcher/patcher.h
Normal file
4
source/patcher/patcher.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void patch_fs();
|
||||
|
@ -8,11 +8,14 @@
|
||||
#include "section/section.h"
|
||||
#include "../screen.h"
|
||||
|
||||
#define MAINMENU_ITEM_COUNT 8
|
||||
#define MAINMENU_ITEM_COUNT 11
|
||||
|
||||
static u32 mainmenu_item_count = MAINMENU_ITEM_COUNT;
|
||||
static list_item mainmenu_items[MAINMENU_ITEM_COUNT] = {
|
||||
{"SD", 0xFF000000, files_open_sd},
|
||||
{"CTR NAND", 0xFF000000, files_open_ctr_nand},
|
||||
{"TWL NAND", 0xFF000000, files_open_twl_nand},
|
||||
{"Dump NAND", 0xFF000000, dump_nand},
|
||||
{"Titles", 0xFF000000, titles_open},
|
||||
{"Pending Titles", 0xFF000000, pendingtitles_open},
|
||||
{"Tickets", 0xFF000000, tickets_open},
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "../section.h"
|
||||
|
||||
void action_browse_system_save_data(system_save_data_info* info, bool* populated) {
|
||||
u32 path[2] = {MEDIATYPE_NAND, (u32) (info->systemSaveDataId & 0xFFFFFFFF)};
|
||||
u32 path[2] = {MEDIATYPE_NAND, info->systemSaveDataId};
|
||||
FS_Archive archive = {ARCHIVE_SYSTEM_SAVEDATA, {PATH_BINARY, 8, path}};
|
||||
files_open(archive);
|
||||
}
|
@ -23,7 +23,7 @@ static void action_delete_system_save_data_success_onresponse(ui_view* view, voi
|
||||
static void action_delete_system_save_data_update(ui_view* view, void* data, float* progress, char* progressText) {
|
||||
delete_system_save_data_data* deleteData = (delete_system_save_data_data*) data;
|
||||
|
||||
FS_SystemSaveDataInfo sysInfo = *(FS_SystemSaveDataInfo*) &deleteData->info->systemSaveDataId;
|
||||
FS_SystemSaveDataInfo sysInfo = {.mediaType = MEDIATYPE_NAND, .saveId = deleteData->info->systemSaveDataId};
|
||||
Result res = FSUSER_DeleteSystemSaveData(sysInfo);
|
||||
|
||||
progressbar_destroy(view);
|
||||
|
158
source/ui/section/dumpnand.c
Normal file
158
source/ui/section/dumpnand.c
Normal file
@ -0,0 +1,158 @@
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include "../error.h"
|
||||
#include "../progressbar.h"
|
||||
#include "../prompt.h"
|
||||
|
||||
typedef struct {
|
||||
Handle in;
|
||||
Handle out;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
|
||||
u8 buffer[1024 * 1024];
|
||||
} dump_nand_data;
|
||||
|
||||
static void dumpnand_done_onresponse(ui_view* view, void* data, bool response) {
|
||||
prompt_destroy(view);
|
||||
}
|
||||
|
||||
static void dumpnand_update(ui_view* view, void* data, float* progress, char* progressText) {
|
||||
dump_nand_data* dumpData = (dump_nand_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
if(dumpData->in != 0) {
|
||||
FSFILE_Close(dumpData->in);
|
||||
dumpData->in = 0;
|
||||
}
|
||||
|
||||
if(dumpData->out != 0) {
|
||||
FSFILE_Close(dumpData->out);
|
||||
dumpData->out = 0;
|
||||
}
|
||||
|
||||
progressbar_destroy(view);
|
||||
ui_pop();
|
||||
|
||||
ui_push(prompt_create("Failure", "Dump cancelled.", 0xFF000000, false, data, NULL, NULL, dumpnand_done_onresponse));
|
||||
|
||||
free(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Result res = 0;
|
||||
|
||||
u32 bytesRead = 0;
|
||||
u32 bytesWritten = 0;
|
||||
if(R_SUCCEEDED(res = FSFILE_Read(dumpData->in, &bytesRead, dumpData->offset, dumpData->buffer, sizeof(dumpData->buffer))) && R_SUCCEEDED(res = FSFILE_Write(dumpData->out, &bytesWritten, dumpData->offset, dumpData->buffer, bytesRead, 0)) && bytesRead == bytesWritten) {
|
||||
dumpData->offset += bytesRead;
|
||||
}
|
||||
|
||||
if(R_FAILED(res)) {
|
||||
if(dumpData->in != 0) {
|
||||
FSFILE_Close(dumpData->in);
|
||||
dumpData->in = 0;
|
||||
}
|
||||
|
||||
if(dumpData->out != 0) {
|
||||
FSFILE_Close(dumpData->out);
|
||||
dumpData->out = 0;
|
||||
}
|
||||
|
||||
progressbar_destroy(view);
|
||||
ui_pop();
|
||||
|
||||
error_display_res(NULL, NULL, res, "Failed to dump NAND.");
|
||||
|
||||
free(data);
|
||||
} else if(bytesRead != bytesWritten) {
|
||||
if(dumpData->in != 0) {
|
||||
FSFILE_Close(dumpData->in);
|
||||
dumpData->in = 0;
|
||||
}
|
||||
|
||||
if(dumpData->out != 0) {
|
||||
FSFILE_Close(dumpData->out);
|
||||
dumpData->out = 0;
|
||||
}
|
||||
|
||||
progressbar_destroy(view);
|
||||
ui_pop();
|
||||
|
||||
error_display(NULL, NULL, "Failed to dump NAND: Read/Write size mismatch.");
|
||||
|
||||
free(data);
|
||||
} else if(dumpData->offset >= dumpData->size) {
|
||||
if(dumpData->in != 0) {
|
||||
FSFILE_Close(dumpData->in);
|
||||
dumpData->in = 0;
|
||||
}
|
||||
|
||||
if(dumpData->out != 0) {
|
||||
FSFILE_Close(dumpData->out);
|
||||
dumpData->out = 0;
|
||||
}
|
||||
|
||||
progressbar_destroy(view);
|
||||
ui_pop();
|
||||
|
||||
ui_push(prompt_create("Success", "NAND dumped.", 0xFF000000, false, NULL, NULL, NULL, dumpnand_done_onresponse));
|
||||
|
||||
free(data);
|
||||
} else {
|
||||
*progress = (float) dumpData->offset / (float) dumpData->size;
|
||||
snprintf(progressText, PROGRESS_TEXT_MAX, "%.2f MB / %.2f MB", dumpData->offset / 1024.0f / 1024.0f, dumpData->size / 1024.0f / 1024.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpnand_onresponse(ui_view* view, void* data, bool response) {
|
||||
prompt_destroy(view);
|
||||
|
||||
if(response) {
|
||||
dump_nand_data* dumpData = (dump_nand_data*) data;
|
||||
|
||||
Result res = 0;
|
||||
|
||||
FS_Archive wnandArchive = {ARCHIVE_NAND_W_FS, fsMakePath(PATH_EMPTY, "")};
|
||||
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&dumpData->in, wnandArchive, fsMakePath(PATH_UTF16, u"/"), FS_OPEN_READ, 0)) && R_SUCCEEDED(res = FSFILE_GetSize(dumpData->in, &dumpData->size))) {
|
||||
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (u8*) ""}};
|
||||
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&dumpData->out, sdmcArchive, fsMakePath(PATH_ASCII, "/NAND.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE, 0))) {
|
||||
dumpData->offset = 0;
|
||||
|
||||
ui_push(progressbar_create("Dumping NAND", "Press B to cancel.", data, dumpnand_update, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
if(R_FAILED(res)) {
|
||||
if(dumpData->in != 0) {
|
||||
FSFILE_Close(dumpData->in);
|
||||
dumpData->in = 0;
|
||||
}
|
||||
|
||||
if(dumpData->out != 0) {
|
||||
FSFILE_Close(dumpData->out);
|
||||
dumpData->out = 0;
|
||||
}
|
||||
|
||||
error_display_res(NULL, NULL, res, "Failed to prepare for NAND dump.");
|
||||
|
||||
free(data);
|
||||
}
|
||||
} else {
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_nand() {
|
||||
dump_nand_data* data = (dump_nand_data*) calloc(1, sizeof(dump_nand_data));
|
||||
data->in = 0;
|
||||
data->out = 0;
|
||||
data->offset = 0;
|
||||
data->size = 0;
|
||||
|
||||
ui_push(prompt_create("Confirmation", "Dump raw NAND image to the SD card?", 0xFF000000, true, data, NULL, NULL, dumpnand_onresponse));
|
||||
}
|
@ -83,6 +83,15 @@ static void extsavedata_update(ui_view* view, void* data, list_item** items, u32
|
||||
extsavedata_data* listData = (extsavedata_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
if(listData->cancelEvent != 0) {
|
||||
svcSignalEvent(listData->cancelEvent);
|
||||
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
|
||||
listData->cancelEvent = 0;
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
free(listData);
|
||||
list_destroy(view);
|
||||
|
@ -178,6 +178,15 @@ static void files_update(ui_view* view, void* data, list_item** items, u32** ite
|
||||
listData->archivePath = NULL;
|
||||
}
|
||||
|
||||
if(listData->cancelEvent != 0) {
|
||||
svcSignalEvent(listData->cancelEvent);
|
||||
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
|
||||
listData->cancelEvent = 0;
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
free(listData);
|
||||
list_destroy(view);
|
||||
@ -256,4 +265,14 @@ void files_open(FS_Archive archive) {
|
||||
void files_open_sd() {
|
||||
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
|
||||
files_open(sdmcArchive);
|
||||
}
|
||||
|
||||
void files_open_ctr_nand() {
|
||||
FS_Archive sdmcArchive = {ARCHIVE_NAND_CTR_FS, fsMakePath(PATH_EMPTY, "")};
|
||||
files_open(sdmcArchive);
|
||||
}
|
||||
|
||||
void files_open_twl_nand() {
|
||||
FS_Archive sdmcArchive = {ARCHIVE_NAND_TWL_FS, fsMakePath(PATH_EMPTY, "")};
|
||||
files_open(sdmcArchive);
|
||||
}
|
@ -82,6 +82,15 @@ static void pendingtitles_update(ui_view* view, void* data, list_item** items, u
|
||||
pendingtitles_data* listData = (pendingtitles_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
if(listData->cancelEvent != 0) {
|
||||
svcSignalEvent(listData->cancelEvent);
|
||||
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
|
||||
listData->cancelEvent = 0;
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
free(listData);
|
||||
list_destroy(view);
|
||||
|
@ -2,9 +2,12 @@
|
||||
|
||||
#include "../ui.h"
|
||||
|
||||
void dump_nand();
|
||||
void extsavedata_open();
|
||||
void files_open(FS_Archive archive);
|
||||
void files_open_sd();
|
||||
void files_open_ctr_nand();
|
||||
void files_open_twl_nand();
|
||||
void networkinstall_open(FS_MediaType dest);
|
||||
void networkinstall_open_sd();
|
||||
void networkinstall_open_nand();
|
||||
|
@ -82,6 +82,15 @@ static void systemsavedata_update(ui_view* view, void* data, list_item** items,
|
||||
systemsavedata_data* listData = (systemsavedata_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
if(listData->cancelEvent != 0) {
|
||||
svcSignalEvent(listData->cancelEvent);
|
||||
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
|
||||
listData->cancelEvent = 0;
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
free(listData);
|
||||
list_destroy(view);
|
||||
|
@ -25,10 +25,10 @@ static void task_populate_system_save_data_thread(void* arg) {
|
||||
Result res = 0;
|
||||
|
||||
u32 systemSaveDataCount = 0;
|
||||
u64* systemSaveDataIds = (u64*) calloc(data->max, sizeof(u64));
|
||||
u32* systemSaveDataIds = (u32*) calloc(data->max, sizeof(u32));
|
||||
if(systemSaveDataIds != NULL) {
|
||||
if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, data->max * sizeof(u64), systemSaveDataIds))) {
|
||||
qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u64), util_compare_u64);
|
||||
if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, data->max * sizeof(u32), (u64*) systemSaveDataIds))) {
|
||||
qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), util_compare_u64);
|
||||
|
||||
for(u32 i = 0; i < systemSaveDataCount && i < data->max; i++) {
|
||||
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
|
||||
@ -40,7 +40,7 @@ static void task_populate_system_save_data_thread(void* arg) {
|
||||
systemSaveDataInfo->systemSaveDataId = systemSaveDataIds[i];
|
||||
|
||||
list_item* item = &data->items[*data->count];
|
||||
snprintf(item->name, NAME_MAX, "%016llX", systemSaveDataIds[i]);
|
||||
snprintf(item->name, NAME_MAX, "%08lX", systemSaveDataIds[i]);
|
||||
item->rgba = 0xFF000000;
|
||||
item->data = systemSaveDataInfo;
|
||||
|
||||
|
@ -60,7 +60,7 @@ typedef struct {
|
||||
} ext_save_data_info;
|
||||
|
||||
typedef struct {
|
||||
u64 systemSaveDataId;
|
||||
u32 systemSaveDataId;
|
||||
} system_save_data_info;
|
||||
|
||||
typedef struct {
|
||||
|
@ -81,6 +81,15 @@ static void tickets_update(ui_view* view, void* data, list_item** items, u32** i
|
||||
tickets_data* listData = (tickets_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
if(listData->cancelEvent != 0) {
|
||||
svcSignalEvent(listData->cancelEvent);
|
||||
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
|
||||
listData->cancelEvent = 0;
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
free(listData);
|
||||
list_destroy(view);
|
||||
|
@ -85,6 +85,15 @@ static void titles_update(ui_view* view, void* data, list_item** items, u32** it
|
||||
titles_data* listData = (titles_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
if(listData->cancelEvent != 0) {
|
||||
svcSignalEvent(listData->cancelEvent);
|
||||
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
|
||||
listData->cancelEvent = 0;
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
free(listData);
|
||||
list_destroy(view);
|
||||
|
@ -461,7 +461,7 @@ void ui_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1
|
||||
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf, 64, "System Save Data ID: %016llX", info->systemSaveDataId);
|
||||
snprintf(buf, 64, "System Save Data ID: %08lX", info->systemSaveDataId);
|
||||
|
||||
float saveDataIdWidth;
|
||||
float saveDataIdHeight;
|
||||
|
@ -404,6 +404,13 @@ Result util_ensure_dir(FS_Archive* archive, const char* path) {
|
||||
return res;
|
||||
}
|
||||
|
||||
int util_compare_u32(const void* e1, const void* e2) {
|
||||
u32 id1 = *(u32*) e1;
|
||||
u32 id2 = *(u32*) e2;
|
||||
|
||||
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
|
||||
}
|
||||
|
||||
int util_compare_u64(const void* e1, const void* e2) {
|
||||
u64 id1 = *(u64*) e1;
|
||||
u64 id2 = *(u64*) e2;
|
||||
|
@ -42,5 +42,6 @@ void util_get_path_file(char* out, const char* path, u32 size);
|
||||
void util_get_parent_path(char* out, const char* path, u32 size);
|
||||
Result util_ensure_dir(FS_Archive* archive, const char* path);
|
||||
|
||||
int util_compare_u32(const void* e1, const void* e2);
|
||||
int util_compare_u64(const void* e1, const void* e2);
|
||||
int util_compare_directory_entries(const void* e1, const void* e2);
|
Loading…
x
Reference in New Issue
Block a user