mirror of
https://github.com/joel16/VitaShell.git
synced 2024-11-26 21:30:45 +00:00
1205 lines
30 KiB
C
1205 lines
30 KiB
C
/*
|
|
VitaShell
|
|
Copyright (C) 2015-2018, TheFloW
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "main.h"
|
|
#include "browser.h"
|
|
#include "init.h"
|
|
#include "archive.h"
|
|
#include "file.h"
|
|
#include "utils.h"
|
|
#include "sha1.h"
|
|
#include "strnatcmp.h"
|
|
|
|
static char *devices[] = {
|
|
"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)
|
|
return fd;
|
|
|
|
int size = sceIoLseek32(fd, 0, SCE_SEEK_END);
|
|
sceIoLseek32(fd, 0, SCE_SEEK_SET);
|
|
|
|
*buffer = malloc(size);
|
|
if (!*buffer) {
|
|
sceIoClose(fd);
|
|
return VITASHELL_ERROR_NO_MEMORY;
|
|
}
|
|
|
|
int read = sceIoRead(fd, *buffer, size);
|
|
sceIoClose(fd);
|
|
|
|
return read;
|
|
}
|
|
|
|
int ReadFile(const char *file, void *buf, int size) {
|
|
SceUID fd = sceIoOpen(file, SCE_O_RDONLY, 0);
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
int read = sceIoRead(fd, buf, size);
|
|
|
|
sceIoClose(fd);
|
|
return read;
|
|
}
|
|
|
|
int WriteFile(const char *file, const void *buf, int size) {
|
|
SceUID fd = sceIoOpen(file, SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
int written = sceIoWrite(fd, buf, size);
|
|
|
|
sceIoClose(fd);
|
|
return written;
|
|
}
|
|
|
|
int getFileSize(const char *file) {
|
|
SceUID fd = sceIoOpen(file, SCE_O_RDONLY, 0);
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
int fileSize = sceIoLseek(fd, 0, SCE_SEEK_END);
|
|
|
|
sceIoClose(fd);
|
|
return fileSize;
|
|
}
|
|
|
|
int checkFileExist(const char *file) {
|
|
SceUID fd = sceIoOpen(file, SCE_O_RDONLY, 0);
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
sceIoClose(fd);
|
|
return 1;
|
|
}
|
|
|
|
int checkFolderExist(const char *folder) {
|
|
SceUID dfd = sceIoDopen(folder);
|
|
if (dfd < 0)
|
|
return 0;
|
|
|
|
sceIoDclose(dfd);
|
|
return 1;
|
|
}
|
|
|
|
int getFileSha1(const char *file, uint8_t *pSha1Out, FileProcessParam *param) {
|
|
// Set up SHA1 context
|
|
SHA1_CTX ctx;
|
|
sha1_init(&ctx);
|
|
|
|
// Open the file to read, else return the error
|
|
SceUID fd = sceIoOpen(file, SCE_O_RDONLY, 0);
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
// Open up the buffer for copying data into
|
|
void *buf = memalign(4096, TRANSFER_SIZE);
|
|
|
|
// Actually take the SHA1 sum
|
|
while (1) {
|
|
int read = sceIoRead(fd, buf, TRANSFER_SIZE);
|
|
|
|
if (read < 0) {
|
|
free(buf);
|
|
sceIoClose(fd);
|
|
return read;
|
|
}
|
|
|
|
if (read == 0)
|
|
break;
|
|
|
|
sha1_update(&ctx, buf, read);
|
|
|
|
if (param) {
|
|
// Defined in io_process.c, check to make sure pointer isn't null before incrementing
|
|
if (param->value)
|
|
(*param->value)++; // Note: Max value is filesize/TRANSFER_SIZE
|
|
|
|
if (param->SetProgress)
|
|
param->SetProgress(param->value ? *param->value : 0, param->max);
|
|
|
|
// Check to see if param->cancelHandler exists, if so call it and free memory if canceled
|
|
if (param->cancelHandler && param->cancelHandler()) {
|
|
free(buf);
|
|
sceIoClose(fd);
|
|
return 0;
|
|
}
|
|
|
|
// This is CPU intensive so the progress bar won't refresh unless we sleep
|
|
// DIALOG_WAIT seemed too long for this application
|
|
// so I set it to 1/2 of a second every 8192 TRANSFER_SIZE blocks
|
|
if ((*param->value) % 8192 == 0)
|
|
sceKernelDelayThread(500000);
|
|
}
|
|
}
|
|
|
|
// Final iteration of SHA1 sum, dump final value into pSha1Out buffer
|
|
sha1_final(&ctx, pSha1Out);
|
|
|
|
// Free up file buffer
|
|
free(buf);
|
|
|
|
// Close file proper
|
|
sceIoClose(fd);
|
|
return 1;
|
|
}
|
|
|
|
int getPathInfo(const char *path, uint64_t *size, uint32_t *folders,
|
|
uint32_t *files, int (* handler)(const char *path)) {
|
|
SceUID dfd = sceIoDopen(path);
|
|
if (dfd >= 0) {
|
|
int res = 0;
|
|
|
|
do {
|
|
SceIoDirent dir;
|
|
memset(&dir, 0, sizeof(SceIoDirent));
|
|
|
|
res = sceIoDread(dfd, &dir);
|
|
if (res > 0) {
|
|
char *new_path = malloc(strlen(path) + strlen(dir.d_name) + 2);
|
|
snprintf(new_path, MAX_PATH_LENGTH, "%s%s%s", path, hasEndSlash(path) ? "" : "/", dir.d_name);
|
|
|
|
if (handler && handler(new_path)) {
|
|
free(new_path);
|
|
continue;
|
|
}
|
|
|
|
if (SCE_S_ISDIR(dir.d_stat.st_mode)) {
|
|
int ret = getPathInfo(new_path, size, folders, files, handler);
|
|
if (ret <= 0) {
|
|
free(new_path);
|
|
sceIoDclose(dfd);
|
|
return ret;
|
|
}
|
|
} else {
|
|
if (size)
|
|
(*size) += dir.d_stat.st_size;
|
|
|
|
if (files)
|
|
(*files)++;
|
|
}
|
|
|
|
free(new_path);
|
|
}
|
|
} while (res > 0);
|
|
|
|
sceIoDclose(dfd);
|
|
|
|
if (folders)
|
|
(*folders)++;
|
|
} else {
|
|
if (handler && handler(path))
|
|
return 1;
|
|
|
|
if (size) {
|
|
SceIoStat stat;
|
|
memset(&stat, 0, sizeof(SceIoStat));
|
|
|
|
int res = sceIoGetstat(path, &stat);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
(*size) += stat.st_size;
|
|
}
|
|
|
|
if (files)
|
|
(*files)++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int removePath(const char *path, FileProcessParam *param) {
|
|
SceUID dfd = sceIoDopen(path);
|
|
if (dfd >= 0) {
|
|
int res = 0;
|
|
|
|
do {
|
|
SceIoDirent dir;
|
|
memset(&dir, 0, sizeof(SceIoDirent));
|
|
|
|
res = sceIoDread(dfd, &dir);
|
|
if (res > 0) {
|
|
char *new_path = malloc(strlen(path) + strlen(dir.d_name) + 2);
|
|
snprintf(new_path, MAX_PATH_LENGTH, "%s%s%s", path, hasEndSlash(path) ? "" : "/", dir.d_name);
|
|
|
|
if (SCE_S_ISDIR(dir.d_stat.st_mode)) {
|
|
int ret = removePath(new_path, param);
|
|
if (ret <= 0) {
|
|
free(new_path);
|
|
sceIoDclose(dfd);
|
|
return ret;
|
|
}
|
|
} else {
|
|
int ret = sceIoRemove(new_path);
|
|
if (ret < 0) {
|
|
free(new_path);
|
|
sceIoDclose(dfd);
|
|
return ret;
|
|
}
|
|
|
|
if (param) {
|
|
if (param->value)
|
|
(*param->value)++;
|
|
|
|
if (param->SetProgress)
|
|
param->SetProgress(param->value ? *param->value : 0, param->max);
|
|
|
|
if (param->cancelHandler && param->cancelHandler()) {
|
|
free(new_path);
|
|
sceIoDclose(dfd);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(new_path);
|
|
}
|
|
} while (res > 0);
|
|
|
|
sceIoDclose(dfd);
|
|
|
|
int ret = sceIoRmdir(path);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (param) {
|
|
if (param->value)
|
|
(*param->value)++;
|
|
|
|
if (param->SetProgress)
|
|
param->SetProgress(param->value ? *param->value : 0, param->max);
|
|
|
|
if (param->cancelHandler && param->cancelHandler()) {
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
int ret = sceIoRemove(path);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (param) {
|
|
if (param->value)
|
|
(*param->value)++;
|
|
|
|
if (param->SetProgress)
|
|
param->SetProgress(param->value ? *param->value : 0, param->max);
|
|
|
|
if (param->cancelHandler && param->cancelHandler()) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int copyFile(const char *src_path, const char *dst_path, FileProcessParam *param) {
|
|
// The source and destination paths are identical
|
|
if (strcasecmp(src_path, dst_path) == 0) {
|
|
return VITASHELL_ERROR_SRC_AND_DST_IDENTICAL;
|
|
}
|
|
|
|
// The destination is a subfolder of the source folder
|
|
int len = strlen(src_path);
|
|
if (strncasecmp(src_path, dst_path, len) == 0 && (dst_path[len] == '/' || dst_path[len - 1] == '/')) {
|
|
return VITASHELL_ERROR_DST_IS_SUBFOLDER_OF_SRC;
|
|
}
|
|
|
|
SceUID fdsrc = sceIoOpen(src_path, SCE_O_RDONLY, 0);
|
|
if (fdsrc < 0)
|
|
return fdsrc;
|
|
|
|
SceUID fddst = sceIoOpen(dst_path, SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
|
|
if (fddst < 0) {
|
|
sceIoClose(fdsrc);
|
|
return fddst;
|
|
}
|
|
|
|
void *buf = memalign(4096, TRANSFER_SIZE);
|
|
|
|
while (1) {
|
|
int read = sceIoRead(fdsrc, buf, TRANSFER_SIZE);
|
|
|
|
if (read < 0) {
|
|
free(buf);
|
|
|
|
sceIoClose(fddst);
|
|
sceIoClose(fdsrc);
|
|
|
|
sceIoRemove(dst_path);
|
|
|
|
return read;
|
|
}
|
|
|
|
if (read == 0)
|
|
break;
|
|
|
|
int written = sceIoWrite(fddst, buf, read);
|
|
|
|
if (written < 0) {
|
|
free(buf);
|
|
|
|
sceIoClose(fddst);
|
|
sceIoClose(fdsrc);
|
|
|
|
sceIoRemove(dst_path);
|
|
|
|
return written;
|
|
}
|
|
|
|
if (param) {
|
|
if (param->value)
|
|
(*param->value) += read;
|
|
|
|
if (param->SetProgress)
|
|
param->SetProgress(param->value ? *param->value : 0, param->max);
|
|
|
|
if (param->cancelHandler && param->cancelHandler()) {
|
|
free(buf);
|
|
|
|
sceIoClose(fddst);
|
|
sceIoClose(fdsrc);
|
|
|
|
sceIoRemove(dst_path);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(buf);
|
|
|
|
// Inherit file stat
|
|
SceIoStat stat;
|
|
memset(&stat, 0, sizeof(SceIoStat));
|
|
sceIoGetstatByFd(fdsrc, &stat);
|
|
sceIoChstatByFd(fddst, &stat, 0x3B);
|
|
|
|
sceIoClose(fddst);
|
|
sceIoClose(fdsrc);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int copyPath(const char *src_path, const char *dst_path, FileProcessParam *param) {
|
|
// The source and destination paths are identical
|
|
if (strcasecmp(src_path, dst_path) == 0) {
|
|
return VITASHELL_ERROR_SRC_AND_DST_IDENTICAL;
|
|
}
|
|
|
|
// The destination is a subfolder of the source folder
|
|
int len = strlen(src_path);
|
|
if (strncasecmp(src_path, dst_path, len) == 0 && (dst_path[len] == '/' || dst_path[len - 1] == '/')) {
|
|
return VITASHELL_ERROR_DST_IS_SUBFOLDER_OF_SRC;
|
|
}
|
|
|
|
SceUID dfd = sceIoDopen(src_path);
|
|
if (dfd >= 0) {
|
|
SceIoStat stat;
|
|
memset(&stat, 0, sizeof(SceIoStat));
|
|
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);
|
|
}
|
|
|
|
if (param) {
|
|
if (param->value)
|
|
(*param->value) += DIRECTORY_SIZE;
|
|
|
|
if (param->SetProgress)
|
|
param->SetProgress(param->value ? *param->value : 0, param->max);
|
|
|
|
if (param->cancelHandler && param->cancelHandler()) {
|
|
sceIoDclose(dfd);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int res = 0;
|
|
|
|
do {
|
|
SceIoDirent dir;
|
|
memset(&dir, 0, sizeof(SceIoDirent));
|
|
|
|
res = sceIoDread(dfd, &dir);
|
|
if (res > 0) {
|
|
char *new_src_path = malloc(strlen(src_path) + strlen(dir.d_name) + 2);
|
|
snprintf(new_src_path, MAX_PATH_LENGTH, "%s%s%s", src_path, hasEndSlash(src_path) ? "" : "/", dir.d_name);
|
|
|
|
char *new_dst_path = malloc(strlen(dst_path) + strlen(dir.d_name) + 2);
|
|
snprintf(new_dst_path, MAX_PATH_LENGTH, "%s%s%s", dst_path, hasEndSlash(dst_path) ? "" : "/", dir.d_name);
|
|
|
|
int ret = 0;
|
|
|
|
if (SCE_S_ISDIR(dir.d_stat.st_mode)) {
|
|
ret = copyPath(new_src_path, new_dst_path, param);
|
|
} else {
|
|
ret = copyFile(new_src_path, new_dst_path, param);
|
|
}
|
|
|
|
free(new_dst_path);
|
|
free(new_src_path);
|
|
|
|
if (ret <= 0) {
|
|
sceIoDclose(dfd);
|
|
return ret;
|
|
}
|
|
}
|
|
} while (res > 0);
|
|
|
|
sceIoDclose(dfd);
|
|
} else {
|
|
return copyFile(src_path, dst_path, param);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int movePath(const char *src_path, const char *dst_path, int flags, FileProcessParam *param) {
|
|
// The source and destination paths are identical
|
|
if (strcasecmp(src_path, dst_path) == 0) {
|
|
return VITASHELL_ERROR_SRC_AND_DST_IDENTICAL;
|
|
}
|
|
|
|
// The destination is a subfolder of the source folder
|
|
int len = strlen(src_path);
|
|
if (strncasecmp(src_path, dst_path, len) == 0 && (dst_path[len] == '/' || dst_path[len - 1] == '/')) {
|
|
return VITASHELL_ERROR_DST_IS_SUBFOLDER_OF_SRC;
|
|
}
|
|
|
|
int res = sceIoRename(src_path, dst_path);
|
|
|
|
if (res == SCE_ERROR_ERRNO_EEXIST && flags & (MOVE_INTEGRATE | MOVE_REPLACE)) {
|
|
// Src stat
|
|
SceIoStat src_stat;
|
|
memset(&src_stat, 0, sizeof(SceIoStat));
|
|
res = sceIoGetstat(src_path, &src_stat);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
// Dst stat
|
|
SceIoStat dst_stat;
|
|
memset(&dst_stat, 0, sizeof(SceIoStat));
|
|
res = sceIoGetstat(dst_path, &dst_stat);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
// Is dir
|
|
int src_is_dir = SCE_S_ISDIR(src_stat.st_mode);
|
|
int dst_is_dir = SCE_S_ISDIR(dst_stat.st_mode);
|
|
|
|
// One of them is a file and the other a directory, no replacement or integration possible
|
|
if (src_is_dir != dst_is_dir)
|
|
return VITASHELL_ERROR_INVALID_TYPE;
|
|
|
|
// Replace file
|
|
if (!src_is_dir && !dst_is_dir && flags & MOVE_REPLACE) {
|
|
sceIoRemove(dst_path);
|
|
|
|
res = sceIoRename(src_path, dst_path);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Integrate directory
|
|
if (src_is_dir && dst_is_dir && flags & MOVE_INTEGRATE) {
|
|
SceUID dfd = sceIoDopen(src_path);
|
|
if (dfd < 0)
|
|
return dfd;
|
|
|
|
int res = 0;
|
|
|
|
do {
|
|
SceIoDirent dir;
|
|
memset(&dir, 0, sizeof(SceIoDirent));
|
|
|
|
res = sceIoDread(dfd, &dir);
|
|
if (res > 0) {
|
|
char *new_src_path = malloc(strlen(src_path) + strlen(dir.d_name) + 2);
|
|
snprintf(new_src_path, MAX_PATH_LENGTH, "%s%s%s", src_path, hasEndSlash(src_path) ? "" : "/", dir.d_name);
|
|
|
|
char *new_dst_path = malloc(strlen(dst_path) + strlen(dir.d_name) + 2);
|
|
snprintf(new_dst_path, MAX_PATH_LENGTH, "%s%s%s", dst_path, hasEndSlash(dst_path) ? "" : "/", dir.d_name);
|
|
|
|
// Recursive move
|
|
int ret = movePath(new_src_path, new_dst_path, flags, param);
|
|
|
|
free(new_dst_path);
|
|
free(new_src_path);
|
|
|
|
if (ret <= 0) {
|
|
sceIoDclose(dfd);
|
|
return ret;
|
|
}
|
|
}
|
|
} while (res > 0);
|
|
|
|
sceIoDclose(dfd);
|
|
|
|
// Integrated, now remove this directory
|
|
sceIoRmdir(src_path);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
typedef struct {
|
|
char *extension;
|
|
int type;
|
|
} ExtensionType;
|
|
|
|
static ExtensionType extension_types[] = {
|
|
{ ".7Z", FILE_TYPE_ARCHIVE },
|
|
{ ".AR", FILE_TYPE_ARCHIVE },
|
|
{ ".BMP", FILE_TYPE_BMP },
|
|
{ ".BZ2", FILE_TYPE_ARCHIVE },
|
|
{ ".CPIO", FILE_TYPE_ARCHIVE },
|
|
{ ".GRZ", FILE_TYPE_ARCHIVE },
|
|
{ ".GZ", FILE_TYPE_ARCHIVE },
|
|
{ ".INI", FILE_TYPE_INI },
|
|
{ ".ISO", FILE_TYPE_ARCHIVE },
|
|
{ ".JPEG", FILE_TYPE_JPEG },
|
|
{ ".JPG", FILE_TYPE_JPEG },
|
|
{ ".LRZ", FILE_TYPE_ARCHIVE },
|
|
{ ".LZ", FILE_TYPE_ARCHIVE },
|
|
{ ".LZ4", FILE_TYPE_ARCHIVE },
|
|
{ ".LZMA", FILE_TYPE_ARCHIVE },
|
|
{ ".LZO", FILE_TYPE_ARCHIVE },
|
|
{ ".MP3", FILE_TYPE_MP3 },
|
|
{ ".MP4", FILE_TYPE_MP4 },
|
|
{ ".MTREE", FILE_TYPE_ARCHIVE },
|
|
{ ".OGG", FILE_TYPE_OGG },
|
|
{ ".PNG", FILE_TYPE_PNG },
|
|
{ ".PSARC", FILE_TYPE_ARCHIVE },
|
|
{ ".PSP2ARC", FILE_TYPE_ARCHIVE },
|
|
{ ".PSP2DMP", FILE_TYPE_PSP2DMP },
|
|
{ ".RAR", FILE_TYPE_ARCHIVE },
|
|
{ ".SFO", FILE_TYPE_SFO },
|
|
{ ".SHAR", FILE_TYPE_ARCHIVE },
|
|
{ ".TAR", FILE_TYPE_ARCHIVE },
|
|
{ ".TAZ", FILE_TYPE_ARCHIVE },
|
|
{ ".TBZ", FILE_TYPE_ARCHIVE },
|
|
{ ".TBZ2", FILE_TYPE_ARCHIVE },
|
|
{ ".TGZ", FILE_TYPE_ARCHIVE },
|
|
{ ".TLZ", FILE_TYPE_ARCHIVE },
|
|
{ ".TMP", FILE_TYPE_PSP2DMP },
|
|
{ ".TXT", FILE_TYPE_TXT },
|
|
{ ".TXZ", FILE_TYPE_ARCHIVE },
|
|
{ ".TZ", FILE_TYPE_ARCHIVE },
|
|
{ ".TZ2", FILE_TYPE_ARCHIVE },
|
|
{ ".TZMA", FILE_TYPE_ARCHIVE },
|
|
{ ".TZO", FILE_TYPE_ARCHIVE },
|
|
{ ".TZST", FILE_TYPE_ARCHIVE },
|
|
{ ".UU", FILE_TYPE_ARCHIVE },
|
|
{ ".VPK", FILE_TYPE_VPK },
|
|
{ ".WAR", FILE_TYPE_ARCHIVE },
|
|
{ ".XAR", FILE_TYPE_ARCHIVE },
|
|
{ ".XML", FILE_TYPE_XML },
|
|
{ ".XZ", FILE_TYPE_ARCHIVE },
|
|
{ ".Z", FILE_TYPE_ARCHIVE },
|
|
{ ".ZIP", FILE_TYPE_ARCHIVE },
|
|
{ ".ZST", FILE_TYPE_ARCHIVE },
|
|
};
|
|
|
|
int getFileType(const char *file) {
|
|
char *p = strrchr(file, '.');
|
|
if (p) {
|
|
int i;
|
|
for (i = 0; i < (sizeof(extension_types) / sizeof(ExtensionType)); i++) {
|
|
if (strcasecmp(p, extension_types[i].extension) == 0) {
|
|
return extension_types[i].type;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FILE_TYPE_UNKNOWN;
|
|
}
|
|
|
|
int getNumberOfDevices() {
|
|
return N_DEVICES;
|
|
}
|
|
|
|
char **getDevices() {
|
|
return devices;
|
|
}
|
|
|
|
FileListEntry *fileListCopyEntry(FileListEntry *src) {
|
|
FileListEntry *dst = malloc(sizeof(FileListEntry));
|
|
if (!dst)
|
|
return NULL;
|
|
|
|
memcpy(dst, src, sizeof(FileListEntry));
|
|
dst->name = malloc(src->name_length + 1);
|
|
strcpy(dst->name, src->name);
|
|
return dst;
|
|
}
|
|
|
|
FileListEntry *fileListFindEntry(FileList *list, const char *name) {
|
|
if (!list)
|
|
return NULL;
|
|
|
|
FileListEntry *entry = list->head;
|
|
|
|
int name_length = strlen(name);
|
|
|
|
while (entry) {
|
|
if (entry->name_length == name_length && strcasecmp(entry->name, name) == 0)
|
|
return entry;
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
FileListEntry *fileListGetNthEntry(FileList *list, int n) {
|
|
if (!list)
|
|
return NULL;
|
|
|
|
FileListEntry *entry = list->head;
|
|
|
|
while (n > 0 && entry) {
|
|
n--;
|
|
entry = entry->next;
|
|
}
|
|
|
|
if (n != 0)
|
|
return NULL;
|
|
|
|
return entry;
|
|
}
|
|
|
|
int fileListGetNumberByName(FileList *list, const char *name) {
|
|
if (!list)
|
|
return VITASHELL_ERROR_ILLEGAL_ADDR;
|
|
|
|
FileListEntry *entry = list->head;
|
|
|
|
int name_length = strlen(name);
|
|
|
|
int n = 0;
|
|
|
|
while (entry) {
|
|
if (entry->name_length == name_length && strcasecmp(entry->name, name) == 0)
|
|
return n;
|
|
|
|
n++;
|
|
entry = entry->next;
|
|
}
|
|
|
|
return VITASHELL_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
void fileListAddEntry(FileList *list, FileListEntry *entry, int sort) {
|
|
if (!list || !entry)
|
|
return;
|
|
|
|
entry->next = NULL;
|
|
entry->previous = NULL;
|
|
|
|
if (list->head == NULL) {
|
|
list->head = entry;
|
|
list->tail = entry;
|
|
} else {
|
|
if (sort != SORT_NONE) {
|
|
FileListEntry *p = list->head;
|
|
FileListEntry *previous = NULL;
|
|
|
|
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;
|
|
|
|
if (strcmp(p_name, "..") == 0) {
|
|
previous = p;
|
|
p = p->next;
|
|
continue;
|
|
}
|
|
|
|
// Sort by type
|
|
if (sort == SORT_BY_NAME) {
|
|
// First folders then files
|
|
if (entry->is_folder > p->is_folder)
|
|
break;
|
|
} else if (sort == SORT_BY_SIZE || sort == SORT_BY_DATE) {
|
|
// First files then folders
|
|
if (entry->is_folder < p->is_folder)
|
|
break;
|
|
}
|
|
|
|
if (sort == SORT_BY_NAME) {
|
|
// Sort by name within the same type
|
|
if (entry->is_folder == p->is_folder) {
|
|
if (strnatcasecmp(entry_name, p_name) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (sort == SORT_BY_SIZE) {
|
|
// Sort by name for folders
|
|
if (entry->is_folder && p->is_folder) {
|
|
if (strnatcasecmp(entry_name, p_name) < 0) {
|
|
break;
|
|
}
|
|
} else if (!entry->is_folder && !p->is_folder) {
|
|
// Sort by size for files
|
|
if (entry->size > p->size)
|
|
break;
|
|
|
|
// Sort by name for files with the same size
|
|
if (entry->size == p->size) {
|
|
if (strnatcasecmp(entry_name, p_name) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (sort == SORT_BY_DATE) {
|
|
if (entry->is_folder == p->is_folder) {
|
|
SceRtcTick entry_tick, p_tick;
|
|
sceRtcGetTick(&entry->mtime, &entry_tick);
|
|
sceRtcGetTick(&p->mtime, &p_tick);
|
|
|
|
// Sort by date within the same type
|
|
if (entry_tick.tick > p_tick.tick)
|
|
break;
|
|
|
|
// Sort by name for files and folders with the same date
|
|
if (entry_tick.tick == p_tick.tick) {
|
|
if (strnatcasecmp(entry_name, p_name) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
previous = p;
|
|
p = p->next;
|
|
}
|
|
|
|
if (previous == NULL) { // Order: entry (new head) -> p (old head)
|
|
entry->next = p;
|
|
p->previous = entry;
|
|
list->head = entry;
|
|
} else if (previous->next == NULL) { // Order: p (old tail) -> entry (new tail)
|
|
FileListEntry *tail = list->tail;
|
|
tail->next = entry;
|
|
entry->previous = tail;
|
|
list->tail = entry;
|
|
} else { // Order: previous -> entry -> p
|
|
previous->next = entry;
|
|
entry->previous = previous;
|
|
entry->next = p;
|
|
p->previous = entry;
|
|
}
|
|
} else {
|
|
FileListEntry *tail = list->tail;
|
|
tail->next = entry;
|
|
entry->previous = tail;
|
|
list->tail = entry;
|
|
}
|
|
}
|
|
|
|
list->length++;
|
|
}
|
|
|
|
int fileListRemoveEntry(FileList *list, FileListEntry *entry) {
|
|
if (!list || !entry)
|
|
return 0;
|
|
|
|
if (entry->previous) {
|
|
entry->previous->next = entry->next;
|
|
} else {
|
|
list->head = entry->next;
|
|
}
|
|
|
|
if (entry->next) {
|
|
entry->next->previous = entry->previous;
|
|
} else {
|
|
list->tail = entry->previous;
|
|
}
|
|
|
|
list->length--;
|
|
free(entry->name);
|
|
free(entry);
|
|
|
|
if (list->length == 0) {
|
|
list->head = NULL;
|
|
list->tail = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int fileListRemoveEntryByName(FileList *list, const char *name) {
|
|
if (!list)
|
|
return 0;
|
|
|
|
FileListEntry *entry = list->head;
|
|
FileListEntry *previous = NULL;
|
|
|
|
int name_length = strlen(name);
|
|
|
|
while (entry) {
|
|
if (entry->name_length == name_length && strcasecmp(entry->name, name) == 0) {
|
|
if (previous) {
|
|
previous->next = entry->next;
|
|
} else {
|
|
list->head = entry->next;
|
|
}
|
|
|
|
if (list->tail == entry) {
|
|
list->tail = previous;
|
|
}
|
|
|
|
list->length--;
|
|
free(entry->name);
|
|
free(entry);
|
|
|
|
if (list->length == 0) {
|
|
list->head = NULL;
|
|
list->tail = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
previous = entry;
|
|
entry = entry->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void fileListEmpty(FileList *list) {
|
|
if (!list)
|
|
return;
|
|
|
|
FileListEntry *entry = list->head;
|
|
|
|
while (entry) {
|
|
FileListEntry *next = entry->next;
|
|
free(entry->name);
|
|
free(entry);
|
|
entry = next;
|
|
}
|
|
|
|
list->head = NULL;
|
|
list->tail = NULL;
|
|
list->length = 0;
|
|
list->files = 0;
|
|
list->folders = 0;
|
|
}
|
|
|
|
int fileListGetDeviceEntries(FileList *list) {
|
|
if (!list)
|
|
return VITASHELL_ERROR_ILLEGAL_ADDR;
|
|
|
|
int i;
|
|
for (i = 0; i < N_DEVICES; i++) {
|
|
if (devices[i]) {
|
|
if (is_safe_mode && strcmp(devices[i], "ux0:") != 0)
|
|
continue;
|
|
|
|
SceIoStat stat;
|
|
memset(&stat, 0, sizeof(SceIoStat));
|
|
if (sceIoGetstat(devices[i], &stat) >= 0) {
|
|
FileListEntry *entry = malloc(sizeof(FileListEntry));
|
|
if (entry) {
|
|
entry->name_length = strlen(devices[i]);
|
|
entry->name = malloc(entry->name_length + 1);
|
|
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));
|
|
int res = sceIoDevctl(entry->name, 0x3001, NULL, 0, &info, sizeof(SceIoDevInfo));
|
|
if (res >= 0) {
|
|
entry->size = info.free_size;
|
|
entry->size2 = info.max_size;
|
|
} else {
|
|
if (strcmp(devices[i], "ux0:") == 0) {
|
|
sceAppMgrGetDevInfo("ux0:", (uint64_t *)&entry->size2, (uint64_t *)&entry->size);
|
|
} else {
|
|
entry->size = 0;
|
|
entry->size2 = 0;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
list->folders++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fileListGetDirectoryEntries(FileList *list, const char *path, int sort) {
|
|
if (!list)
|
|
return VITASHELL_ERROR_ILLEGAL_ADDR;
|
|
|
|
SceUID dfd = sceIoDopen(path);
|
|
if (dfd < 0)
|
|
return dfd;
|
|
|
|
FileListEntry *entry = malloc(sizeof(FileListEntry));
|
|
if (entry) {
|
|
entry->name_length = strlen(DIR_UP);
|
|
entry->name = malloc(entry->name_length + 1);
|
|
strcpy(entry->name, DIR_UP);
|
|
entry->is_folder = 1;
|
|
entry->type = FILE_TYPE_UNKNOWN;
|
|
entry->is_symlink = 0;
|
|
fileListAddEntry(list, entry, sort);
|
|
}
|
|
|
|
int res = 0;
|
|
|
|
do {
|
|
SceIoDirent dir;
|
|
memset(&dir, 0, sizeof(SceIoDirent));
|
|
|
|
res = sceIoDread(dfd, &dir);
|
|
if (res > 0) {
|
|
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);
|
|
strcpy(entry->name, dir.d_name);
|
|
addEndSlash(entry->name);
|
|
entry->type = FILE_TYPE_UNKNOWN;
|
|
list->folders++;
|
|
} else {
|
|
entry->name_length = strlen(dir.d_name);
|
|
entry->name = malloc(entry->name_length + 1);
|
|
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));
|
|
|
|
fileListAddEntry(list, entry, sort);
|
|
}
|
|
}
|
|
} while (res > 0);
|
|
|
|
sceIoDclose(dfd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fileListGetEntries(FileList *list, const char *path, int sort) {
|
|
if (!list)
|
|
return VITASHELL_ERROR_ILLEGAL_ADDR;
|
|
|
|
if (isInArchive()) {
|
|
return fileListGetArchiveEntries(list, path, sort);
|
|
}
|
|
|
|
if (strcasecmp(path, HOME_PATH) == 0) {
|
|
return fileListGetDeviceEntries(list);
|
|
}
|
|
|
|
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;
|
|
} |