file_io: Zip file support
Support for loading files stored inside a zip file. Treats zipped files as folders and allows loading files from inside the zip file. Files are loaded on-demand from the archive. Nested zip-files are currently not supported.
This commit is contained in:
683
file_io.cpp
683
file_io.cpp
@@ -14,6 +14,7 @@
|
||||
#include <linux/magic.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "osd.h"
|
||||
#include "fpga_io.h"
|
||||
#include "menu.h"
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "user_io.h"
|
||||
#include "cfg.h"
|
||||
#include "input.h"
|
||||
#include "miniz_zip.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
typedef std::vector<dirent> DirentVector;
|
||||
@@ -34,8 +36,181 @@ int iFirstEntry = 0;
|
||||
|
||||
static char full_path[1200];
|
||||
|
||||
struct fileZipArchive
|
||||
{
|
||||
mz_zip_archive archive;
|
||||
int index;
|
||||
mz_zip_reader_extract_iter_state* iter;
|
||||
__off64_t offset;
|
||||
};
|
||||
|
||||
static bool FileIsZipped(const std::string& path, std::string* zip_path, std::string* file_path)
|
||||
{
|
||||
const std::string zipext(".zip");
|
||||
auto it = std::search(path.begin(), path.end(),
|
||||
zipext.begin(), zipext.end(),
|
||||
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); });
|
||||
|
||||
if (it != path.end())
|
||||
{
|
||||
if (zip_path)
|
||||
{
|
||||
*zip_path = std::string(path.begin(), it + zipext.length());
|
||||
}
|
||||
if (file_path)
|
||||
{
|
||||
if ((it + zipext.length()) < path.end())
|
||||
{
|
||||
*file_path = std::string(it + zipext.length() + 1, path.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
*file_path = {};
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int get_stmode(const char *path)
|
||||
{
|
||||
sprintf(full_path, "%s/%s", getRootDir(), path);
|
||||
struct stat64 st;
|
||||
return (stat64(path, &st) < 0) ? 0 : st.st_mode;
|
||||
}
|
||||
|
||||
static bool isPathDirectory(const std::string& path)
|
||||
{
|
||||
std::string full_path{path};
|
||||
if (full_path[0] != '/')
|
||||
{
|
||||
full_path = std::string(getRootDir()) + "/" + path;
|
||||
}
|
||||
|
||||
std::string zip_path, file_path;
|
||||
if (FileIsZipped(full_path, &zip_path, &file_path))
|
||||
{
|
||||
mz_zip_archive z{};
|
||||
if (!mz_zip_reader_init_file(&z, zip_path.c_str(), 0))
|
||||
{
|
||||
printf("isPathDirectory(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_path.empty())
|
||||
{
|
||||
mz_zip_reader_end(&z);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Folder names always end with a slash in the zip
|
||||
// file central directory.
|
||||
file_path += "/";
|
||||
const int file_index = mz_zip_reader_locate_file(&z, file_path.c_str(), NULL, 0);
|
||||
if (file_index < 0)
|
||||
{
|
||||
printf("isPathDirectory(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n",
|
||||
zip_path.c_str(), file_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
||||
mz_zip_reader_end(&z);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mz_zip_reader_is_file_a_directory(&z, file_index))
|
||||
{
|
||||
mz_zip_reader_end(&z);
|
||||
return true;
|
||||
}
|
||||
mz_zip_reader_end(&z);
|
||||
}
|
||||
else
|
||||
{
|
||||
int stmode = get_stmode(path.c_str());
|
||||
if (!stmode)
|
||||
{
|
||||
printf("isPathDirectory(stat) path:%s, error:%s.\n", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stmode & S_IFDIR) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isPathRegularFile(const std::string& path)
|
||||
{
|
||||
std::string full_path{path};
|
||||
if (full_path[0] != '/')
|
||||
{
|
||||
full_path = std::string(getRootDir()) + "/" + path;
|
||||
}
|
||||
|
||||
std::string zip_path, file_path;
|
||||
if (FileIsZipped(full_path, &zip_path, &file_path))
|
||||
{
|
||||
mz_zip_archive z{};
|
||||
if (!mz_zip_reader_init_file(&z, zip_path.c_str(), 0))
|
||||
{
|
||||
printf("isPathDirectory(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_path.empty())
|
||||
{
|
||||
mz_zip_reader_end(&z);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int file_index = mz_zip_reader_locate_file(&z, file_path.c_str(), NULL, 0);
|
||||
if (file_index < 0)
|
||||
{
|
||||
printf("isPathDirectory(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n",
|
||||
zip_path.c_str(), file_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
||||
mz_zip_reader_end(&z);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mz_zip_reader_is_file_a_directory(&z, file_index) && mz_zip_reader_is_file_supported(&z, file_index))
|
||||
{
|
||||
mz_zip_reader_end(&z);
|
||||
return true;
|
||||
}
|
||||
mz_zip_reader_end(&z);
|
||||
}
|
||||
else
|
||||
{
|
||||
int stmode = get_stmode(path.c_str());
|
||||
if (!stmode)
|
||||
{
|
||||
printf("isPathDirectory(stat) path:%s, error:%s.\n", path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stmode & S_IFREG) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileClose(fileTYPE *file)
|
||||
{
|
||||
if (file->zip)
|
||||
{
|
||||
if (file->zip->iter)
|
||||
{
|
||||
mz_zip_reader_extract_iter_free(file->zip->iter);
|
||||
}
|
||||
mz_zip_reader_end(&file->zip->archive);
|
||||
|
||||
delete file->zip;
|
||||
file->zip = nullptr;
|
||||
}
|
||||
|
||||
if (file->filp)
|
||||
{
|
||||
//printf("closing %p\n", file->filp);
|
||||
@@ -68,47 +243,101 @@ int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute)
|
||||
file->mode = 0;
|
||||
file->type = 0;
|
||||
|
||||
char *p = strrchr(full_path, '/');
|
||||
strcpy(file->name, (mode == -1) ? full_path : p+1);
|
||||
|
||||
int fd = (mode == -1) ? shm_open("/vtrd", O_CREAT | O_RDWR | O_TRUNC, 0777) : open(full_path, mode, 0777);
|
||||
if (fd <= 0)
|
||||
std::string zip_path, file_path;
|
||||
if (FileIsZipped(full_path, &zip_path, &file_path))
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(open) File:%s, error: %s.\n", full_path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *fmode = mode & O_RDWR ? "w+" : "r";
|
||||
file->filp = fdopen(fd, fmode);
|
||||
if (!file->filp)
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(fdopen) File:%s, error: %s.\n", full_path, strerror(errno));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mode == -1)
|
||||
{
|
||||
file->type = 1;
|
||||
file->size = 0;
|
||||
file->offset = 0;
|
||||
file->mode = O_CREAT | O_RDWR | O_TRUNC;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat64 st;
|
||||
int ret = fstat64(fd, &st);
|
||||
if (ret < 0)
|
||||
if (mode & O_RDWR || mode & O_WRONLY)
|
||||
{
|
||||
if (!mute) printf("FileOpenEx(fstat) File:%s, error: %d.\n", full_path, ret);
|
||||
if(!mute) printf("FileOpenEx(mode) Zip:%s, writing to zipped files is not supported.\n",
|
||||
full_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->zip = new fileZipArchive{};
|
||||
if (!mz_zip_reader_init_file(&file->zip->archive, zip_path.c_str(), 0))
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->zip->index = mz_zip_reader_locate_file(&file->zip->archive, file_path.c_str(), NULL, 0);
|
||||
if (file->zip->index < 0)
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n",
|
||||
zip_path.c_str(), file_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
FileClose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->size = st.st_size;
|
||||
mz_zip_archive_file_stat s;
|
||||
if (!mz_zip_reader_file_stat(&file->zip->archive, file->zip->index, &s))
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(mz_zip_reader_file_stat) Zip:%s, file:%s, error:%s\n",
|
||||
zip_path.c_str(), file_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
FileClose(file);
|
||||
return 0;
|
||||
}
|
||||
file->size = s.m_uncomp_size;
|
||||
|
||||
file->zip->iter = mz_zip_reader_extract_iter_new(&file->zip->archive, file->zip->index, 0);
|
||||
if (!file->zip->iter)
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(mz_zip_reader_extract_iter_new) Zip:%s, file:%s, error:%s\n",
|
||||
zip_path.c_str(), file_path.c_str(),
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
FileClose(file);
|
||||
return 0;
|
||||
}
|
||||
file->zip->offset = 0;
|
||||
file->offset = 0;
|
||||
file->mode = mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *p = strrchr(full_path, '/');
|
||||
strcpy(file->name, (mode == -1) ? full_path : p+1);
|
||||
|
||||
int fd = (mode == -1) ? shm_open("/vtrd", O_CREAT | O_RDWR | O_TRUNC, 0777) : open(full_path, mode, 0777);
|
||||
if (fd <= 0)
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(open) File:%s, error: %s.\n", full_path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
const char *fmode = mode & O_RDWR ? "w+" : "r";
|
||||
file->filp = fdopen(fd, fmode);
|
||||
if (!file->filp)
|
||||
{
|
||||
if(!mute) printf("FileOpenEx(fdopen) File:%s, error: %s.\n", full_path, strerror(errno));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mode == -1)
|
||||
{
|
||||
file->type = 1;
|
||||
file->size = 0;
|
||||
file->offset = 0;
|
||||
file->mode = O_CREAT | O_RDWR | O_TRUNC;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat64 st;
|
||||
int ret = fstat64(fileno(file->filp), &st);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (!mute) printf("FileOpenEx(fstat) File:%s, error: %d.\n", full_path, ret);
|
||||
FileClose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->size = st.st_size;
|
||||
file->offset = 0;
|
||||
file->mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("opened %s, size %lu\n", full_path, file->size);
|
||||
return 1;
|
||||
@@ -116,11 +345,17 @@ int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute)
|
||||
|
||||
__off64_t FileGetSize(fileTYPE *file)
|
||||
{
|
||||
if (!file->filp) return 0;
|
||||
|
||||
struct stat64 st;
|
||||
int ret = fstat64(fileno(file->filp), &st);
|
||||
return (ret < 0) ? 0 : st.st_size;
|
||||
if (file->filp)
|
||||
{
|
||||
struct stat64 st;
|
||||
int ret = fstat64(fileno(file->filp), &st);
|
||||
return (ret < 0) ? 0 : st.st_size;
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
return file->size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileOpen(fileTYPE *file, const char *name, char mute)
|
||||
@@ -130,28 +365,88 @@ int FileOpen(fileTYPE *file, const char *name, char mute)
|
||||
|
||||
int FileNextSector(fileTYPE *file)
|
||||
{
|
||||
__off64_t newoff = lseek64(fileno(file->filp), file->offset + 512, SEEK_SET);
|
||||
if (newoff != file->offset + 512)
|
||||
if (file->filp)
|
||||
{
|
||||
//printf("Fail to seek to next sector. File: %s.\n", file->name);
|
||||
lseek64(fileno(file->filp), file->offset, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
__off64_t newoff = lseek64(fileno(file->filp), file->offset + 512, SEEK_SET);
|
||||
if (newoff != file->offset + 512)
|
||||
{
|
||||
//printf("Fail to seek to next sector. File: %s.\n", file->name);
|
||||
lseek64(fileno(file->filp), file->offset, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->offset = newoff;
|
||||
return 1;
|
||||
file->offset = newoff;
|
||||
return 1;
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
if (!FileSeek(file, file->offset + 512, SEEK_SET))
|
||||
{
|
||||
FileSeek(file, file->offset, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileSeek(fileTYPE *file, __off64_t offset, int origin)
|
||||
{
|
||||
__off64_t newoff = lseek64(fileno(file->filp), offset, origin);
|
||||
if(newoff<0)
|
||||
if (file->filp)
|
||||
{
|
||||
offset = lseek64(fileno(file->filp), offset, origin);
|
||||
if(offset<0)
|
||||
{
|
||||
printf("Fail to seek the file.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
if (origin == SEEK_CUR)
|
||||
{
|
||||
offset = file->zip->offset + offset;
|
||||
}
|
||||
else if (origin == SEEK_END)
|
||||
{
|
||||
offset = file->size - offset;
|
||||
}
|
||||
|
||||
if (offset < file->zip->offset)
|
||||
{
|
||||
mz_zip_reader_extract_iter_state *iter = mz_zip_reader_extract_iter_new(&file->zip->archive, file->zip->index, 0);
|
||||
if (!iter)
|
||||
{
|
||||
printf("FileSeek(mz_zip_reader_extract_iter_new) Failed to rewind iterator, error:%s\n",
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
mz_zip_reader_extract_iter_free(file->zip->iter);
|
||||
file->zip->iter = iter;
|
||||
file->zip->offset = 0;
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
while (file->zip->offset < offset)
|
||||
{
|
||||
const size_t want_len = std::min((__off64_t)sizeof(buf), offset - file->zip->offset);
|
||||
const size_t read_len = mz_zip_reader_extract_iter_read(file->zip->iter, buf, want_len);
|
||||
file->zip->offset += read_len;
|
||||
if (read_len < want_len)
|
||||
{
|
||||
printf("FileSeek(mz_zip_reader_extract_iter_read) Failed to advance iterator, error:%s\n",
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Fail to seek the file.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->offset = newoff;
|
||||
file->offset = offset;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -182,10 +477,30 @@ int FileReadEx(fileTYPE *file, void *pBuffer, int nSize)
|
||||
{
|
||||
for (int i = 0; i < nSize; i++)
|
||||
{
|
||||
int ret = fread(tmpbuff, 512, 1, file->filp);
|
||||
if (ret < 0)
|
||||
if (file->filp)
|
||||
{
|
||||
printf("FileRead error(%d).\n", ret);
|
||||
int ret = fread(tmpbuff, 512, 1, file->filp);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("FileRead error(%d).\n", ret);
|
||||
return 0;
|
||||
}
|
||||
i += ret;
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
size_t ret = mz_zip_reader_extract_iter_read(file->zip->iter, tmpbuff, 512);
|
||||
if (!ret)
|
||||
{
|
||||
printf("FileReadEx(mz_zip_reader_extract_iter_read) Failed to read, error:%s\n",
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
return 0;
|
||||
}
|
||||
file->zip->offset += ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FileRead error(unknown file type).\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -196,10 +511,34 @@ int FileReadEx(fileTYPE *file, void *pBuffer, int nSize)
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = fread(pBuffer, nSize, 512, file->filp);
|
||||
if (ret < 0)
|
||||
if (file->filp)
|
||||
{
|
||||
printf("FileRead error(%d).\n", ret);
|
||||
int ret = fread(pBuffer, nSize, 512, file->filp);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("FileRead error(%d).\n", ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
char *p = (char*)pBuffer;
|
||||
for (int i = 0; i < nSize; i++)
|
||||
{
|
||||
size_t ret = mz_zip_reader_extract_iter_read(file->zip->iter, p, 512);
|
||||
if (!ret)
|
||||
{
|
||||
printf("FileReadEx(mz_zip_reader_extract_iter_read) Failed to read, error:%s\n",
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
return 0;
|
||||
}
|
||||
p += ret;
|
||||
file->zip->offset += ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FileRead error(unknown file type).\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -216,10 +555,23 @@ int FileWrite(fileTYPE *file, void *pBuffer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = fwrite(pBuffer, 512, 1, file->filp);
|
||||
if (ret < 0)
|
||||
if (file->filp)
|
||||
{
|
||||
printf("FileWrite error(%d).\n", ret);
|
||||
int ret = fwrite(pBuffer, 512, 1, file->filp);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("FileWrite error(%d).\n", ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
printf("FileWrite error(not supported for zip).\n");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FileWrite error(unknown file type).\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -229,11 +581,32 @@ int FileWrite(fileTYPE *file, void *pBuffer)
|
||||
// Read with offset advancing
|
||||
int FileReadAdv(fileTYPE *file, void *pBuffer, int length)
|
||||
{
|
||||
ssize_t ret = fread(pBuffer, length, 1, file->filp);
|
||||
if (ret < 0)
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (file->filp)
|
||||
{
|
||||
printf("FileReadAdv error(%d).\n", ret);
|
||||
return 0;
|
||||
ret = fread(pBuffer, length, 1, file->filp);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("FileReadAdv error(%d).\n", ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
ret = mz_zip_reader_extract_iter_read(file->zip->iter, pBuffer, length);
|
||||
if (!ret)
|
||||
{
|
||||
printf("FileReadEx(mz_zip_reader_extract_iter_read) Failed to read, error:%s\n",
|
||||
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
||||
return 0;
|
||||
}
|
||||
file->zip->offset += ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FileReadAdv error(unknown file type).\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
file->offset += ret;
|
||||
@@ -248,10 +621,25 @@ int FileReadSec(fileTYPE *file, void *pBuffer)
|
||||
// Write with offset advancing
|
||||
int FileWriteAdv(fileTYPE *file, void *pBuffer, int length)
|
||||
{
|
||||
int ret = fwrite(pBuffer, length, 1, file->filp);
|
||||
if (ret < 0)
|
||||
int ret;
|
||||
|
||||
if (file->filp)
|
||||
{
|
||||
printf("FileWriteAdv error(%d).\n", ret);
|
||||
ret = fwrite(pBuffer, length, 1, file->filp);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("FileWriteAdv error(%d).\n", ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (file->zip)
|
||||
{
|
||||
printf("FileWriteAdv error(not supported for zip).\n");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FileWriteAdv error(unknown file type).\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -267,6 +655,12 @@ int FileWriteSec(fileTYPE *file, void *pBuffer)
|
||||
int FileSave(const char *name, void *pBuffer, int size)
|
||||
{
|
||||
sprintf(full_path, "%s/%s", getRootDir(), name);
|
||||
if (!FileCanWrite(name))
|
||||
{
|
||||
printf("FileSave(FileCanWrite) File:%s, not writable.\n", full_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (fd < 0)
|
||||
{
|
||||
@@ -341,6 +735,11 @@ int FileCanWrite(const char *name)
|
||||
{
|
||||
sprintf(full_path, "%s/%s", getRootDir(), name);
|
||||
|
||||
if (FileIsZipped(full_path, nullptr, nullptr))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct stat64 st;
|
||||
int ret = stat64(full_path, &st);
|
||||
if (ret < 0)
|
||||
@@ -353,6 +752,36 @@ int FileCanWrite(const char *name)
|
||||
return ((st.st_mode & S_IWUSR) != 0);
|
||||
}
|
||||
|
||||
void FileGenerateSavePath(const char *name, const char* extension, char* out_name, int length)
|
||||
{
|
||||
const char *d = strrchr(name, '.');
|
||||
if (d)
|
||||
{
|
||||
const int l = std::min(d - name, length);
|
||||
strncpy(out_name, name, l);
|
||||
out_name[l] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(out_name, name, length);
|
||||
}
|
||||
|
||||
char *z = strcasestr(out_name, ".zip");
|
||||
if (z)
|
||||
{
|
||||
// Remove '.' from '.zip' so file logic won't think
|
||||
// the file has been compressed.
|
||||
*z = '-';
|
||||
for (char *p = z; (p = strchr(p, '/')); )
|
||||
{
|
||||
*p = '-';
|
||||
}
|
||||
}
|
||||
|
||||
strncat(out_name, ".", length);
|
||||
strncat(out_name, extension, length);
|
||||
}
|
||||
|
||||
uint32_t getFileType(const char *name)
|
||||
{
|
||||
sprintf(full_path, "%s/%s", getRootDir(), name);
|
||||
@@ -565,24 +994,9 @@ struct DirentComp
|
||||
size_t iterations = 0;
|
||||
};
|
||||
|
||||
static int get_stmode(const char *path)
|
||||
{
|
||||
sprintf(full_path, "%s/%s", getRootDir(), path);
|
||||
struct stat64 st;
|
||||
return (stat64(full_path, &st) < 0) ? 0 : st.st_mode;
|
||||
}
|
||||
|
||||
void AdjustDirectory(char *path)
|
||||
{
|
||||
int stmode = get_stmode(path);
|
||||
if (!stmode)
|
||||
{
|
||||
printf("AdjustDirectory(stat) path:%s, error.\n", path);
|
||||
path[0] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stmode & S_IFDIR) return;
|
||||
if (isPathDirectory(path)) return;
|
||||
|
||||
char *p = strrchr(path, '/');
|
||||
if (p)
|
||||
@@ -595,6 +1009,20 @@ void AdjustDirectory(char *path)
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsInSameFolder(const char *folder, const char *path)
|
||||
{
|
||||
if (strcasestr(path, folder) == path)
|
||||
{
|
||||
const char *subpath = path + strlen(folder) + 1;
|
||||
if (*subpath != '\0')
|
||||
{
|
||||
const char *slash = strchr(subpath, '/');
|
||||
return !slash || *(slash + 1) == '\0';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ScanDirectory(char* path, int mode, const char *extension, int options, const char *prefix)
|
||||
{
|
||||
static char file_name[1024];
|
||||
@@ -607,6 +1035,12 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons
|
||||
ext += 3;
|
||||
}
|
||||
|
||||
const char* is_zipped = strcasestr(path, ".zip");
|
||||
if (is_zipped && strcasestr(is_zipped + 4, ".zip"))
|
||||
{
|
||||
printf("Nested zip-files are not supported: %s\n", path);
|
||||
return 0;
|
||||
}
|
||||
int extlen = strlen(extension);
|
||||
|
||||
//printf("scan dir\n");
|
||||
@@ -614,8 +1048,7 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons
|
||||
if (mode == SCANF_INIT)
|
||||
{
|
||||
file_name[0] = 0;
|
||||
int stmode = get_stmode(path);
|
||||
if (!(stmode & S_IFDIR))
|
||||
if (!isPathDirectory(path))
|
||||
{
|
||||
char *p = strrchr(path, '/');
|
||||
if (p)
|
||||
@@ -629,37 +1062,80 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons
|
||||
path[0] = 0;
|
||||
}
|
||||
|
||||
if (!(stmode & S_IFREG)) file_name[0] = 0;
|
||||
if (!isPathRegularFile(path)) file_name[0] = 0;
|
||||
}
|
||||
|
||||
if (!(get_stmode(path) & S_IFDIR))
|
||||
if (!isPathDirectory(path))
|
||||
{
|
||||
path[0] = 0;
|
||||
file_name[0] = 0;
|
||||
}
|
||||
|
||||
sprintf(full_path, "%s/%s", getRootDir(), path);
|
||||
printf("Start to scan dir: %s\n", full_path);
|
||||
printf("Start to scan %sdir: %s\n", is_zipped ? "zipped " : "", full_path);
|
||||
|
||||
std::string zip_path, file_path_in_zip;
|
||||
FileIsZipped(full_path, &zip_path, &file_path_in_zip);
|
||||
|
||||
iFirstEntry = 0;
|
||||
iSelectedEntry = 0;
|
||||
DirItem.clear();
|
||||
|
||||
DIR *d = opendir(full_path);
|
||||
if (!d)
|
||||
DIR *d = nullptr;
|
||||
mz_zip_archive *z = nullptr;
|
||||
if (is_zipped)
|
||||
{
|
||||
printf("Couldn't open dir: %s\n", full_path);
|
||||
return 0;
|
||||
mz_zip_archive _z = { 0 };
|
||||
if (!mz_zip_reader_init_file(&_z, zip_path.c_str(), 0))
|
||||
{
|
||||
printf("Couldn't open zip file %s: %s\n", full_path, mz_zip_get_error_string(mz_zip_get_last_error(z)));
|
||||
return 0;
|
||||
}
|
||||
z = new mz_zip_archive(_z);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = opendir(full_path);
|
||||
if (!d)
|
||||
{
|
||||
printf("Couldn't open dir: %s\n", full_path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct dirent *de;
|
||||
for (size_t i = 0; (de = readdir(d)); i++)
|
||||
struct dirent *de = nullptr;
|
||||
for (size_t i = 0; (d && (de = readdir(d)))
|
||||
|| (z && i < mz_zip_reader_get_num_files(z)); i++)
|
||||
{
|
||||
if (0 < i && i % YieldIterations == 0)
|
||||
{
|
||||
scheduler_yield();
|
||||
}
|
||||
|
||||
struct dirent _de = { 0 };
|
||||
if (z) {
|
||||
mz_zip_reader_get_filename(z, i, &_de.d_name[0], sizeof(_de.d_name));
|
||||
if (!IsInSameFolder(file_path_in_zip.c_str(), _de.d_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Remove leading folders.
|
||||
const char* subpath = _de.d_name + file_path_in_zip.length();
|
||||
if (*subpath == '/')
|
||||
{
|
||||
subpath++;
|
||||
}
|
||||
strcpy(_de.d_name, subpath);
|
||||
|
||||
_de.d_type = mz_zip_reader_is_file_a_directory(z, i) ? DT_DIR : DT_REG;
|
||||
if (_de.d_type == DT_DIR)
|
||||
{
|
||||
// Remove trailing slash.
|
||||
_de.d_name[strlen(_de.d_name) - 1] = '\0';
|
||||
}
|
||||
de = &_de;
|
||||
}
|
||||
|
||||
if (de->d_type == DT_DIR)
|
||||
{
|
||||
if (!strcmp(de->d_name, ".")) continue;
|
||||
@@ -688,6 +1164,12 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons
|
||||
int len = strlen(de->d_name);
|
||||
const char *ext = extension;
|
||||
int found = (has_trd && x2trd_ext_supp(de->d_name));
|
||||
if (!found && !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".zip"))
|
||||
{
|
||||
// Fake that zip-file is a directory.
|
||||
de->d_type = DT_DIR;
|
||||
found = 1;
|
||||
}
|
||||
if (!found && is_minimig() && !memcmp(extension, "HDF", 3))
|
||||
{
|
||||
found = !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".iso");
|
||||
@@ -732,7 +1214,22 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons
|
||||
}
|
||||
DirItem.push_back(*de);
|
||||
}
|
||||
closedir(d);
|
||||
if (z)
|
||||
{
|
||||
// Since zip files aren't actually folders the entry to
|
||||
// exit the zip file must be added manually.
|
||||
dirent up;
|
||||
up.d_type = DT_DIR;
|
||||
strcpy(up.d_name, "..");
|
||||
DirItem.push_back(up);
|
||||
|
||||
mz_zip_reader_end(z);
|
||||
delete z;
|
||||
}
|
||||
if (d)
|
||||
{
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
printf("Got %d dir entries\n", flist_nDirEntries());
|
||||
if (!flist_nDirEntries()) return 0;
|
||||
|
||||
18
file_io.h
18
file_io.h
@@ -7,15 +7,18 @@
|
||||
#include <fcntl.h>
|
||||
#include "spi.h"
|
||||
|
||||
struct fileZipArchive;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FILE *filp;
|
||||
int mode;
|
||||
int type;
|
||||
__off64_t size;
|
||||
__off64_t offset;
|
||||
char path[1024];
|
||||
char name[261];
|
||||
FILE *filp;
|
||||
int mode;
|
||||
int type;
|
||||
fileZipArchive *zip;
|
||||
__off64_t size;
|
||||
__off64_t offset;
|
||||
char path[1024];
|
||||
char name[261];
|
||||
} fileTYPE;
|
||||
|
||||
int flist_nDirEntries();
|
||||
@@ -65,6 +68,7 @@ int FileWriteAdv(fileTYPE *file, void *pBuffer, int length);
|
||||
int FileWriteSec(fileTYPE *file, void *pBuffer);
|
||||
|
||||
int FileCanWrite(const char *name);
|
||||
void FileGenerateSavePath(const char *name, const char* extension, char* out_name, int length);
|
||||
|
||||
int FileSave(const char *name, void *pBuffer, int size);
|
||||
int FileLoad(const char *name, void *pBuffer, int size); // supply pBuffer = 0 to get the file size without loading
|
||||
|
||||
@@ -1299,10 +1299,7 @@ int user_io_file_tx(const char* name, unsigned char index, char opensave, char m
|
||||
|
||||
if (opensave)
|
||||
{
|
||||
strcpy((char*)buf, name);
|
||||
char *p = strrchr((char*)buf, '.');
|
||||
if (!p) p = (char*)buf + strlen(name);
|
||||
strcpy(p, ".sav");
|
||||
FileGenerateSavePath(name, "sav", (char*)buf, sizeof(buf));
|
||||
user_io_file_mount((char*)buf, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user