mirror of
https://github.com/MiSTer-devel/Main_MiSTer.git
synced 2026-04-12 03:04:02 +00:00
Added a new argument to ScanDirectory, a "filter" parameter. If it exists, it will filter items regardless of where in the filename the filter appears (not just in the beginning, as with the prefix). So if you type "ADV" as the filter, both "Advance Wars" as well as "Ace Combat Advance" will pass through
1746 lines
38 KiB
C++
1746 lines
38 KiB
C++
#include "file_io.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <ctype.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mount.h>
|
|
#include <linux/magic.h>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <string>
|
|
#include "osd.h"
|
|
#include "fpga_io.h"
|
|
#include "menu.h"
|
|
#include "errno.h"
|
|
#include "DiskImage.h"
|
|
#include "user_io.h"
|
|
#include "cfg.h"
|
|
#include "input.h"
|
|
#include "miniz_zip.h"
|
|
#include "scheduler.h"
|
|
#include "video.h"
|
|
#include "support.h"
|
|
|
|
#define MIN(a,b) (((a)<(b)) ? (a) : (b))
|
|
|
|
typedef std::vector<direntext_t> DirentVector;
|
|
|
|
static const size_t YieldIterations = 128;
|
|
|
|
DirentVector DirItem;
|
|
static int iSelectedEntry = 0; // selected entry index
|
|
static int iFirstEntry = 0;
|
|
|
|
static char full_path[2100];
|
|
uint8_t loadbuf[LOADBUF_SZ];
|
|
|
|
fileTYPE::fileTYPE()
|
|
{
|
|
filp = 0;
|
|
mode = 0;
|
|
type = 0;
|
|
zip = 0;
|
|
size = 0;
|
|
offset = 0;
|
|
}
|
|
|
|
fileTYPE::~fileTYPE()
|
|
{
|
|
FileClose(this);
|
|
}
|
|
|
|
int fileTYPE::opened()
|
|
{
|
|
return filp || zip;
|
|
}
|
|
|
|
struct fileZipArchive
|
|
{
|
|
mz_zip_archive archive;
|
|
int index;
|
|
mz_zip_reader_extract_iter_state* iter;
|
|
__off64_t offset;
|
|
};
|
|
|
|
static int FileIsZipped(char* path, char** zip_path, char** file_path)
|
|
{
|
|
char* z = strcasestr(path, ".zip");
|
|
if (z)
|
|
{
|
|
z += 4;
|
|
if (!z[0]) z[1] = 0;
|
|
*z++ = 0;
|
|
|
|
if (zip_path) *zip_path = path;
|
|
if (file_path) *file_path = z;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char* make_fullpath(const char *path, int mode = 0)
|
|
{
|
|
const char *root = getRootDir();
|
|
if (strncasecmp(getRootDir(), path, strlen(root)))
|
|
{
|
|
sprintf(full_path, "%s/%s", (mode == -1) ? "" : root, path);
|
|
}
|
|
else
|
|
{
|
|
sprintf(full_path, "%s",path);
|
|
}
|
|
|
|
return full_path;
|
|
}
|
|
|
|
static int get_stmode(const char *path)
|
|
{
|
|
struct stat64 st;
|
|
return (stat64(path, &st) < 0) ? 0 : st.st_mode;
|
|
}
|
|
|
|
struct stat64* getPathStat(const char *path)
|
|
{
|
|
make_fullpath(path);
|
|
static struct stat64 st;
|
|
return (stat64(full_path, &st) >= 0) ? &st : NULL;
|
|
}
|
|
|
|
static int isPathDirectory(const char *path, int use_zip = 1)
|
|
{
|
|
make_fullpath(path);
|
|
|
|
char *zip_path, *file_path;
|
|
if (use_zip && FileIsZipped(full_path, &zip_path, &file_path))
|
|
{
|
|
mz_zip_archive z{};
|
|
if (!mz_zip_reader_init_file(&z, zip_path, 0))
|
|
{
|
|
printf("isPathDirectory(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path,
|
|
mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
|
return 0;
|
|
}
|
|
|
|
if (!*file_path)
|
|
{
|
|
mz_zip_reader_end(&z);
|
|
return 1;
|
|
}
|
|
|
|
// Folder names always end with a slash in the zip
|
|
// file central directory.
|
|
strcat(file_path, "/");
|
|
const int file_index = mz_zip_reader_locate_file(&z, file_path, NULL, 0);
|
|
if (file_index < 0)
|
|
{
|
|
printf("isPathDirectory(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n",
|
|
zip_path, file_path,
|
|
mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
|
mz_zip_reader_end(&z);
|
|
return 0;
|
|
}
|
|
|
|
if (mz_zip_reader_is_file_a_directory(&z, file_index))
|
|
{
|
|
mz_zip_reader_end(&z);
|
|
return 1;
|
|
}
|
|
mz_zip_reader_end(&z);
|
|
}
|
|
else
|
|
{
|
|
int stmode = get_stmode(full_path);
|
|
if (!stmode)
|
|
{
|
|
//printf("isPathDirectory(stat) path: %s, error: %s.\n", full_path, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
if (stmode & S_IFDIR) return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isPathRegularFile(const char *path, int use_zip = 1)
|
|
{
|
|
make_fullpath(path);
|
|
|
|
char *zip_path, *file_path;
|
|
if (use_zip && FileIsZipped(full_path, &zip_path, &file_path))
|
|
{
|
|
mz_zip_archive z{};
|
|
if (!mz_zip_reader_init_file(&z, zip_path, 0))
|
|
{
|
|
//printf("isPathRegularFile(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path,
|
|
// mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
|
return 0;
|
|
}
|
|
|
|
if (!*file_path)
|
|
{
|
|
mz_zip_reader_end(&z);
|
|
return 0;
|
|
}
|
|
|
|
const int file_index = mz_zip_reader_locate_file(&z, file_path, NULL, 0);
|
|
if (file_index < 0)
|
|
{
|
|
//printf("isPathRegularFile(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n",
|
|
// zip_path, file_path,
|
|
// mz_zip_get_error_string(mz_zip_get_last_error(&z)));
|
|
mz_zip_reader_end(&z);
|
|
return 0;
|
|
}
|
|
|
|
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 1;
|
|
}
|
|
mz_zip_reader_end(&z);
|
|
}
|
|
else
|
|
{
|
|
if (get_stmode(full_path) & S_IFREG) return true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (file->filp)
|
|
{
|
|
//printf("closing %p\n", file->filp);
|
|
fclose(file->filp);
|
|
if (file->type == 1)
|
|
{
|
|
if (file->name[0] == '/')
|
|
{
|
|
shm_unlink(file->name);
|
|
}
|
|
file->type = 0;
|
|
}
|
|
}
|
|
|
|
file->zip = nullptr;
|
|
file->filp = nullptr;
|
|
file->size = 0;
|
|
}
|
|
|
|
static int zip_search_by_crc(mz_zip_archive *zipArchive, uint32_t crc32)
|
|
{
|
|
for (unsigned int file_index = 0; file_index < zipArchive->m_total_files; file_index++)
|
|
{
|
|
mz_zip_archive_file_stat s;
|
|
if (mz_zip_reader_file_stat(zipArchive, file_index, &s))
|
|
{
|
|
if (s.m_crc32 == crc32)
|
|
{
|
|
return file_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int FileOpenZip(fileTYPE *file, const char *name, uint32_t crc32)
|
|
{
|
|
make_fullpath(name);
|
|
FileClose(file);
|
|
file->mode = 0;
|
|
file->type = 0;
|
|
|
|
char *p = strrchr(full_path, '/');
|
|
strcpy(file->name, (p) ? p + 1 : full_path);
|
|
|
|
char *zip_path, *file_path;
|
|
if (!FileIsZipped(full_path, &zip_path, &file_path))
|
|
{
|
|
printf("FileOpenZip: %s, is not a zip.\n", full_path);
|
|
return 0;
|
|
}
|
|
|
|
file->zip = new fileZipArchive{};
|
|
if (!mz_zip_reader_init_file(&file->zip->archive, zip_path, 0))
|
|
{
|
|
printf("FileOpenZip(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path,
|
|
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
|
return 0;
|
|
}
|
|
|
|
file->zip->index = -1;
|
|
if (crc32) file->zip->index = zip_search_by_crc(&file->zip->archive, crc32);
|
|
if (file->zip->index < 0) file->zip->index = mz_zip_reader_locate_file(&file->zip->archive, file_path, NULL, 0);
|
|
if (file->zip->index < 0)
|
|
{
|
|
printf("FileOpenZip(mz_zip_reader_locate_file) Zip:%s, file:%s, error: %s\n",
|
|
zip_path, file_path,
|
|
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
|
FileClose(file);
|
|
return 0;
|
|
}
|
|
|
|
mz_zip_archive_file_stat s;
|
|
if (!mz_zip_reader_file_stat(&file->zip->archive, file->zip->index, &s))
|
|
{
|
|
printf("FileOpenZip(mz_zip_reader_file_stat) Zip:%s, file:%s, error:%s\n",
|
|
zip_path, file_path,
|
|
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)
|
|
{
|
|
printf("FileOpenZip(mz_zip_reader_extract_iter_new) Zip:%s, file:%s, error:%s\n",
|
|
zip_path, file_path,
|
|
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 = O_RDONLY;
|
|
return 1;
|
|
}
|
|
|
|
int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute, int use_zip)
|
|
{
|
|
make_fullpath((char*)name, mode);
|
|
FileClose(file);
|
|
file->mode = 0;
|
|
file->type = 0;
|
|
|
|
char *p = strrchr(full_path, '/');
|
|
strcpy(file->name, (mode == -1) ? full_path : p + 1);
|
|
|
|
char *zip_path, *file_path;
|
|
if (use_zip && (mode != -1) && FileIsZipped(full_path, &zip_path, &file_path))
|
|
{
|
|
if (mode & O_RDWR || mode & O_WRONLY)
|
|
{
|
|
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, 0))
|
|
{
|
|
if(!mute) printf("FileOpenEx(mz_zip_reader_init_file) Zip:%s, error:%s\n", zip_path,
|
|
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, 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, file_path,
|
|
mz_zip_get_error_string(mz_zip_get_last_error(&file->zip->archive)));
|
|
FileClose(file);
|
|
return 0;
|
|
}
|
|
|
|
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, file_path,
|
|
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, file_path,
|
|
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
|
|
{
|
|
int fd = (mode == -1) ? shm_open("/vdsk", 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;
|
|
if (st.st_rdev && !st.st_size) //for special files we need an ioctl call to get the correct size
|
|
{
|
|
unsigned long long blksize;
|
|
int ret = ioctl(fd, BLKGETSIZE64, &blksize);
|
|
if (ret < 0)
|
|
{
|
|
if (!mute) printf("FileOpenEx(ioctl) File:%s, error: %d.\n", full_path, ret);
|
|
FileClose(file);
|
|
return 0;
|
|
}
|
|
file->size = blksize;
|
|
}
|
|
|
|
file->offset = 0;
|
|
file->mode = mode;
|
|
}
|
|
}
|
|
|
|
//printf("opened %s, size %llu\n", full_path, file->size);
|
|
return 1;
|
|
}
|
|
|
|
__off64_t FileGetSize(fileTYPE *file)
|
|
{
|
|
if (file->filp)
|
|
{
|
|
struct stat64 st;
|
|
if (fstat64(fileno(file->filp), &st) < 0) return 0;
|
|
|
|
if (st.st_rdev && !st.st_size) //for special files we need an ioctl call to get the correct size
|
|
{
|
|
unsigned long long blksize;
|
|
int ret = ioctl(fileno(file->filp), BLKGETSIZE64, &blksize);
|
|
if (ret < 0) return 0;
|
|
return blksize;
|
|
}
|
|
|
|
return st.st_size;
|
|
}
|
|
else if (file->zip)
|
|
{
|
|
return file->size;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int FileOpen(fileTYPE *file, const char *name, char mute)
|
|
{
|
|
return FileOpenEx(file, name, O_RDONLY, mute);
|
|
}
|
|
|
|
int FileSeek(fileTYPE *file, __off64_t offset, int origin)
|
|
{
|
|
if (file->filp)
|
|
{
|
|
__off64_t res = fseeko64(file->filp, offset, origin);
|
|
if (res < 0)
|
|
{
|
|
printf("Fail to seek the file: offset=%lld, %s.\n", offset, file->name);
|
|
return 0;
|
|
}
|
|
offset = ftello64(file->filp);
|
|
}
|
|
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;
|
|
}
|
|
|
|
static char buf[4*1024];
|
|
while (file->zip->offset < offset)
|
|
{
|
|
const size_t want_len = 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
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
file->offset = offset;
|
|
return 1;
|
|
}
|
|
|
|
int FileSeekLBA(fileTYPE *file, uint32_t offset)
|
|
{
|
|
__off64_t off64 = offset;
|
|
off64 <<= 9;
|
|
return FileSeek(file, off64, SEEK_SET);
|
|
}
|
|
|
|
// Read with offset advancing
|
|
int FileReadAdv(fileTYPE *file, void *pBuffer, int length)
|
|
{
|
|
ssize_t ret = 0;
|
|
|
|
if (file->filp)
|
|
{
|
|
ret = fread(pBuffer, 1, length, 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 0;
|
|
}
|
|
|
|
file->offset += ret;
|
|
return ret;
|
|
}
|
|
|
|
int FileReadSec(fileTYPE *file, void *pBuffer)
|
|
{
|
|
return FileReadAdv(file, pBuffer, 512);
|
|
}
|
|
|
|
// Write with offset advancing
|
|
int FileWriteAdv(fileTYPE *file, void *pBuffer, int length)
|
|
{
|
|
int ret;
|
|
|
|
if (file->filp)
|
|
{
|
|
ret = fwrite(pBuffer, 1, length, file->filp);
|
|
fflush(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;
|
|
}
|
|
|
|
file->offset += ret;
|
|
return ret;
|
|
}
|
|
|
|
int FileWriteSec(fileTYPE *file, void *pBuffer)
|
|
{
|
|
return FileWriteAdv(file, pBuffer, 512);
|
|
}
|
|
|
|
int FileSave(const char *name, void *pBuffer, int size)
|
|
{
|
|
if(name[0] != '/') sprintf(full_path, "%s/%s", getRootDir(), name);
|
|
else strcpy(full_path, name);
|
|
|
|
int fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
if (fd < 0)
|
|
{
|
|
printf("FileSave(open) File:%s, error: %d.\n", full_path, fd);
|
|
return 0;
|
|
}
|
|
|
|
int ret = write(fd, pBuffer, size);
|
|
close(fd);
|
|
|
|
if (ret < 0)
|
|
{
|
|
printf("FileSave(write) File:%s, error: %d.\n", full_path, ret);
|
|
return 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int FileDelete(const char *name)
|
|
{
|
|
if (name[0] != '/') sprintf(full_path, "%s/%s", getRootDir(), name);
|
|
else strcpy(full_path, name);
|
|
|
|
printf("delete %s\n", full_path);
|
|
return !unlink(full_path);
|
|
}
|
|
|
|
int DirDelete(const char *name)
|
|
{
|
|
if (name[0] != '/') sprintf(full_path, "%s/%s", getRootDir(), name);
|
|
else strcpy(full_path, name);
|
|
|
|
printf("rmdir %s\n", full_path);
|
|
return !rmdir(full_path);
|
|
}
|
|
|
|
int FileLoad(const char *name, void *pBuffer, int size)
|
|
{
|
|
fileTYPE f;
|
|
if (!FileOpen(&f, name)) return 0;
|
|
|
|
int ret = f.size;
|
|
if (pBuffer) ret = FileReadAdv(&f, pBuffer, size ? size : f.size);
|
|
|
|
FileClose(&f);
|
|
return ret;
|
|
}
|
|
|
|
int FileLoadConfig(const char *name, void *pBuffer, int size)
|
|
{
|
|
char path[256] = { CONFIG_DIR"/" };
|
|
strcat(path, name);
|
|
return FileLoad(path, pBuffer, size);
|
|
}
|
|
|
|
int FileSaveConfig(const char *name, void *pBuffer, int size)
|
|
{
|
|
char path[256] = { CONFIG_DIR };
|
|
const char *p;
|
|
while ((p = strchr(name, '/')))
|
|
{
|
|
strcat(path, "/");
|
|
strncat(path, name, p - name);
|
|
name = ++p;
|
|
FileCreatePath(path);
|
|
}
|
|
|
|
strcat(path, "/");
|
|
strcat(path, name);
|
|
return FileSave(path, pBuffer, size);
|
|
}
|
|
|
|
int FileDeleteConfig(const char *name)
|
|
{
|
|
char path[256] = { CONFIG_DIR"/" };
|
|
strcat(path, name);
|
|
return FileDelete(path);
|
|
}
|
|
|
|
int FileExists(const char *name, int use_zip)
|
|
{
|
|
return isPathRegularFile(name, use_zip);
|
|
}
|
|
|
|
int PathIsDir(const char *name, int use_zip)
|
|
{
|
|
return isPathDirectory(name, use_zip);
|
|
}
|
|
|
|
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)
|
|
{
|
|
printf("FileCanWrite(stat) File:%s, error: %d.\n", full_path, ret);
|
|
return 0;
|
|
}
|
|
|
|
//printf("FileCanWrite: mode=%04o.\n", st.st_mode);
|
|
return ((st.st_mode & S_IWUSR) != 0);
|
|
}
|
|
|
|
static void create_path(const char *base_dir, const char* sub_dir)
|
|
{
|
|
make_fullpath(base_dir);
|
|
mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
strcat(full_path, "/");
|
|
strcat(full_path, sub_dir);
|
|
mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
}
|
|
|
|
int FileCreatePath(const char *dir)
|
|
{
|
|
int res = 1;
|
|
if (!isPathDirectory(dir)) {
|
|
make_fullpath(dir);
|
|
res = !mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void FileGenerateScreenshotName(const char *name, char *out_name, int buflen)
|
|
{
|
|
create_path(SCREENSHOT_DIR, CoreName);
|
|
|
|
time_t t = time(NULL);
|
|
struct tm tm = *localtime(&t);
|
|
char datecode[32] = {};
|
|
if (tm.tm_year >= 119) // 2019 or up considered valid time
|
|
{
|
|
strftime(datecode, 31, "%Y%m%d_%H%M%S", &tm);
|
|
snprintf(out_name, buflen, "%s/%s/%s-%s.png", SCREENSHOT_DIR, CoreName, datecode, name[0] ? name : SCREENSHOT_DEFAULT);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 1; i < 10000; i++)
|
|
{
|
|
snprintf(out_name, buflen, "%s/%s/NODATE-%s_%04d.png", SCREENSHOT_DIR, CoreName, name[0] ? name : SCREENSHOT_DEFAULT, i);
|
|
if (!getFileType(out_name)) return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileGenerateSavePath(const char *name, char* out_name)
|
|
{
|
|
create_path(SAVE_DIR, CoreName);
|
|
|
|
sprintf(out_name, "%s/%s/", SAVE_DIR, CoreName);
|
|
char *fname = out_name + strlen(out_name);
|
|
|
|
const char *p = strrchr(name, '/');
|
|
if (p)
|
|
{
|
|
strcat(fname, p+1);
|
|
}
|
|
else
|
|
{
|
|
strcat(fname, name);
|
|
}
|
|
|
|
char *e = strrchr(fname, '.');
|
|
if (e)
|
|
{
|
|
strcpy(e,".sav");
|
|
}
|
|
else
|
|
{
|
|
strcat(fname, ".sav");
|
|
}
|
|
|
|
printf("SavePath=%s\n", out_name);
|
|
}
|
|
|
|
void FileGenerateSavestatePath(const char *name, char* out_name)
|
|
{
|
|
create_path(SAVESTATE_DIR, CoreName);
|
|
|
|
sprintf(out_name, "%s/%s/", SAVESTATE_DIR, CoreName);
|
|
char *fname = out_name + strlen(out_name);
|
|
|
|
const char *p = strrchr(name, '/');
|
|
if (p)
|
|
{
|
|
strcat(fname, p + 1);
|
|
}
|
|
else
|
|
{
|
|
strcat(fname, name);
|
|
}
|
|
|
|
char *e = strrchr(fname, '.');
|
|
if (e)
|
|
{
|
|
strcpy(e, ".ss");
|
|
}
|
|
else
|
|
{
|
|
strcat(fname, ".ss");
|
|
}
|
|
|
|
printf("SavestatePath=%s\n", out_name);
|
|
}
|
|
|
|
uint32_t getFileType(const char *name)
|
|
{
|
|
sprintf(full_path, "%s/%s", getRootDir(), name);
|
|
|
|
struct stat64 st;
|
|
if (stat64(full_path, &st)) return 0;
|
|
|
|
return st.st_mode;
|
|
}
|
|
|
|
int findPrefixDir(char *dir, size_t dir_len)
|
|
{
|
|
// Searches for the core's folder in the following order:
|
|
// /media/fat
|
|
// /media/usb<0..5>
|
|
// /media/usb<0..5>/games
|
|
// /media/fat/cifs
|
|
// /media/fat/cifs/games
|
|
// /media/fat/games/
|
|
// if the core folder is not found anywhere,
|
|
// it will be created in /media/fat/games/<dir>
|
|
static char temp_dir[1024];
|
|
|
|
for (int x = 0; x < 6; x++) {
|
|
snprintf(temp_dir, 1024, "%s%d/%s", "../usb", x, dir);
|
|
if (isPathDirectory(temp_dir)) {
|
|
printf("Found USB dir: %s\n", temp_dir);
|
|
strncpy(dir, temp_dir, dir_len);
|
|
return 1;
|
|
}
|
|
|
|
snprintf(temp_dir, 1024, "%s%d/%s/%s", "../usb", x, GAMES_DIR, dir);
|
|
if (isPathDirectory(temp_dir)) {
|
|
printf("Found USB dir: %s\n", temp_dir);
|
|
strncpy(dir, temp_dir, dir_len);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
snprintf(temp_dir, 1024, "%s/%s", CIFS_DIR, dir);
|
|
if (isPathDirectory(temp_dir)) {
|
|
printf("Found CIFS dir: %s\n", temp_dir);
|
|
strncpy(dir, temp_dir, dir_len);
|
|
return 1;
|
|
}
|
|
|
|
snprintf(temp_dir, 1024, "%s/%s/%s", CIFS_DIR, GAMES_DIR, dir);
|
|
if (isPathDirectory(temp_dir)) {
|
|
printf("Found CIFS dir: %s\n", temp_dir);
|
|
strncpy(dir, temp_dir, dir_len);
|
|
return 1;
|
|
}
|
|
|
|
if (isPathDirectory(dir)) {
|
|
printf("Found existing: %s\n", dir);
|
|
return 1;
|
|
}
|
|
|
|
snprintf(temp_dir, 1024, "%s/%s", GAMES_DIR, dir);
|
|
if (isPathDirectory(temp_dir)) {
|
|
printf("Found dir: %s\n", temp_dir);
|
|
strncpy(dir, temp_dir, dir_len);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void prefixGameDir(char *dir, size_t dir_len)
|
|
{
|
|
if (!findPrefixDir(dir, dir_len))
|
|
{
|
|
static char temp_dir[1024];
|
|
|
|
//FileCreatePath(GAMES_DIR);
|
|
snprintf(temp_dir, 1024, "%s/%s", GAMES_DIR, dir);
|
|
strncpy(dir, temp_dir, dir_len);
|
|
printf("Prefixed dir to %s\n", temp_dir);
|
|
}
|
|
}
|
|
|
|
static int device = 0;
|
|
static int usbnum = 0;
|
|
const char *getStorageDir(int dev)
|
|
{
|
|
static char path[32];
|
|
if (!dev) return "/media/fat";
|
|
sprintf(path, "/media/usb%d", usbnum);
|
|
return path;
|
|
}
|
|
|
|
const char *getRootDir()
|
|
{
|
|
return getStorageDir(device);
|
|
}
|
|
|
|
const char *getFullPath(const char *name)
|
|
{
|
|
sprintf(full_path, "%s/%s", getRootDir(), name);
|
|
return full_path;
|
|
}
|
|
|
|
void setStorage(int dev)
|
|
{
|
|
device = 0;
|
|
FileSave(CONFIG_DIR"/device.bin", &dev, sizeof(int));
|
|
fpga_load_rbf("menu.rbf");
|
|
}
|
|
|
|
static int orig_device = 0;
|
|
int getStorage(int from_setting)
|
|
{
|
|
return from_setting ? orig_device : device;
|
|
}
|
|
|
|
int isPathMounted(int n)
|
|
{
|
|
char path[32];
|
|
sprintf(path, "/media/usb%d", n);
|
|
|
|
struct stat file_stat;
|
|
struct stat parent_stat;
|
|
|
|
if (-1 == stat(path, &file_stat))
|
|
{
|
|
printf("failed to stat %s\n", path);
|
|
return 0;
|
|
}
|
|
|
|
if (!(file_stat.st_mode & S_IFDIR))
|
|
{
|
|
printf("%s is not a directory.\n", path);
|
|
return 0;
|
|
}
|
|
|
|
if (-1 == stat("/media", &parent_stat))
|
|
{
|
|
printf("failed to stat /media\n");
|
|
return 0;
|
|
}
|
|
|
|
if (file_stat.st_dev != parent_stat.st_dev ||
|
|
(file_stat.st_dev == parent_stat.st_dev &&
|
|
file_stat.st_ino == parent_stat.st_ino))
|
|
{
|
|
printf("%s IS a mountpoint.\n", path);
|
|
struct statfs fs_stat;
|
|
if (!statfs(path, &fs_stat))
|
|
{
|
|
printf("%s is FS: 0x%08X\n", path, fs_stat.f_type);
|
|
if (fs_stat.f_type != EXT4_SUPER_MAGIC)
|
|
{
|
|
printf("%s is not EXT2/3/4.\n", path);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("%s is NOT a VFAT mountpoint.\n", path);
|
|
return 0;
|
|
}
|
|
|
|
int isUSBMounted()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (isPathMounted(i))
|
|
{
|
|
usbnum = i;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void FindStorage(void)
|
|
{
|
|
char str[128];
|
|
printf("Looking for root device...\n");
|
|
device = 0;
|
|
FileLoad(CONFIG_DIR"/device.bin", &device, sizeof(int));
|
|
orig_device = device;
|
|
|
|
if(device && !isUSBMounted())
|
|
{
|
|
uint8_t core_type = (fpga_core_id() & 0xFF);
|
|
if (core_type == CORE_TYPE_8BIT)
|
|
{
|
|
user_io_read_confstr();
|
|
user_io_read_core_name();
|
|
}
|
|
|
|
int saveddev = device;
|
|
device = 0;
|
|
cfg_parse();
|
|
device = saveddev;
|
|
video_mode_load();
|
|
user_io_send_buttons(1);
|
|
|
|
printf("Waiting for USB...\n");
|
|
int btn = 0;
|
|
int done = 0;
|
|
|
|
OsdWrite(16, "", 1);
|
|
OsdWrite(17, " www.MiSTerFPGA.org ", 1);
|
|
OsdWrite(18, "", 1);
|
|
|
|
for (int i = 30; i >= 0; i--)
|
|
{
|
|
sprintf(str, "\n Waiting for USB...\n\n %d \n\n\n OSD/USER or ESC to cancel", i);
|
|
InfoMessage(str);
|
|
if (isUSBMounted())
|
|
{
|
|
done = 1;
|
|
break;
|
|
}
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
btn = fpga_get_buttons();
|
|
if (!btn) btn = input_poll(1);
|
|
if (btn)
|
|
{
|
|
printf("Button has been pressed %d\n", btn);
|
|
InfoMessage("\n\n Canceled!\n");
|
|
usleep(500000);
|
|
setStorage(0);
|
|
break;
|
|
}
|
|
usleep(100000);
|
|
}
|
|
if (done) break;
|
|
}
|
|
|
|
if (!done)
|
|
{
|
|
InfoMessage("\n\n No USB storage found\n Falling back to SD card\n");
|
|
usleep(2000000);
|
|
setStorage(0);
|
|
}
|
|
}
|
|
|
|
if (device)
|
|
{
|
|
printf("Using USB as a root device\n");
|
|
}
|
|
else
|
|
{
|
|
printf("Using SD card as a root device\n");
|
|
}
|
|
|
|
sprintf(full_path, "%s/" CONFIG_DIR, getRootDir());
|
|
DIR* dir = opendir(full_path);
|
|
if (dir) closedir(dir);
|
|
else if (ENOENT == errno) mkdir(full_path, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
}
|
|
|
|
struct DirentComp
|
|
{
|
|
bool operator()(const direntext_t& de1, const direntext_t& de2)
|
|
{
|
|
|
|
#ifdef USE_SCHEDULER
|
|
if (++iterations % YieldIterations == 0)
|
|
{
|
|
scheduler_yield();
|
|
}
|
|
#endif
|
|
|
|
if ((de1.de.d_type == DT_DIR) && !strcmp(de1.altname, "..")) return true;
|
|
if ((de2.de.d_type == DT_DIR) && !strcmp(de2.altname, "..")) return false;
|
|
|
|
if ((de1.de.d_type == DT_DIR) && (de2.de.d_type != DT_DIR)) return true;
|
|
if ((de1.de.d_type != DT_DIR) && (de2.de.d_type == DT_DIR)) return false;
|
|
|
|
int len1 = strlen(de1.altname);
|
|
int len2 = strlen(de2.altname);
|
|
if ((len1 > 4) && (de1.altname[len1 - 4] == '.')) len1 -= 4;
|
|
if ((len2 > 4) && (de2.altname[len2 - 4] == '.')) len2 -= 4;
|
|
|
|
int len = (len1 < len2) ? len1 : len2;
|
|
int ret = strncasecmp(de1.altname, de2.altname, len);
|
|
if (!ret)
|
|
{
|
|
ret = strcasecmp(de1.datecode, de2.datecode);
|
|
if (!ret)
|
|
{
|
|
return len1 < len2;
|
|
}
|
|
}
|
|
|
|
return ret < 0;
|
|
}
|
|
|
|
size_t iterations = 0;
|
|
};
|
|
|
|
void AdjustDirectory(char *path)
|
|
{
|
|
if (!FileExists(path)) return;
|
|
|
|
char *p = strrchr(path, '/');
|
|
if (p)
|
|
{
|
|
*p = 0;
|
|
}
|
|
else
|
|
{
|
|
path[0] = 0;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int names_loaded = 0;
|
|
static void get_display_name(direntext_t *dext, const char *ext, int options)
|
|
{
|
|
static char *names = 0;
|
|
memcpy(dext->altname, dext->de.d_name, sizeof(dext->altname));
|
|
if (dext->de.d_type == DT_DIR) return;
|
|
|
|
int len = strlen(dext->altname);
|
|
int rbf = (len > 4 && !strcasecmp(dext->altname + len - 4, ".rbf"));
|
|
if (rbf)
|
|
{
|
|
dext->altname[len - 4] = 0;
|
|
char *p = strstr(dext->altname, "_20");
|
|
if (p) if (strlen(p + 3) < 6) p = 0;
|
|
if (p)
|
|
{
|
|
*p = 0;
|
|
strncpy(dext->datecode, p + 3, 15);
|
|
dext->datecode[15] = 0;
|
|
}
|
|
else
|
|
{
|
|
strcpy(dext->datecode, "------");
|
|
}
|
|
|
|
if (!names_loaded)
|
|
{
|
|
if (names)
|
|
{
|
|
free(names);
|
|
names = 0;
|
|
}
|
|
|
|
int size = FileLoad("names.txt", 0, 0);
|
|
if (size)
|
|
{
|
|
names = (char*)malloc(size + 1);
|
|
if (names)
|
|
{
|
|
names[0] = 0;
|
|
FileLoad("names.txt", names, 0);
|
|
names[size] = 0;
|
|
}
|
|
}
|
|
names_loaded = 1;
|
|
}
|
|
|
|
if (names)
|
|
{
|
|
strcat(dext->altname, ":");
|
|
len = strlen(dext->altname);
|
|
char *transl = strstr(names, dext->altname);
|
|
if (transl)
|
|
{
|
|
int copy = 0;
|
|
transl += len;
|
|
len = 0;
|
|
while (*transl && len < (int)sizeof(dext->altname) - 1)
|
|
{
|
|
if (!copy && *transl <= 32)
|
|
{
|
|
transl++;
|
|
continue;
|
|
}
|
|
|
|
if (copy && *transl < 32) break;
|
|
|
|
copy = 1;
|
|
dext->altname[len++] = *transl++;
|
|
}
|
|
len++;
|
|
}
|
|
|
|
dext->altname[len - 1] = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//do not remove ext if core supplies more than 1 extension and it's not list of cores
|
|
if (!(options & SCANO_CORES) && strlen(ext) > 3) return;
|
|
if (strchr(ext, '*') || strchr(ext, '?')) return;
|
|
|
|
/* find the extension on the end of the name*/
|
|
char *fext = strrchr(dext->altname, '.');
|
|
if (fext) *fext = 0;
|
|
}
|
|
|
|
int ScanDirectory(char* path, int mode, const char *extension, int options, const char *prefix, const char *filter)
|
|
{
|
|
static char file_name[1024];
|
|
static char full_path[1024];
|
|
|
|
int has_trd = 0;
|
|
const char *ext = extension;
|
|
while (*ext)
|
|
{
|
|
if (!strncasecmp(ext, "TRD", 3)) has_trd = 1;
|
|
ext += 3;
|
|
}
|
|
|
|
int extlen = strlen(extension);
|
|
int filterlen = filter ? strlen(filter) : 0;
|
|
//printf("scan dir\n");
|
|
|
|
if (mode == SCANF_INIT)
|
|
{
|
|
iFirstEntry = 0;
|
|
iSelectedEntry = 0;
|
|
DirItem.clear();
|
|
|
|
file_name[0] = 0;
|
|
if ((options & SCANO_NOENTER) || isPathRegularFile(path))
|
|
{
|
|
char *p = strrchr(path, '/');
|
|
if (p)
|
|
{
|
|
strcpy(file_name, p + 1);
|
|
*p = 0;
|
|
}
|
|
else
|
|
{
|
|
strcpy(file_name, path);
|
|
path[0] = 0;
|
|
}
|
|
}
|
|
|
|
if (!isPathDirectory(path)) return 0;
|
|
|
|
if (options & SCANO_NEOGEO) neogeo_scan_xml(path);
|
|
|
|
sprintf(full_path, "%s/%s", getRootDir(), path);
|
|
int path_len = strlen(full_path);
|
|
|
|
const char* is_zipped = strcasestr(full_path, ".zip");
|
|
if (is_zipped && strcasestr(is_zipped + 4, ".zip"))
|
|
{
|
|
printf("Nested zip-files are not supported: %s\n", full_path);
|
|
return 0;
|
|
}
|
|
|
|
printf("Start to scan %sdir: %s\n", is_zipped ? "zipped " : "", full_path);
|
|
printf("Position on item: %s\n", file_name);
|
|
|
|
char *zip_path, *file_path_in_zip = (char*)"";
|
|
FileIsZipped(full_path, &zip_path, &file_path_in_zip);
|
|
|
|
DIR *d = nullptr;
|
|
mz_zip_archive *z = nullptr;
|
|
if (is_zipped)
|
|
{
|
|
mz_zip_archive _z = {};
|
|
if (!mz_zip_reader_init_file(&_z, zip_path, 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 dirent64 *de = nullptr;
|
|
for (size_t i = 0; (d && (de = readdir64(d)))
|
|
|| (z && i < mz_zip_reader_get_num_files(z)); i++)
|
|
{
|
|
#ifdef USE_SCHEDULER
|
|
if (0 < i && i % YieldIterations == 0)
|
|
{
|
|
scheduler_yield();
|
|
}
|
|
#endif
|
|
struct dirent64 _de = {};
|
|
if (z) {
|
|
mz_zip_reader_get_filename(z, i, &_de.d_name[0], sizeof(_de.d_name));
|
|
if (!IsInSameFolder(file_path_in_zip, _de.d_name))
|
|
{
|
|
continue;
|
|
}
|
|
// Remove leading folders.
|
|
const char* subpath = _de.d_name + strlen(file_path_in_zip);
|
|
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;
|
|
}
|
|
else
|
|
// Handle (possible) symbolic link type in the directory entry
|
|
if (de->d_type == DT_LNK || de->d_type == DT_REG)
|
|
{
|
|
sprintf(full_path+path_len, "/%s", de->d_name);
|
|
|
|
struct stat entrystat;
|
|
|
|
if (!stat(full_path, &entrystat))
|
|
{
|
|
if (S_ISREG(entrystat.st_mode))
|
|
{
|
|
de->d_type = DT_REG;
|
|
}
|
|
else if (S_ISDIR(entrystat.st_mode))
|
|
{
|
|
de->d_type = DT_DIR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (filter) {
|
|
bool passes_filter = false;
|
|
|
|
for(const char *str = de->d_name; *str; str++) {
|
|
if (strncasecmp(str, filter, filterlen) == 0) {
|
|
passes_filter = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!passes_filter) continue;
|
|
}
|
|
|
|
|
|
if (options & SCANO_NEOGEO)
|
|
{
|
|
if (de->d_type == DT_REG && !strcasecmp(de->d_name + strlen(de->d_name) - 4, ".zip"))
|
|
{
|
|
de->d_type = DT_DIR;
|
|
}
|
|
|
|
if (strcasecmp(de->d_name + strlen(de->d_name) - 4, ".neo"))
|
|
{
|
|
if (de->d_type != DT_DIR) continue;
|
|
}
|
|
|
|
if (!strcmp(de->d_name, ".."))
|
|
{
|
|
if (!strlen(path)) continue;
|
|
}
|
|
else
|
|
{
|
|
// skip hidden folders
|
|
if (!strncasecmp(de->d_name, ".", 1)) continue;
|
|
}
|
|
|
|
direntext_t dext;
|
|
memset(&dext, 0, sizeof(dext));
|
|
memcpy(&dext.de, de, sizeof(dext.de));
|
|
memcpy(dext.altname, de->d_name, sizeof(dext.altname));
|
|
if (!strcasecmp(dext.altname + strlen(dext.altname) - 4, ".zip")) dext.altname[strlen(dext.altname) - 4] = 0;
|
|
|
|
full_path[path_len] = 0;
|
|
char *altname = neogeo_get_altname(full_path, dext.de.d_name, dext.altname);
|
|
if (altname)
|
|
{
|
|
if (altname == (char*)-1) continue;
|
|
|
|
dext.de.d_type = DT_REG;
|
|
memcpy(dext.altname, altname, sizeof(dext.altname));
|
|
}
|
|
|
|
DirItem.push_back(dext);
|
|
}
|
|
else
|
|
{
|
|
if (de->d_type == DT_DIR)
|
|
{
|
|
// skip System Volume Information folder
|
|
if (!strcmp(de->d_name, "System Volume Information")) continue;
|
|
if (!strcmp(de->d_name, ".."))
|
|
{
|
|
if (!strlen(path)) continue;
|
|
}
|
|
else
|
|
{
|
|
// skip hidden folder
|
|
if (!strncasecmp(de->d_name, ".", 1)) continue;
|
|
}
|
|
|
|
if (!(options & SCANO_DIR))
|
|
{
|
|
if (de->d_name[0] != '_' && strcmp(de->d_name, "..")) continue;
|
|
if (!(options & SCANO_CORES)) continue;
|
|
}
|
|
}
|
|
else if (de->d_type == DT_REG)
|
|
{
|
|
// skip hidden files
|
|
if (!strncasecmp(de->d_name, ".", 1)) continue;
|
|
//skip non-selectable files
|
|
if (!strcasecmp(de->d_name, "menu.rbf")) continue;
|
|
if (!strncasecmp(de->d_name, "menu_20", 7)) continue;
|
|
if (!strcasecmp(de->d_name, "boot.rom")) continue;
|
|
|
|
//check the prefix if given
|
|
if (prefix && strncasecmp(prefix, de->d_name, strlen(prefix))) continue;
|
|
|
|
if (extlen > 0)
|
|
{
|
|
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") && (options & SCANO_DIR))
|
|
{
|
|
// 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");
|
|
}
|
|
|
|
char *fext = strrchr(de->d_name, '.');
|
|
if (fext) fext++;
|
|
while (!found && *ext && fext)
|
|
{
|
|
char e[4];
|
|
memcpy(e, ext, 3);
|
|
if (e[2] == ' ')
|
|
{
|
|
e[2] = 0;
|
|
if (e[1] == ' ') e[1] = 0;
|
|
}
|
|
|
|
e[3] = 0;
|
|
found = 1;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (e[i] == '*') break;
|
|
if (e[i] == '?' && fext[i]) continue;
|
|
|
|
if (tolower(e[i]) != tolower(fext[i])) found = 0;
|
|
|
|
if (!e[i] || !found) break;
|
|
}
|
|
if (found) break;
|
|
|
|
if (strlen(ext) < 3) break;
|
|
ext += 3;
|
|
}
|
|
if (!found) continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
{
|
|
direntext_t dext;
|
|
memset(&dext, 0, sizeof(dext));
|
|
memcpy(&dext.de, de, sizeof(dext.de));
|
|
get_display_name(&dext, extension, options);
|
|
DirItem.push_back(dext);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (z)
|
|
{
|
|
// Since zip files aren't actually folders the entry to
|
|
// exit the zip file must be added manually.
|
|
direntext_t dext;
|
|
memset(&dext, 0, sizeof(dext));
|
|
dext.de.d_type = DT_DIR;
|
|
strcpy(dext.de.d_name, "..");
|
|
get_display_name(&dext, extension, options);
|
|
DirItem.push_back(dext);
|
|
|
|
mz_zip_reader_end(z);
|
|
delete z;
|
|
}
|
|
|
|
if (d)
|
|
{
|
|
closedir(d);
|
|
}
|
|
|
|
printf("Got %d dir entries\n", flist_nDirEntries());
|
|
if (!flist_nDirEntries()) return 0;
|
|
|
|
std::sort(DirItem.begin(), DirItem.end(), DirentComp());
|
|
if (file_name[0])
|
|
{
|
|
for (int i = 0; i < flist_nDirEntries(); i++)
|
|
{
|
|
if (!strcmp(file_name, DirItem[i].de.d_name))
|
|
{
|
|
iSelectedEntry = i;
|
|
if (iSelectedEntry + (OsdGetSize() / 2) >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize();
|
|
else iFirstEntry = iSelectedEntry - (OsdGetSize() / 2) + 1;
|
|
if (iFirstEntry < 0) iFirstEntry = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return flist_nDirEntries();
|
|
}
|
|
else
|
|
{
|
|
if (flist_nDirEntries() == 0) // directory is empty so there is no point in searching for any entry
|
|
return 0;
|
|
|
|
if (mode == SCANF_END || (mode == SCANF_PREV && iSelectedEntry <= 0))
|
|
{
|
|
iSelectedEntry = flist_nDirEntries() - 1;
|
|
iFirstEntry = iSelectedEntry - OsdGetSize() + 1;
|
|
if (iFirstEntry < 0) iFirstEntry = 0;
|
|
return 0;
|
|
}
|
|
else if (mode == SCANF_NEXT)
|
|
{
|
|
if(iSelectedEntry + 1 < flist_nDirEntries()) // scroll within visible items
|
|
{
|
|
iSelectedEntry++;
|
|
if (iSelectedEntry > iFirstEntry + OsdGetSize() - 1) iFirstEntry = iSelectedEntry - OsdGetSize() + 1;
|
|
}
|
|
else
|
|
{
|
|
// jump to first visible item
|
|
iFirstEntry = 0;
|
|
iSelectedEntry = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
else if (mode == SCANF_PREV)
|
|
{
|
|
if (iSelectedEntry > 0) // scroll within visible items
|
|
{
|
|
iSelectedEntry--;
|
|
if (iSelectedEntry < iFirstEntry) iFirstEntry = iSelectedEntry;
|
|
}
|
|
return 0;
|
|
}
|
|
else if (mode == SCANF_NEXT_PAGE)
|
|
{
|
|
if (iSelectedEntry < iFirstEntry + OsdGetSize() - 2)
|
|
{
|
|
iSelectedEntry = iFirstEntry + OsdGetSize() - 1;
|
|
if (iSelectedEntry >= flist_nDirEntries()) iSelectedEntry = flist_nDirEntries() - 1;
|
|
}
|
|
else
|
|
{
|
|
iSelectedEntry += OsdGetSize();
|
|
iFirstEntry += OsdGetSize();
|
|
if (iSelectedEntry >= flist_nDirEntries())
|
|
{
|
|
iSelectedEntry = flist_nDirEntries() - 1;
|
|
iFirstEntry = iSelectedEntry - OsdGetSize() + 1;
|
|
if (iFirstEntry < 0) iFirstEntry = 0;
|
|
}
|
|
else if (iFirstEntry + OsdGetSize() > flist_nDirEntries())
|
|
{
|
|
iFirstEntry = flist_nDirEntries() - OsdGetSize();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
else if (mode == SCANF_PREV_PAGE)
|
|
{
|
|
if(iSelectedEntry != iFirstEntry)
|
|
{
|
|
iSelectedEntry = iFirstEntry;
|
|
}
|
|
else
|
|
{
|
|
iFirstEntry -= OsdGetSize();
|
|
if (iFirstEntry < 0) iFirstEntry = 0;
|
|
iSelectedEntry = iFirstEntry;
|
|
}
|
|
}
|
|
else if (mode == SCANF_SET_ITEM)
|
|
{
|
|
for (int i = 0; i < flist_nDirEntries(); i++)
|
|
{
|
|
if((DirItem[i].de.d_type == DT_DIR) && !strcmp(DirItem[i].altname, extension))
|
|
{
|
|
iSelectedEntry = i;
|
|
if (iSelectedEntry + (OsdGetSize() / 2) >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize();
|
|
else iFirstEntry = iSelectedEntry - (OsdGetSize() / 2) + 1;
|
|
if (iFirstEntry < 0) iFirstEntry = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//printf("dir scan for key: %x/%c\n", mode, mode);
|
|
mode = toupper(mode);
|
|
if ((mode >= '0' && mode <= '9') || (mode >= 'A' && mode <= 'Z'))
|
|
{
|
|
int found = -1;
|
|
for (int i = iSelectedEntry+1; i < flist_nDirEntries(); i++)
|
|
{
|
|
if (toupper(DirItem[i].altname[0]) == mode)
|
|
{
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found < 0)
|
|
{
|
|
for (int i = 0; i < flist_nDirEntries(); i++)
|
|
{
|
|
if (toupper(DirItem[i].altname[0]) == mode)
|
|
{
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found >= 0)
|
|
{
|
|
iSelectedEntry = found;
|
|
if (iSelectedEntry + (OsdGetSize() / 2) >= flist_nDirEntries()) iFirstEntry = flist_nDirEntries() - OsdGetSize();
|
|
else iFirstEntry = iSelectedEntry - (OsdGetSize()/2) + 1;
|
|
if (iFirstEntry < 0) iFirstEntry = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int flist_nDirEntries()
|
|
{
|
|
return DirItem.size();
|
|
}
|
|
|
|
int flist_iFirstEntry()
|
|
{
|
|
return iFirstEntry;
|
|
}
|
|
|
|
void flist_iFirstEntryInc()
|
|
{
|
|
iFirstEntry++;
|
|
}
|
|
|
|
int flist_iSelectedEntry()
|
|
{
|
|
return iSelectedEntry;
|
|
}
|
|
|
|
direntext_t* flist_DirItem(int n)
|
|
{
|
|
return &DirItem[n];
|
|
}
|
|
|
|
direntext_t* flist_SelectedItem()
|
|
{
|
|
return &DirItem[iSelectedEntry];
|
|
}
|
|
|
|
bool isMraName(char *path)
|
|
{
|
|
char *spl = strrchr(path, '.');
|
|
return (spl && !strcmp(spl, ".mra"));
|
|
}
|
|
|