diff --git a/file.c b/file.c index 3ab86be..61671aa 100644 --- a/file.c +++ b/file.c @@ -25,26 +25,28 @@ #include "strnatcmp.h" static char *devices[] = { - "gro0:", - "grw0:", - "imc0:", - "os0:", - "pd0:", - "sa0:", - "sd0:", - "tm0:", - "ud0:", - "uma0:", - "ur0:", - "ux0:", - "vd0:", - "vs0:", - "xmc0:", - "host0:", + "gro0:", + "grw0:", + "imc0:", + "os0:", + "pd0:", + "sa0:", + "sd0:", + "tm0:", + "ud0:", + "uma0:", + "ur0:", + "ux0:", + "vd0:", + "vs0:", + "xmc0:", + "host0:", }; #define N_DEVICES (sizeof(devices) / sizeof(char **)) +const char symlink_header_bytes[SYMLINK_HEADER_SIZE] = {0xF1, 0x1E, 0x00, 0x00}; + int allocateReadFile(const char *file, void **buffer) { SceUID fd = sceIoOpen(file, SCE_O_RDONLY, 0); if (fd < 0) @@ -436,13 +438,13 @@ int copyPath(const char *src_path, const char *dst_path, FileProcessParam *param sceIoGetstatByFd(dfd, &stat); stat.st_mode |= SCE_S_IWUSR; - + int ret = sceIoMkdir(dst_path, stat.st_mode & 0xFFF); if (ret < 0 && ret != SCE_ERROR_ERRNO_EEXIST) { sceIoDclose(dfd); return ret; } - + if (ret == SCE_ERROR_ERRNO_EEXIST) { sceIoChstat(dst_path, &stat, 0x3B); } @@ -756,12 +758,12 @@ void fileListAddEntry(FileList *list, FileListEntry *entry, int sort) { char entry_name[MAX_NAME_LENGTH]; strcpy(entry_name, entry->name); removeEndSlash(entry_name); - + while (p) { char p_name[MAX_NAME_LENGTH]; strcpy(p_name, p->name); removeEndSlash(p_name); - + // '..' is always at first if (strcmp(entry_name, "..") == 0) break; @@ -842,7 +844,7 @@ void fileListAddEntry(FileList *list, FileListEntry *entry, int sort) { list->tail = entry; } else { // Order: previous -> entry -> p previous->next = entry; - entry->previous = previous; + entry->previous = previous; entry->next = p; p->previous = entry; } @@ -965,6 +967,7 @@ int fileListGetDeviceEntries(FileList *list) { strcpy(entry->name, devices[i]); entry->is_folder = 1; entry->type = FILE_TYPE_UNKNOWN; + entry->is_symlink = 0; SceIoDevInfo info; memset(&info, 0, sizeof(SceIoDevInfo)); @@ -981,9 +984,9 @@ int fileListGetDeviceEntries(FileList *list) { } } - memcpy(&entry->ctime, (SceDateTime *)&stat.st_ctime, sizeof(SceDateTime)); - memcpy(&entry->mtime, (SceDateTime *)&stat.st_mtime, sizeof(SceDateTime)); - memcpy(&entry->atime, (SceDateTime *)&stat.st_atime, sizeof(SceDateTime)); + memcpy(&entry->ctime, (SceDateTime *) &stat.st_ctime, sizeof(SceDateTime)); + memcpy(&entry->mtime, (SceDateTime *) &stat.st_mtime, sizeof(SceDateTime)); + memcpy(&entry->atime, (SceDateTime *) &stat.st_atime, sizeof(SceDateTime)); fileListAddEntry(list, entry, SORT_BY_NAME); @@ -1011,6 +1014,7 @@ int fileListGetDirectoryEntries(FileList *list, const char *path, int sort) { strcpy(entry->name, DIR_UP); entry->is_folder = 1; entry->type = FILE_TYPE_UNKNOWN; + entry->is_symlink = 0; fileListAddEntry(list, entry, sort); } @@ -1025,6 +1029,9 @@ int fileListGetDirectoryEntries(FileList *list, const char *path, int sort) { FileListEntry *entry = malloc(sizeof(FileListEntry)); if (entry) { entry->is_folder = SCE_S_ISDIR(dir.d_stat.st_mode); + entry->is_symlink = 0; + entry->symlink = NULL; + if (entry->is_folder) { entry->name_length = strlen(dir.d_name) + 1; entry->name = malloc(entry->name_length + 1); @@ -1038,13 +1045,34 @@ int fileListGetDirectoryEntries(FileList *list, const char *path, int sort) { strcpy(entry->name, dir.d_name); entry->type = getFileType(entry->name); list->files++; + + if (dir.d_stat.st_size <= SYMLINK_MAX_SIZE) { + char *p = malloc(strlen(path) + strlen(dir.d_name) + 2); + if (!p) { + return VITASHELL_ERROR_INTERNAL; + } + snprintf(p, MAX_PATH_LENGTH - 1, "%s%s%s", + path, hasEndSlash(path) ? "" : "/", dir.d_name); + + Symlink* symlink = malloc(sizeof(Symlink)); + if (!symlink) { + return VITASHELL_ERROR_INTERNAL; + } + int res = resolveSimLink(symlink, p); + if (res < 0) { + if (symlink) + free(symlink); + } else { + entry->is_symlink = 1; + entry->symlink = symlink; + } + free(p); + } } - entry->size = dir.d_stat.st_size; - - memcpy(&entry->ctime, (SceDateTime *)&dir.d_stat.st_ctime, sizeof(SceDateTime)); - memcpy(&entry->mtime, (SceDateTime *)&dir.d_stat.st_mtime, sizeof(SceDateTime)); - memcpy(&entry->atime, (SceDateTime *)&dir.d_stat.st_atime, sizeof(SceDateTime)); + memcpy(&entry->ctime, (SceDateTime *) &dir.d_stat.st_ctime, sizeof(SceDateTime)); + memcpy(&entry->mtime, (SceDateTime *) &dir.d_stat.st_mtime, sizeof(SceDateTime)); + memcpy(&entry->atime, (SceDateTime *) &dir.d_stat.st_atime, sizeof(SceDateTime)); fileListAddEntry(list, entry, sort); } @@ -1070,3 +1098,107 @@ int fileListGetEntries(FileList *list, const char *path, int sort) { return fileListGetDirectoryEntries(list, path, sort); } + +// returns < 0 on error +int resolveSimLink(Symlink *symlink, const char *path) { + SceUID fd = sceIoOpen(path, SCE_O_RDONLY, 0); + if (fd < 0) + return VITASHELL_ERROR_SYMLINK_INTERNAL; + char magic[SYMLINK_HEADER_SIZE + 1]; + magic[SYMLINK_HEADER_SIZE] = '\0'; + + if (sceIoRead(fd, &magic, SYMLINK_HEADER_SIZE) < SYMLINK_HEADER_SIZE) { + sceIoClose(fd); + return VITASHELL_ERROR_SYMLINK_INTERNAL; + } + if(memcmp(magic, symlink_header_bytes, SYMLINK_HEADER_SIZE) != 0) { + sceIoClose(fd); + return VITASHELL_ERROR_SYMLINK_INTERNAL; + } + char *resolve = (char *) malloc(MAX_PATH_LENGTH); + if (!resolve) { + sceIoClose(fd); + return VITASHELL_ERROR_INTERNAL; + } + int bytes_read = sceIoRead(fd, resolve, MAX_PATH_LENGTH - 1); + sceIoClose(fd); + + if (bytes_read <= 0) { + free(resolve); + return VITASHELL_ERROR_SYMLINK_INTERNAL; + } + resolve[bytes_read] = '\0'; + SceIoStat io_stat; + memset(&io_stat, 0, sizeof(SceIoStat)); + if (sceIoGetstat(resolve, &io_stat) < 0) { + free(resolve); + return VITASHELL_ERROR_SYMLINK_INTERNAL; + } + symlink->to_file = !SCE_S_ISDIR(io_stat.st_mode); + symlink->target_path = resolve; + symlink->target_path_length = bytes_read + 1; + return 0; +} + +// return < 0 on error +int createSymLink(const char *store_location, const char *target) { + SceUID fd = sceIoOpen(store_location, SCE_O_WRONLY | SCE_O_CREAT, 0777); + if (fd < 0) { + return VITASHELL_ERROR_SYMLINK_INTERNAL; + } + sceIoWrite(fd, (void*) &symlink_header_bytes, SYMLINK_HEADER_SIZE); + sceIoWrite(fd, (void*) target, strnlen(target, MAX_PATH_LENGTH)); + sceIoClose(fd); + return 0; +} + +// get directory path from filename +// result has slash at the end +char * getBaseDirectory(const char * path) { + int i; + int sep_ind = -1; + int len = strlen(path); + if (len > MAX_PATH_LENGTH - 1 || len <= 0) return NULL; + for(i = len - 1; i >=0; i --) { + if (path[i] == '/' || path[i] == ':') { + sep_ind = i; + break; + } + } + if (sep_ind == -1) return NULL; + + char * res = (char *) malloc(MAX_PATH_LENGTH); + if (!res) return NULL; + + strncpy(res, path, MAX_PATH_LENGTH); + res[sep_ind + 1] = '\0'; + return res; +} + +// returns NULL when no filename found or error +// result is at most MAX_PATH_LEN +char * getFilename(const char *path) { + int i; + int sep_ind = -1; + int len = strlen(path); + if (len > MAX_PATH_LENGTH || len <= 0) return NULL; + if (path[len - 1] == '/' || path[len - 1] == ':') return NULL; // no file + + for(i = len - 1; i >=0; i --) { + if (path[i] == '/' || path[i] == ':') { + sep_ind = i; + break; + } + } + if (sep_ind == -1) return NULL; + char * res = (char *) malloc(MAX_PATH_LENGTH); + if (!res) return NULL; + + int new_len = len - (sep_ind + 1); + strncpy(res, path + (sep_ind + 1), new_len); // dont copy separation char + if (new_len + 1 < MAX_PATH_LENGTH) + res[new_len] = '\0'; + else + res[MAX_PATH_LENGTH - 1] = '\0'; + return res; +} \ No newline at end of file diff --git a/file.h b/file.h index a0d75b3..cc6ea2a 100644 --- a/file.h +++ b/file.h @@ -34,6 +34,12 @@ #define HOME_PATH "home" #define DIR_UP ".." +#define SYMLINK_HEADER_SIZE 4 +#define SYMLINK_MAX_SIZE (SYMLINK_HEADER_SIZE + MAX_PATH_LENGTH) +#define SYMLINK_EXT "lnk" +extern const char symlink_header_bytes[SYMLINK_HEADER_SIZE]; + + enum FileTypes { FILE_TYPE_UNKNOWN, FILE_TYPE_ARCHIVE, @@ -63,6 +69,12 @@ enum FileMoveFlags { MOVE_REPLACE = 0x2, // Replace files }; +typedef struct { + int to_file; // 1: to file, 0: to directory + char *target_path; + int target_path_length; +} Symlink; + typedef struct { uint64_t *value; uint64_t max; @@ -77,6 +89,8 @@ typedef struct FileListEntry { int name_length; int is_folder; int type; + int is_symlink; + Symlink *symlink; SceOff size; SceOff size2; SceDateTime ctime; @@ -101,6 +115,10 @@ int WriteFile(const char *file, const void *buf, int size); int checkFileExist(const char *file); int checkFolderExist(const char *folder); + +char * getBaseDirectory(const char *path); +char * getFilename(const char *path); + int getFileSize(const char *file); int getFileSha1(const char *file, uint8_t *pSha1Out, FileProcessParam *param); int getPathInfo(const char *path, uint64_t *size, uint32_t *folders, uint32_t *files, int (* handler)(const char *path)); @@ -127,4 +145,7 @@ void fileListEmpty(FileList *list); int fileListGetEntries(FileList *list, const char *path, int sort); +int resolveSimLink(Symlink* symlink, const char *target); +int createSymLink(const char *source_location, const char *target); + #endif diff --git a/init.c b/init.c index 6a3eda7..422cd37 100644 --- a/init.c +++ b/init.c @@ -48,6 +48,8 @@ INCLUDE_EXTERN_RESOURCE(default_pause_png); INCLUDE_EXTERN_RESOURCE(default_play_png); INCLUDE_EXTERN_RESOURCE(default_sfo_icon_png); INCLUDE_EXTERN_RESOURCE(default_text_icon_png); +INCLUDE_EXTERN_RESOURCE(default_file_symlink_icon_png); +INCLUDE_EXTERN_RESOURCE(default_folder_symlink_icon_png); INCLUDE_EXTERN_RESOURCE(electron_colors_txt); INCLUDE_EXTERN_RESOURCE(electron_archive_icon_png); @@ -106,7 +108,9 @@ static DefaultFile default_files[] = { DEFAULT_FILE("ux0:VitaShell/theme/Default/fastforward.png", default_fastforward_png, 1), DEFAULT_FILE("ux0:VitaShell/theme/Default/fastrewind.png", default_fastrewind_png, 1), DEFAULT_FILE("ux0:VitaShell/theme/Default/file_icon.png", default_file_icon_png, 1), + DEFAULT_FILE("ux0:VitaShell/theme/Default/file_symlink_icon.png",default_file_symlink_icon_png, 1), DEFAULT_FILE("ux0:VitaShell/theme/Default/folder_icon.png", default_folder_icon_png, 1), + DEFAULT_FILE("ux0:VitaShell/theme/Default/folder_symlink_icon.png",default_folder_symlink_icon_png, 1), DEFAULT_FILE("ux0:VitaShell/theme/Default/ftp.png", default_ftp_png, 1), DEFAULT_FILE("ux0:VitaShell/theme/Default/image_icon.png", default_image_icon_png, 1), DEFAULT_FILE("ux0:VitaShell/theme/Default/pause.png", default_pause_png, 1), @@ -391,6 +395,27 @@ void initVitaShell() { taiStopUnloadKernelModule(kernel_modid, 0, NULL, 0, NULL, NULL); } user_modid = sceKernelLoadStartModule("ux0:VitaShell/module/user.suprx", 0, NULL, 0, NULL, NULL); + + // clear up recent folder frequently + SceIoStat stat; + SceDateTime now; + sceRtcGetCurrentClock(&now, 0); + int res = sceIoGetstat(VITASHELL_RECENT_PATH, &stat); + if (res >= 0) { + if (now.year * 365 + now.day - stat.st_ctime.year * 365 - stat.st_ctime.day + >= VITASHELL_RECENT_PATH_DELETE_INTERVAL_DAYS) { + removePath(VITASHELL_RECENT_PATH, 0); + } + } + + if (!checkFolderExist(VITASHELL_BOOKMARKS_PATH)) { + sceIoMkdir(VITASHELL_BOOKMARKS_PATH, 0777); + } + if (!checkFolderExist(VITASHELL_RECENT_PATH)) { + sceIoMkdir(VITASHELL_RECENT_PATH, 0777); + } + time_last_recent_files = 0; + time_last_bookmars = 0; } void finishVitaShell() { diff --git a/language.c b/language.c index dfb0b5a..3e114d3 100644 --- a/language.c +++ b/language.c @@ -132,6 +132,11 @@ void loadLanguage(int id) { LANGUAGE_ENTRY(SEND), LANGUAGE_ENTRY(RECEIVE), LANGUAGE_ENTRY(MORE), + LANGUAGE_ENTRY(BOOKMARKS), + LANGUAGE_ENTRY(ADHOC_TRANSFER), + LANGUAGE_ENTRY(BOOKMARKS_SHOW), + LANGUAGE_ENTRY(BOOKMARKS_NEW), + LANGUAGE_ENTRY(RECENT_FILES_SHOW), LANGUAGE_ENTRY(COMPRESS), LANGUAGE_ENTRY(INSTALL_ALL), LANGUAGE_ENTRY(INSTALL_FOLDER), @@ -276,6 +281,7 @@ void loadLanguage(int id) { LANGUAGE_ENTRY(ARCHIVE_NAME), LANGUAGE_ENTRY(COMPRESSION_LEVEL), LANGUAGE_ENTRY(ENTER_PASSWORD), + LANGUAGE_ENTRY(BOOKMARK_CREATED) }; // Load default config file diff --git a/language.h b/language.h index df4377b..b82dcea 100644 --- a/language.h +++ b/language.h @@ -91,6 +91,11 @@ enum LanguageContainer { SEND, RECEIVE, MORE, + ADHOC_TRANSFER, + BOOKMARKS, + BOOKMARKS_SHOW, + BOOKMARKS_NEW, + RECENT_FILES_SHOW, COMPRESS, INSTALL_ALL, INSTALL_FOLDER, @@ -235,6 +240,7 @@ enum LanguageContainer { ARCHIVE_NAME, COMPRESSION_LEVEL, ENTER_PASSWORD, + BOOKMARK_CREATED, LANGUAGE_CONTAINER_SIZE, }; diff --git a/main.c b/main.c index ee82028..ff6df05 100644 --- a/main.c +++ b/main.c @@ -70,6 +70,7 @@ static int dir_level = 0; // Modes int sort_mode = SORT_BY_NAME; +int last_set_sort_mode = SORT_BY_NAME; int copy_mode = COPY_MODE_NORMAL; int file_type = FILE_TYPE_UNKNOWN; @@ -96,6 +97,44 @@ int SCE_CTRL_ENTER = SCE_CTRL_CROSS, SCE_CTRL_CANCEL = SCE_CTRL_CIRCLE; // Use custom config int use_custom_config = 1; +SceInt64 time_last_recent_files, time_last_bookmars; + +static void setFocusOnFilename(const char *name); +static void fileBrowserHandleSymlink(FileListEntry* file_entry); +static void fileBrowserHandleFolder(FileListEntry* file_entry); +static void fileBrowserHandleFile(FileListEntry* file_entry); + +static void create_recent_symlink(FileListEntry *file_entry); + +static void delete_all_symlink_directory_path(); + +// escape from dir hierarchy with a symlink +typedef struct SymlinkDirectoryPath { + struct SymlinkDirectoryPath* previous; + // contains / at the end, is directory where jumped from + char last_path[MAX_PATH_LENGTH]; + // contains / at the end, is directory where jumped to + char last_hook[MAX_PATH_LENGTH]; +} SymlinkDirectoryPath; + +static SymlinkDirectoryPath* symlink_directory_path = NULL; +static SymlinkDirectoryPath* symlink_directory_path_head = NULL; + +static void storeSymlinkPath(SymlinkDirectoryPath * path) { + if (!symlink_directory_path) { + symlink_directory_path = path; + symlink_directory_path_head = path; + symlink_directory_path->previous = 0; + } else { + SymlinkDirectoryPath *prev = symlink_directory_path; + symlink_directory_path = path; + symlink_directory_path->previous = prev; + } +} + +static void delete_all_symlink_directory_path() { + +} int getDialogStep() { sceKernelLockLwMutex(&dialog_mutex, 1, NULL); @@ -132,6 +171,45 @@ void dirUpCloseArchive() { } } +int change_to_directory(char *lastdir) { + if (!checkFolderExist(lastdir)) { + return VITASHELL_ERROR_NAVIGATION; + } else { + if (isInArchive()) { + dirUpCloseArchive(); + } + int i; + for (i = 0; i < strlen(lastdir) + 1; i++) { + if (lastdir[i] == ':' || lastdir[i] == '/') { + char ch = lastdir[i + 1]; + lastdir[i + 1] = '\0'; + + char ch2 = lastdir[i]; + lastdir[i] = '\0'; + + char *p = strrchr(lastdir, '/'); + if (!p) + p = strrchr(lastdir, ':'); + if (!p) + p = lastdir - 1; + + lastdir[i] = ch2; + + refreshFileList(); + setFocusOnFilename(p + 1); + + strcpy(file_list.path, lastdir); + + lastdir[i + 1] = ch; + + dirLevelUp(); + } + } + } + refreshFileList(); + return 0; +} + static void dirUp() { if (pfs_mounted_path[0] && strcmp(file_list.path, pfs_mounted_path) == 0 && // we're about to leave the pfs path @@ -140,10 +218,36 @@ static void dirUp() { pfsUmount(); } + // skip all symlink hierarchies when pressing O in bookmarks/ recent files + if (symlink_directory_path_head && + ((strncmp(file_list.path, VITASHELL_BOOKMARKS_PATH, MAX_PATH_LENGTH) == 0) + || strncmp(file_list.path, VITASHELL_RECENT_PATH, MAX_PATH_LENGTH) == 0)) { + strcpy(file_list.path, symlink_directory_path_head->last_path); + SymlinkDirectoryPath *e = symlink_directory_path; + while(e != NULL) { + SymlinkDirectoryPath *prev = e->previous; + free(e); + dir_level--; + e = prev; + } + symlink_directory_path_head = 0; + symlink_directory_path = 0; + goto DIR_UP_RETURN; + } + + if (symlink_directory_path + && strncmp(file_list.path, symlink_directory_path->last_hook, MAX_PATH_LENGTH) == 0) { + strcpy(file_list.path, symlink_directory_path->last_path); + SymlinkDirectoryPath* prev = symlink_directory_path->previous; + free(symlink_directory_path); + symlink_directory_path = prev; + dir_level--; + goto DIR_UP_RETURN; + } + removeEndSlash(file_list.path); char *p; - p = strrchr(file_list.path, '/'); if (p) { p[1] = '\0'; @@ -188,6 +292,14 @@ static void setFocusOnFilename(const char *name) { int refreshFileList() { int ret = 0, res = 0; + // always sort recent files by date + char *contains = strstr(file_list.path, VITASHELL_RECENT_PATH); + if (contains) { + sort_mode = SORT_BY_DATE; + } else { + sort_mode = last_set_sort_mode; + } + do { fileListEmpty(&file_list); @@ -1491,21 +1603,24 @@ static int fileBrowserMenuCtrl() { // SELECT button if (pressed_pad[PAD_SELECT]) { - if (vitashell_config.select_button == SELECT_BUTTON_MODE_USB && sceKernelGetModel() == SCE_KERNEL_MODEL_VITA) { + if (vitashell_config.select_button == SELECT_BUTTON_MODE_USB && + sceKernelGetModel() == SCE_KERNEL_MODEL_VITA) { if (is_safe_mode) { infoDialog(language_container[EXTENDED_PERMISSIONS_REQUIRED]); } else { SceUdcdDeviceState state; sceUdcdGetDeviceState(&state); - + if (state.cable & SCE_UDCD_STATUS_CABLE_CONNECTED) { initUsb(); } else { - initMessageDialog(SCE_MSG_DIALOG_BUTTON_TYPE_CANCEL, language_container[USB_NOT_CONNECTED]); + initMessageDialog(SCE_MSG_DIALOG_BUTTON_TYPE_CANCEL, + language_container[USB_NOT_CONNECTED]); setDialogStep(DIALOG_STEP_USB_WAIT); } } - } else if (vitashell_config.select_button == SELECT_BUTTON_MODE_FTP || sceKernelGetModel() == SCE_KERNEL_MODEL_VITATV) { + } else if (vitashell_config.select_button == SELECT_BUTTON_MODE_FTP || + sceKernelGetModel() == SCE_KERNEL_MODEL_VITATV) { // Init FTP if (!ftpvita_is_initialized()) { int res = ftpvita_init(vita_ip, &vita_port); @@ -1522,11 +1637,13 @@ static int fileBrowserMenuCtrl() { // Dialog if (ftpvita_is_initialized()) { - initMessageDialog(SCE_MSG_DIALOG_BUTTON_TYPE_OK_CANCEL, language_container[FTP_SERVER], vita_ip, vita_port); + initMessageDialog(SCE_MSG_DIALOG_BUTTON_TYPE_OK_CANCEL, language_container[FTP_SERVER], + vita_ip, vita_port); setDialogStep(DIALOG_STEP_FTP); } } } + /* // QR if (hold_pad[PAD_LTRIGGER] && hold_pad[PAD_RTRIGGER] && enabledQR()) { @@ -1535,10 +1652,43 @@ static int fileBrowserMenuCtrl() { setDialogStep(DIALOG_STEP_QR); } */ - // Move + + // bookmarks shortcut + if (current_pad[PAD_LEFT] && current_pad[PAD_SQUARE]) { + SceInt64 now = sceKernelGetSystemTimeWide(); + + // switching too quickly back and forth between recent and bookmarks + // causes VS to crash + if (now - time_last_bookmars > THRESHOLD_LAST_PAD_BOOKMARKS_WAIT) { + if (strncmp(file_list.path, VITASHELL_BOOKMARKS_PATH, MAX_PATH_LENGTH) != 0) { + char path[MAX_PATH_LENGTH] = VITASHELL_BOOKMARKS_PATH; + sort_mode = last_set_sort_mode; + jump_to_directory_track_current_path(path); + time_last_bookmars = now; + return 0; + + } + } + } + // recent files shortcut + if (current_pad[PAD_LEFT] && current_pad[PAD_TRIANGLE]) { + SceInt64 now = sceKernelGetSystemTimeWide(); + if (now - time_last_recent_files > THRESHOLD_LAST_PAD_RECENT_FILES_WAIT) { + if (strncmp(file_list.path, VITASHELL_RECENT_PATH, MAX_PATH_LENGTH) != 0) { + char path[MAX_PATH_LENGTH] = VITASHELL_RECENT_PATH; + sort_mode = SORT_BY_DATE; + jump_to_directory_track_current_path(path); + time_last_recent_files = now; + return 0; + + } + } + } + + // Move if (hold_pad[PAD_UP] || hold2_pad[PAD_LEFT_ANALOG_UP]) { int old_pos = base_pos + rel_pos; - + if (rel_pos > 0) { rel_pos--; } else if (base_pos > 0) { @@ -1632,55 +1782,135 @@ static int fileBrowserMenuCtrl() { fileListEmpty(&mark_list); - // Handle file or folder + // Handle file, symlink or folder FileListEntry *file_entry = fileListGetNthEntry(&file_list, base_pos + rel_pos); if (file_entry) { - if (file_entry->is_folder) { - if (strcmp(file_entry->name, DIR_UP) == 0) { - dirUp(); - } else { - if (dir_level == 0) { - strcpy(file_list.path, file_entry->name); - } else { - if (dir_level > 1) - addEndSlash(file_list.path); - strcat(file_list.path, file_entry->name); - } - - dirLevelUp(); - } - - // Save last dir - WriteFile(VITASHELL_LASTDIR, file_list.path, strlen(file_list.path) + 1); - - // Open folder - int res = refreshFileList(); - if (res < 0) - errorDialog(res); + if (file_entry->is_symlink) { + fileBrowserHandleSymlink(file_entry); + } else if (file_entry->is_folder) { + fileBrowserHandleFolder(file_entry); } else { - snprintf(cur_file, MAX_PATH_LENGTH, "%s%s", file_list.path, file_entry->name); - int type = handleFile(cur_file, file_entry); - - // Archive mode - if (type == FILE_TYPE_ARCHIVE && getDialogStep() != DIALOG_STEP_ENTER_PASSWORD) { - is_in_archive = 1; - dir_level_archive = dir_level; - - snprintf(archive_path, MAX_PATH_LENGTH, "%s%s", file_list.path, file_entry->name); - - strcat(file_list.path, file_entry->name); - addEndSlash(file_list.path); - - dirLevelUp(); - refreshFileList(); - } + fileBrowserHandleFile(file_entry); + create_recent_symlink(file_entry); } } } - return refresh; } +static void create_recent_symlink(FileListEntry *file_entry) { + char target[MAX_PATH_LENGTH]; + snprintf(target, MAX_PATH_LENGTH, "%s%s."SYMLINK_EXT, VITASHELL_RECENT_PATH, + file_entry->name); + snprintf(cur_file, MAX_PATH_LENGTH, "%s%s", file_list.path, file_entry->name); + + // create file recent symlink + createSymLink(target, cur_file); +} + +static void fileBrowserHandleFile(FileListEntry *file_entry) { + snprintf(cur_file, MAX_PATH_LENGTH - 1, "%s%s", file_list.path, file_entry->name); + int type = handleFile(cur_file, file_entry); + + // Archive mode + if (type == FILE_TYPE_ARCHIVE && getDialogStep() != DIALOG_STEP_ENTER_PASSWORD) { + is_in_archive = 1; + dir_level_archive = dir_level; + + snprintf(archive_path, MAX_PATH_LENGTH - 1, "%s%s", file_list.path, file_entry->name); + + strcat(file_list.path, file_entry->name); + addEndSlash(file_list.path); + + dirLevelUp(); + refreshFileList(); + } +} + +static void fileBrowserHandleFolder(FileListEntry *file_entry) { + if (strcmp(file_entry->name, DIR_UP) == 0) { + dirUp(); + } else { + if (dir_level == 0) { + strcpy(file_list.path, file_entry->name); + } else { + if (dir_level > 1) + addEndSlash(file_list.path); + strcat(file_list.path, file_entry->name); + } + dirLevelUp(); + } + + // Save last dir + WriteFile(VITASHELL_LASTDIR, file_list.path, strlen(file_list.path) + 1); + + // Open folder + int res = refreshFileList(); + if (res < 0) + errorDialog(res); +} + +// escape from dir level structure so that parent directory is browsed +// where this jump came from and not the hierarchically higher folder +int jump_to_directory_track_current_path(char *path) { + SymlinkDirectoryPath *symlink_path = malloc(sizeof(SymlinkDirectoryPath)); + if (symlink_path) { + strncpy(symlink_path->last_path, file_list.path, MAX_PATH_LENGTH); + strncpy(symlink_path->last_hook, path, MAX_PATH_LENGTH); + dirLevelUp(); + int _dir_level = dir_level; // we escape from hierarchical dir level structure + if (change_to_directory(path) < 0) { + free(symlink_path); + return VITASHELL_ERROR_NAVIGATION; + } + WriteFile(VITASHELL_LASTDIR, file_list.path, strlen(file_list.path) + 1); + storeSymlinkPath(symlink_path); + dir_level = _dir_level; + refreshFileList(); + } + return 0; +} + +static void fileBrowserHandleSymlink(FileListEntry *file_entry) { + if ((file_entry->symlink->to_file == 1 && !checkFileExist(file_entry->symlink->target_path)) + || (file_entry->symlink->to_file == 0 && !checkFolderExist(file_entry->symlink->target_path))) { + // TODO: What if in archive? + snprintf(cur_file, MAX_PATH_LENGTH - 1, "%s%s", file_list.path, file_entry->name); + textViewer(cur_file); + return; + } + if (file_entry->symlink->to_file == 0) { + if (jump_to_directory_track_current_path(file_entry->symlink->target_path) < 0) { + errorDialog(VITASHELL_ERROR_SYMLINK_INVALID_PATH); + } + } else { + char *target_base_directory = getBaseDirectory(file_entry->symlink->target_path); + if (!target_base_directory) { + errorDialog(VITASHELL_ERROR_SYMLINK_CANT_RESOLVE_BASEDIR); + return; + } + char *target_file_name = getFilename(file_entry->symlink->target_path); + if (!target_file_name) { + errorDialog(VITASHELL_ERROR_SYMLINK_CANT_RESOLVE_FILENAME); + return; + } + if (jump_to_directory_track_current_path(target_base_directory) < 0) { + errorDialog(VITASHELL_ERROR_SYMLINK_INVALID_PATH); + return; + } + FileListEntry *resolved_file_entry = fileListFindEntry(&file_list, target_file_name); + if (!resolved_file_entry) { + errorDialog(VITASHELL_ERROR_SYMLINK_INVALID_PATH); + return; + } + fileBrowserHandleFile(resolved_file_entry); + dirUp(); + } + int res = refreshFileList(); + if (res < 0) + errorDialog(res); +} + static int shellMain() { // Position memset(base_pos_list, 0, sizeof(base_pos_list)); @@ -1703,37 +1933,7 @@ static int shellMain() { // Last dir char lastdir[MAX_PATH_LENGTH]; ReadFile(VITASHELL_LASTDIR, lastdir, sizeof(lastdir)); - - // Calculate dir positions if the dir is valid - if (checkFolderExist(lastdir)) { - int i; - for (i = 0; i < strlen(lastdir) + 1; i++) { - if (lastdir[i] == ':' || lastdir[i] == '/') { - char ch = lastdir[i + 1]; - lastdir[i + 1] = '\0'; - - char ch2 = lastdir[i]; - lastdir[i] = '\0'; - - char *p = strrchr(lastdir, '/'); - if (!p) - p = strrchr(lastdir, ':'); - if (!p) - p = lastdir - 1; - - lastdir[i] = ch2; - - refreshFileList(); - setFocusOnFilename(p + 1); - - strcpy(file_list.path, lastdir); - - lastdir[i + 1] = ch; - - dirLevelUp(); - } - } - } + change_to_directory(lastdir); } // Refresh file list @@ -1774,7 +1974,6 @@ static int shellMain() { pfsUmount(); // umount game data at resume refresh = REFRESH_MODE_NORMAL; } - if (refresh != REFRESH_MODE_NONE) { // Refresh lists refreshFileList(); @@ -1802,9 +2001,17 @@ static int shellMain() { float y = START_Y + (i * FONT_Y_SPACE); vita2d_texture *icon = NULL; - + if (file_entry->is_symlink) { + if (file_entry->symlink->to_file) { + color = FILE_SYMLINK_COLOR; + icon = file_symlink_icon; + } else { + color = FOLDER_SYMLINK_COLOR; + icon = folder_symlink_icon; + } + } // Folder - if (file_entry->is_folder) { + else if (file_entry->is_folder) { color = FOLDER_COLOR; icon = folder_icon; } else { @@ -1862,11 +2069,21 @@ static int shellMain() { // Draw file name vita2d_enable_clipping(); vita2d_set_clip_rectangle(FILE_X + 1.0f, y, FILE_X + 1.0f + MAX_NAME_WIDTH, y + FONT_Y_SPACE); - + float x = FILE_X; - + + char file_name[MAX_PATH_LENGTH]; + memset(file_name, 0, sizeof(MAX_PATH_LENGTH)); + + if (file_entry->is_symlink) { + snprintf(file_name, MAX_PATH_LENGTH, "%s → %s", + file_entry->name, file_entry->symlink->target_path); + } else { + strncpy(file_name, file_entry->name, file_entry->name_length + 1); + file_name[file_entry->name_length] = '\0'; + } if (i == rel_pos) { - int width = (int)pgf_text_width(file_entry->name); + int width = (int)pgf_text_width(file_name); if (width >= MAX_NAME_WIDTH) { if (scroll_count < 60) { scroll_x = x; @@ -1885,7 +2102,7 @@ static int shellMain() { } } - pgf_draw_text(x, y, color, file_entry->name); + pgf_draw_text(x, y, color, file_name); vita2d_disable_clipping(); diff --git a/main.h b/main.h index 3f90430..ba8a31a 100644 --- a/main.h +++ b/main.h @@ -60,6 +60,11 @@ #define VITASHELL_LASTDIR "ux0:VitaShell/internal/lastdir.txt" +// needs / at the end +#define VITASHELL_BOOKMARKS_PATH "ux0:VitaShell/bookmarks/" +#define VITASHELL_RECENT_PATH "ux0:VitaShell/recent/" +#define VITASHELL_RECENT_PATH_DELETE_INTERVAL_DAYS 14 + #define VITASHELL_TITLEID "VITASHELL" #define ALIGN(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) @@ -254,6 +259,13 @@ extern char archive_path[MAX_PATH_LENGTH]; extern int base_pos, rel_pos; extern int sort_mode, copy_mode; +extern int last_set_sort_mode; + +// minimum time to pass before shortcutting to recent files/ bookmarks via L/R keys +extern SceInt64 time_last_recent_files, time_last_bookmars; +#define THRESHOLD_LAST_PAD_RECENT_FILES_WAIT 1000000 +#define THRESHOLD_LAST_PAD_BOOKMARKS_WAIT 1000000 + extern vita2d_pgf *font; extern char font_size_cache[256]; @@ -276,5 +288,6 @@ int isInArchive(); int refreshFileList(); void ftpvita_PROM(ftpvita_client_info_t *client); +int jump_to_directory_track_current_path(char *path); #endif diff --git a/main_context.c b/main_context.c index 90296f4..ac834db 100644 --- a/main_context.c +++ b/main_context.c @@ -77,8 +77,9 @@ enum MenuMainEntrys { MENU_MAIN_ENTRY_PROPERTIES, MENU_MAIN_ENTRY_SORT_BY, MENU_MAIN_ENTRY_MORE, - MENU_MAIN_ENTRY_SEND, - MENU_MAIN_ENTRY_RECEIVE, + MENU_MAIN_ENTRY_ADHOC, + MENU_MAIN_ENTRY_BOOKMARKS, + }; MenuEntry menu_main_entries[] = { @@ -93,8 +94,9 @@ MenuEntry menu_main_entries[] = { { PROPERTIES, 11, 0, CTX_INVISIBLE }, { SORT_BY, 13, CTX_FLAG_MORE, CTX_VISIBLE }, { MORE, 14, CTX_FLAG_MORE, CTX_INVISIBLE }, - { SEND, 17, 0, CTX_INVISIBLE }, // CTX_FLAG_BARRIER - { RECEIVE, 18, 0, CTX_INVISIBLE }, + { ADHOC_TRANSFER, 16, CTX_FLAG_MORE, CTX_INVISIBLE }, + { BOOKMARKS, 17, CTX_FLAG_MORE, CTX_INVISIBLE }, + }; #define N_MENU_MAIN_ENTRIES (sizeof(menu_main_entries) / sizeof(MenuEntry)) @@ -113,6 +115,30 @@ MenuEntry menu_sort_entries[] = { #define N_MENU_SORT_ENTRIES (sizeof(menu_sort_entries) / sizeof(MenuEntry)) +enum MenuBookmarksEntrys { + MENU_BOOKMARKS_SHOW_BOOKMARKS, + MENU_BOOKMARKS_RECENT_FILES +}; + +MenuEntry menu_bookmark_entries[] = { + { BOOKMARKS_SHOW, 12, 0, CTX_INVISIBLE }, + { RECENT_FILES_SHOW, 13, 0, CTX_INVISIBLE }, +}; + +#define N_MENU_BOOKMARK_ENTRIES (sizeof(menu_bookmark_entries) / sizeof(MenuEntry)) + +enum MenuAdhocEntrys { + MENU_ADHOC_SEND, + MENU_ADHOC_RECEIVE +}; + +MenuEntry menu_adhoc_entries[] = { + { SEND, 12, 0, CTX_INVISIBLE }, + { RECEIVE, 13, 0, CTX_INVISIBLE }, +}; + +#define N_MENU_ADHOC_ENTRIES (sizeof(menu_adhoc_entries) / sizeof(MenuEntry)) + enum MenuMoreEntrys { MENU_MORE_ENTRY_COMPRESS, MENU_MORE_ENTRY_INSTALL_ALL, @@ -133,12 +159,14 @@ MenuEntry menu_more_entries[] = { enum MenuNewEntrys { MENU_NEW_FILE, - MENU_NEW_FOLDER + MENU_NEW_FOLDER, + MENU_NEW_BOOKMARK }; MenuEntry menu_new_entries[] = { {NEW_FILE, 10, 0, CTX_INVISIBLE}, - {NEW_FOLDER, 11, 0, CTX_INVISIBLE} + {NEW_FOLDER, 11, 0, CTX_INVISIBLE}, + {BOOKMARKS_NEW, 12, 0, CTX_INVISIBLE} }; @@ -148,6 +176,8 @@ static int contextMenuHomeEnterCallback(int sel, void *context); static int contextMenuMainEnterCallback(int sel, void *context); static int contextMenuSortEnterCallback(int sel, void *context); static int contextMenuMoreEnterCallback(int sel, void *context); +static int contextMenuBookmarksEnterCallback(int sel, void *context); +static int contextMenuAdhocEnterCallback(int sel, void *context); static int contextMenuNewEnterCallback(int sel, void *context); ContextMenu context_menu_home = { @@ -186,6 +216,25 @@ ContextMenu context_menu_more = { .sel = -1, }; +ContextMenu context_menu_bookmarks = { + .parent = &context_menu_main, + .entries = menu_bookmark_entries, + .n_entries = N_MENU_BOOKMARK_ENTRIES, + .max_width = 0.0f, + .callback = contextMenuBookmarksEnterCallback, + .sel = -1, +}; + +ContextMenu context_menu_adhoc = { + .parent = &context_menu_main, + .entries = menu_adhoc_entries, + .n_entries = N_MENU_ADHOC_ENTRIES, + .max_width = 0.0f, + .callback = contextMenuAdhocEnterCallback, + .sel = -1, +}; + + ContextMenu context_menu_new = { .parent = &context_menu_main, .entries = menu_new_entries, @@ -287,7 +336,8 @@ void initContextMenuWidth() { // Main for (i = 0; i < N_MENU_MAIN_ENTRIES; i++) { - context_menu_main.max_width = MAX(context_menu_main.max_width, pgf_text_width(language_container[menu_main_entries[i].name])); + context_menu_main.max_width = MAX(context_menu_main.max_width, + pgf_text_width(language_container[menu_main_entries[i].name])); if (menu_main_entries[i].name == MARK_ALL) { menu_main_entries[i].name = UNMARK_ALL; @@ -321,6 +371,26 @@ void initContextMenuWidth() { } context_menu_new.max_width += 2.0f * CONTEXT_MENU_MARGIN; context_menu_new.max_width = MAX(context_menu_new.max_width, CONTEXT_MENU_MIN_WIDTH); + + // bookmarks + for (i = 0; i < N_MENU_BOOKMARK_ENTRIES; i++) { + context_menu_bookmarks.max_width = MAX(context_menu_bookmarks.max_width, pgf_text_width + (language_container[menu_bookmark_entries[i].name])); + } + + context_menu_bookmarks.max_width += 2.0f * CONTEXT_MENU_MARGIN; + context_menu_bookmarks.max_width = MAX(context_menu_bookmarks.max_width, CONTEXT_MENU_MIN_WIDTH); + + // adhoc + for (i = 0; i < N_MENU_ADHOC_ENTRIES; i++) { + context_menu_adhoc.max_width = MAX(context_menu_adhoc.max_width, pgf_text_width + (language_container[menu_adhoc_entries[i].name])); + } + + context_menu_adhoc.max_width += 2.0f * CONTEXT_MENU_MARGIN; + context_menu_adhoc.max_width = MAX(context_menu_adhoc.max_width, CONTEXT_MENU_MIN_WIDTH); + + } void setContextMenuHomeVisibilities() { @@ -415,8 +485,6 @@ void setContextMenuMainVisibilities() { menu_main_entries[MENU_MAIN_ENTRY_DELETE].visibility = CTX_INVISIBLE; menu_main_entries[MENU_MAIN_ENTRY_RENAME].visibility = CTX_INVISIBLE; menu_main_entries[MENU_MAIN_ENTRY_PROPERTIES].visibility = CTX_INVISIBLE; - menu_main_entries[MENU_MAIN_ENTRY_SEND].visibility = CTX_INVISIBLE; - // menu_main_entries[MENU_MAIN_ENTRY_RECEIVE].flags = CTX_FLAG_BARRIER; } // Invisible 'Paste' if nothing is copied yet @@ -433,8 +501,6 @@ void setContextMenuMainVisibilities() { menu_main_entries[MENU_MAIN_ENTRY_DELETE].visibility = CTX_INVISIBLE; menu_main_entries[MENU_MAIN_ENTRY_RENAME].visibility = CTX_INVISIBLE; menu_main_entries[MENU_MAIN_ENTRY_NEW].visibility = CTX_INVISIBLE; - menu_main_entries[MENU_MAIN_ENTRY_SEND].visibility = CTX_INVISIBLE; - menu_main_entries[MENU_MAIN_ENTRY_RECEIVE].visibility = CTX_INVISIBLE; } // Mark/Unmark all text @@ -472,6 +538,74 @@ void setContextMenuMainVisibilities() { context_menu_main.sel = -1; } +void setContextMenuBookmarksVisibilities() { + int i; + // All visible + for (i = 0; i < N_MENU_BOOKMARK_ENTRIES; i++) { + if (menu_bookmark_entries[i].visibility == CTX_INVISIBLE) + menu_bookmark_entries[i].visibility = CTX_VISIBLE; + } + + FileListEntry *file_entry = fileListGetNthEntry(&file_list, base_pos + rel_pos); + if (!file_entry) + return; + + if (strcmp(file_list.path, VITASHELL_BOOKMARKS_PATH) == 0) { + menu_bookmark_entries[MENU_BOOKMARKS_SHOW_BOOKMARKS].visibility = CTX_INVISIBLE; + } + + if (strcmp(file_list.path, VITASHELL_RECENT_PATH) == 0) { + menu_bookmark_entries[MENU_BOOKMARKS_RECENT_FILES].visibility = CTX_INVISIBLE; + } + + // Go to first entry + for (i = 0; i < N_MENU_BOOKMARK_ENTRIES; i++) { + if (menu_bookmark_entries[i].visibility == CTX_VISIBLE) { + context_menu_bookmarks.sel = i; + break; + } + } + + if (i == N_MENU_BOOKMARK_ENTRIES) + context_menu_bookmarks.sel = -1; + +} +void setContextMenuAdhocVisibilities() { + int i; + // All visible + for (i = 0; i < N_MENU_ADHOC_ENTRIES; i++) { + if (menu_adhoc_entries[i].visibility == CTX_INVISIBLE) + menu_adhoc_entries[i].visibility = CTX_VISIBLE; + } + + FileListEntry *file_entry = fileListGetNthEntry(&file_list, base_pos + rel_pos); + if (!file_entry) + return; + + // Invisble entries when on '..' + if (strcmp(file_entry->name, DIR_UP) == 0) { + menu_adhoc_entries[MENU_ADHOC_SEND].visibility = CTX_INVISIBLE; + // menu_adhoc_entries[MENU_ADHOC_RECEIVE].flags = CTX_FLAG_BARRIER; + } + + // Invisible write operations in archives + // TODO: read-only mount points + if (isInArchive() || (pfs_mounted_path[0] && strstr(file_list.path, pfs_mounted_path) && read_only)) { + menu_adhoc_entries[MENU_ADHOC_SEND].visibility = CTX_INVISIBLE; + menu_adhoc_entries[MENU_ADHOC_RECEIVE].visibility = CTX_INVISIBLE; + } + // Go to first entry + for (i = 0; i < N_MENU_ADHOC_ENTRIES; i++) { + if (menu_adhoc_entries[i].visibility == CTX_VISIBLE) { + context_menu_adhoc.sel = i; + break; + } + } + + if (i == N_MENU_ADHOC_ENTRIES) + context_menu_adhoc.sel = -1; +} + void setContextMenuSortVisibilities() { int i; @@ -601,6 +735,21 @@ void setContextMenuNewVisibilities() { } } + FileListEntry *file_entry = fileListGetNthEntry(&file_list, base_pos + rel_pos); + if (file_entry) { + snprintf(cur_file, MAX_PATH_LENGTH, "%s%s", file_list.path, file_entry->name); + if (strncmp(cur_file, VITASHELL_BOOKMARKS_PATH, MAX_PATH_LENGTH) == 0) { + menu_new_entries[MENU_NEW_BOOKMARK].visibility = CTX_INVISIBLE; + } + // Invisble entries when on '..' + if (strcmp(file_entry->name, DIR_UP) == 0 || file_entry->is_symlink) { + menu_new_entries[MENU_NEW_BOOKMARK].visibility = CTX_INVISIBLE; + } + } else { + menu_new_entries[MENU_NEW_BOOKMARK].visibility = CTX_INVISIBLE; + } + + if (i == N_MENU_NEW_ENTRIES) context_menu_new.sel = -1; } @@ -999,18 +1148,18 @@ static int contextMenuMainEnterCallback(int sel, void *context) { return CONTEXT_MENU_MORE_OPENING; } - case MENU_MAIN_ENTRY_SEND: + case MENU_MAIN_ENTRY_BOOKMARKS: { - initNetCheckDialog(SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_JOIN, 60 * 1000 * 1000); - setDialogStep(DIALOG_STEP_ADHOC_SEND_NETCHECK); - break; + setContextMenu(&context_menu_bookmarks); + setContextMenuBookmarksVisibilities(); + return CONTEXT_MENU_MORE_OPENING; } - case MENU_MAIN_ENTRY_RECEIVE: + case MENU_MAIN_ENTRY_ADHOC: { - initNetCheckDialog(SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_CONN, 0); - setDialogStep(DIALOG_STEP_ADHOC_RECEIVE_NETCHECK); - break; + setContextMenu(&context_menu_adhoc); + setContextMenuAdhocVisibilities(); + return CONTEXT_MENU_MORE_OPENING; } } @@ -1032,11 +1181,47 @@ static int contextMenuSortEnterCallback(int sel, void *context) { break; } + last_set_sort_mode = sort_mode; + // Refresh list refreshFileList(); return CONTEXT_MENU_CLOSING; } +static int contextMenuBookmarksEnterCallback(int sel, void *context) { + switch (sel) { + case MENU_BOOKMARKS_SHOW_BOOKMARKS: + { + char path[MAX_PATH_LENGTH] = VITASHELL_BOOKMARKS_PATH; + jump_to_directory_track_current_path(path); + break; + } + case MENU_BOOKMARKS_RECENT_FILES: + { + char path[MAX_PATH_LENGTH] = VITASHELL_RECENT_PATH; + jump_to_directory_track_current_path(path); + break; + } + } + return CONTEXT_MENU_CLOSING; +} + +static int contextMenuAdhocEnterCallback(int sel, void *context) { + switch(sel) { + case MENU_ADHOC_RECEIVE: { + initNetCheckDialog(SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_CONN, 0); + setDialogStep(DIALOG_STEP_ADHOC_RECEIVE_NETCHECK); + break; + }; + case MENU_ADHOC_SEND : { + initNetCheckDialog(SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_JOIN, 60 * 1000 * 1000); + setDialogStep(DIALOG_STEP_ADHOC_SEND_NETCHECK); + } + break; + } + return CONTEXT_MENU_CLOSING; +} + static int contextMenuMoreEnterCallback(int sel, void *context) { switch (sel) { @@ -1156,7 +1341,6 @@ static int contextMenuMoreEnterCallback(int sel, void *context) { return CONTEXT_MENU_CLOSING; } - static int contextMenuNewEnterCallback(int sel, void *context) { switch (sel) { case MENU_NEW_FILE: { @@ -1204,6 +1388,26 @@ static int contextMenuNewEnterCallback(int sel, void *context) { MAX_NAME_LENGTH, SCE_IME_TYPE_BASIC_LATIN, 0, 0); setDialogStep(DIALOG_STEP_NEW_FOLDER); break; + }; + case MENU_NEW_BOOKMARK: { + FileListEntry *file_entry = fileListGetNthEntry(&file_list, base_pos + rel_pos); + if (file_entry) { + snprintf(cur_file, MAX_PATH_LENGTH, "%s%s", file_list.path, file_entry->name); + char target[MAX_PATH_LENGTH]; + char name[MAX_PATH_LENGTH]; + strncpy(name, file_entry->name, MAX_PATH_LENGTH); + removeEndSlash(name); + snprintf(target, MAX_PATH_LENGTH, "%s%s."SYMLINK_EXT, VITASHELL_BOOKMARKS_PATH, name); + int res; + if ((res = createSymLink(target, cur_file)) < 0) { + errorDialog(res); + } else { + infoDialog(language_container[BOOKMARK_CREATED]); + } + } else { + errorDialog(-2); + } + break; } } // Refresh list diff --git a/main_context.h b/main_context.h index c981b0a..94d349d 100644 --- a/main_context.h +++ b/main_context.h @@ -35,6 +35,8 @@ void initContextMenuWidth(); void setContextMenuHomeVisibilities(); void setContextMenuMainVisibilities(); void setContextMenuSortVisibilities(); +void setContextMenuBookmarksVisibilities(); +void setContextMenuAdhocVisibilities(); void setContextMenuMoreVisibilities(); void setContextMenuNewVisibilities(); diff --git a/resources/default/colors.txt b/resources/default/colors.txt index 27304a3..073e36e 100644 --- a/resources/default/colors.txt +++ b/resources/default/colors.txt @@ -24,6 +24,8 @@ ARCHIVE_COLOR = 0xFF007FFF # Orange SCROLL_BAR_COLOR = 0xFFFF7F00 # Azure SCROLL_BAR_BG_COLOR = 0xFF7F7F7F # Gray MARKED_COLOR = 0x4FFF7F00 # Azure with alpha +FILE_SYMLINK_COLOR = 0xFFFFFFFF # White +FOLDER_SYMLINK_COLOR = 0xFFFFFFFF # White # Context menu colors CONTEXT_MENU_TEXT_COLOR = 0xFFFFFFFF # White diff --git a/resources/default/file_symlink_icon.png b/resources/default/file_symlink_icon.png new file mode 100644 index 0000000..42a72ee Binary files /dev/null and b/resources/default/file_symlink_icon.png differ diff --git a/resources/default/folder_symlink_icon.png b/resources/default/folder_symlink_icon.png new file mode 100644 index 0000000..4a671e3 Binary files /dev/null and b/resources/default/folder_symlink_icon.png differ diff --git a/resources/english_us.txt b/resources/english_us.txt index 8977da4..dcb10bd 100644 --- a/resources/english_us.txt +++ b/resources/english_us.txt @@ -81,6 +81,12 @@ EXPORT_MEDIA = "Export media" CUT = "Cut" INSERT_EMPTY_LINE = "Insert empty line" SEARCH = "Search" +BOOKMARKS = "Bookmarks" +ADHOC_TRANSFER = "Adhoc" +BOOKMARKS_SHOW = "Show bookmarks" +BOOKMARKS_NEW = "New bookmark" +BOOKMARK_CREATED = "Bookmark added" +RECENT_FILES_SHOW = "Recent files" # File browser properties strings PROPERTY_NAME = "Name" diff --git a/theme.c b/theme.c index d991a99..59df7b8 100644 --- a/theme.c +++ b/theme.c @@ -34,7 +34,9 @@ INCLUDE_EXTERN_RESOURCE(default_cover_png); INCLUDE_EXTERN_RESOURCE(default_fastforward_png); INCLUDE_EXTERN_RESOURCE(default_fastrewind_png); INCLUDE_EXTERN_RESOURCE(default_file_icon_png); +INCLUDE_EXTERN_RESOURCE(default_file_symlink_icon_png); INCLUDE_EXTERN_RESOURCE(default_folder_icon_png); +INCLUDE_EXTERN_RESOURCE(default_folder_symlink_icon_png); INCLUDE_EXTERN_RESOURCE(default_ftp_png); INCLUDE_EXTERN_RESOURCE(default_image_icon_png); INCLUDE_EXTERN_RESOURCE(default_pause_png); @@ -58,6 +60,8 @@ int SETTINGS_MENU_OPTION_COLOR; // File browser colors int FOCUS_COLOR; int FILE_COLOR; +int FILE_SYMLINK_COLOR; +int FOLDER_SYMLINK_COLOR; int SFO_COLOR; int TXT_COLOR; int FOLDER_COLOR; @@ -105,7 +109,8 @@ int AUDIO_TIME_TOTAL; int AUDIO_TIME_BAR; int AUDIO_TIME_BAR_BG; -vita2d_texture *folder_icon = NULL, *file_icon = NULL, *archive_icon = NULL, *image_icon = NULL, *audio_icon = NULL, *sfo_icon = NULL, *text_icon = NULL, +vita2d_texture *folder_icon = NULL, *folder_symlink_icon = NULL, *file_icon = NULL,*file_symlink_icon = NULL, *archive_icon =NULL, *image_icon = NULL, + *audio_icon = NULL, *sfo_icon = NULL, *text_icon = NULL, *ftp_image = NULL, *dialog_image = NULL, *context_image = NULL, *context_more_image = NULL, *settings_image = NULL, *battery_image = NULL, *battery_bar_red_image = NULL, *battery_bar_green_image = NULL, *battery_bar_charge_image = NULL, *bg_browser_image = NULL, *bg_hex_image = NULL, *bg_text_image = NULL, *bg_photo_image = NULL, *bg_audio_image = NULL, *cover_image = NULL, *play_image = NULL, *pause_image = NULL, *fastforward_image = NULL, *fastrewind_image = NULL; @@ -142,7 +147,11 @@ ThemeImage theme_images[] = { { "fastforward.png", &_binary_resources_default_fastforward_png_start, &fastforward_image }, { "fastrewind.png", &_binary_resources_default_fastrewind_png_start, &fastrewind_image }, { "file_icon.png", &_binary_resources_default_file_icon_png_start, &file_icon }, + { "file_symlink_icon.png", &_binary_resources_default_file_symlink_icon_png_start, + &file_symlink_icon }, { "folder_icon.png", &_binary_resources_default_folder_icon_png_start, &folder_icon }, + { "folder_symlink_icon.png", &_binary_resources_default_folder_symlink_icon_png_start, + &folder_symlink_icon }, { "ftp.png", &_binary_resources_default_ftp_png_start, &ftp_image }, { "image_icon.png", &_binary_resources_default_image_icon_png_start, &image_icon }, { "pause.png", &_binary_resources_default_pause_png_start, &pause_image }, @@ -174,6 +183,8 @@ void loadTheme() { // File browser colors COLOR_ENTRY(FOCUS_COLOR), COLOR_ENTRY(FILE_COLOR), + COLOR_ENTRY(FILE_SYMLINK_COLOR), + COLOR_ENTRY(FOLDER_SYMLINK_COLOR), COLOR_ENTRY(SFO_COLOR), COLOR_ENTRY(TXT_COLOR), COLOR_ENTRY(FOLDER_COLOR), diff --git a/theme.h b/theme.h index 56fc890..62dd824 100644 --- a/theme.h +++ b/theme.h @@ -43,6 +43,8 @@ extern int ARCHIVE_COLOR; extern int SCROLL_BAR_COLOR; extern int SCROLL_BAR_BG_COLOR; extern int MARKED_COLOR; +extern int FILE_SYMLINK_COLOR; +extern int FOLDER_SYMLINK_COLOR; // Context menu colors extern int CONTEXT_MENU_TEXT_COLOR; @@ -85,7 +87,8 @@ extern int AUDIO_TIME_BAR_BG; extern vita2d_texture *folder_icon, *file_icon, *archive_icon, *image_icon, *audio_icon, *sfo_icon, *text_icon, *ftp_image, *dialog_image, *context_image, *context_more_image, *settings_image, *battery_image, *battery_bar_red_image, *battery_bar_green_image, *battery_bar_charge_image, *bg_browser_image, *bg_hex_image, *bg_text_image, - *bg_photo_image, *bg_audio_image, *cover_image, *play_image, *pause_image, *fastforward_image, *fastrewind_image; + *bg_photo_image, *bg_audio_image, *cover_image, *play_image, *pause_image, + *fastforward_image, *fastrewind_image, *folder_symlink_icon, *file_symlink_icon; extern vita2d_texture *wallpaper_image; extern vita2d_texture *previous_wallpaper_image, *current_wallpaper_image; diff --git a/vitashell_error.h b/vitashell_error.h index 19bde65..114892a 100644 --- a/vitashell_error.h +++ b/vitashell_error.h @@ -34,6 +34,17 @@ enum VitaShellErrors { VITASHELL_ERROR_DST_IS_SUBFOLDER_OF_SRC = 0xF0020001, VITASHELL_ERROR_INVALID_TITLEID = 0xF0030000, + + VITASHELL_ERROR_SYMLINK_INVALID_PATH = 0xF0040000, + VITASHELL_ERROR_SYMLINK_CANT_RESOLVE_BASEDIR = 0xF0040001, + VITASHELL_ERROR_SYMLINK_CANT_RESOLVE_FILENAME = 0xF0040002, + VITASHELL_ERROR_SYMLINK_INTERNAL = 0xF0040003, + + VITASHELL_ERROR_NAVIGATION = 0xF0050000, + + + + }; #endif