49be3faf07
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13222 a1c6a512-1295-4272-9138-f99709370657
601 lines
14 KiB
C
601 lines
14 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2002 Daniel Stenberg
|
|
*
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#ifdef __FreeBSD__
|
|
#include <sys/param.h>
|
|
#include <sys/mount.h>
|
|
#elif defined(__APPLE__)
|
|
#include <sys/param.h>
|
|
#include <sys/mount.h>
|
|
#elif !defined(WIN32)
|
|
#include <sys/vfs.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#ifndef _MSC_VER
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#else
|
|
#include "dir-win32.h"
|
|
#endif
|
|
|
|
#define MAX_PATH 260
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include "debug.h"
|
|
#include "config.h"
|
|
|
|
/* Windows (and potentially other OSes) distinguish binary and text files.
|
|
* Define a dummy for the others. */
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
/* Unicode compatibility for win32 */
|
|
#if defined __MINGW32__
|
|
/* Rockbox unicode functions */
|
|
extern const unsigned char* utf8decode(const unsigned char *utf8,
|
|
unsigned short *ucs);
|
|
extern unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8);
|
|
|
|
/* Static buffers for the conversion results. This isn't thread safe,
|
|
* but it's sufficient for rockbox. */
|
|
static unsigned char convbuf1[3*MAX_PATH];
|
|
static unsigned char convbuf2[3*MAX_PATH];
|
|
|
|
static wchar_t* utf8_to_ucs2(const unsigned char *utf8, void *buffer)
|
|
{
|
|
wchar_t *ucs = buffer;
|
|
|
|
while (*utf8)
|
|
utf8 = utf8decode(utf8, ucs++);
|
|
|
|
*ucs = 0;
|
|
return buffer;
|
|
}
|
|
static unsigned char *ucs2_to_utf8(const wchar_t *ucs, unsigned char *buffer)
|
|
{
|
|
unsigned char *utf8 = buffer;
|
|
|
|
while (*ucs)
|
|
utf8 = utf8encode(*ucs++, utf8);
|
|
|
|
*utf8 = 0;
|
|
return buffer;
|
|
}
|
|
|
|
#define UTF8_TO_OS(a) utf8_to_ucs2(a,convbuf1)
|
|
#define OS_TO_UTF8(a) ucs2_to_utf8(a,convbuf1)
|
|
#define DIR_T _WDIR
|
|
#define DIRENT_T struct _wdirent
|
|
#define STAT_T struct _stat
|
|
extern int _wmkdir(const wchar_t*);
|
|
extern int _wrmdir(const wchar_t*);
|
|
#define MKDIR(a,b) (_wmkdir)(UTF8_TO_OS(a))
|
|
#define RMDIR(a) (_wrmdir)(UTF8_TO_OS(a))
|
|
#define OPENDIR(a) (_wopendir)(UTF8_TO_OS(a))
|
|
#define READDIR(a) (_wreaddir)(a)
|
|
#define CLOSEDIR(a) (_wclosedir)(a)
|
|
#define STAT(a,b) (_wstat)(UTF8_TO_OS(a),b)
|
|
#define OPEN(a,b,c) (_wopen)(UTF8_TO_OS(a),b,c)
|
|
#define REMOVE(a) (_wremove)(UTF8_TO_OS(a))
|
|
#define RENAME(a,b) (_wrename)(UTF8_TO_OS(a),utf8_to_ucs2(b,convbuf2))
|
|
|
|
#else /* !__MINGW32__ */
|
|
|
|
#define UTF8_TO_OS(a) (a)
|
|
#define OS_TO_UTF8(a) (a)
|
|
#define DIR_T DIR
|
|
#define DIRENT_T struct dirent
|
|
#define STAT_T struct stat
|
|
#define MKDIR(a,b) (mkdir)(a,b)
|
|
#define RMDIR(a) (rmdir)(a)
|
|
#define OPENDIR(a) (opendir)(a)
|
|
#define READDIR(a) (readdir)(a)
|
|
#define CLOSEDIR(a) (closedir)(a)
|
|
#define STAT(a,b) (stat)(a,b)
|
|
#define OPEN(a,b,c) (open)(a,b,c)
|
|
#define REMOVE(a) (remove)(a)
|
|
#define RENAME(a,b) (rename)(a,b)
|
|
|
|
#endif /* !__MINGW32__ */
|
|
|
|
|
|
#ifdef HAVE_DIRCACHE
|
|
void dircache_remove(const char *name);
|
|
void dircache_rename(const char *oldpath, const char *newpath);
|
|
#endif
|
|
|
|
#define SIMULATOR_ARCHOS_ROOT "archos"
|
|
|
|
struct sim_dirent {
|
|
unsigned char d_name[MAX_PATH];
|
|
int attribute;
|
|
long size;
|
|
long startcluster;
|
|
unsigned short wrtdate; /* Last write date */
|
|
unsigned short wrttime; /* Last write time */
|
|
};
|
|
|
|
struct dirstruct {
|
|
void *dir; /* actually a DIR* dir */
|
|
char *name;
|
|
} SIM_DIR;
|
|
|
|
struct mydir {
|
|
DIR_T *dir;
|
|
char *name;
|
|
};
|
|
|
|
typedef struct mydir MYDIR;
|
|
|
|
#if 1 /* maybe this needs disabling for MSVC... */
|
|
static unsigned int rockbox2sim(int opt)
|
|
{
|
|
int newopt = O_BINARY;
|
|
|
|
if(opt & 1)
|
|
newopt |= O_WRONLY;
|
|
if(opt & 2)
|
|
newopt |= O_RDWR;
|
|
if(opt & 4)
|
|
newopt |= O_CREAT;
|
|
if(opt & 8)
|
|
newopt |= O_APPEND;
|
|
if(opt & 0x10)
|
|
newopt |= O_TRUNC;
|
|
|
|
return newopt;
|
|
}
|
|
#endif
|
|
|
|
MYDIR *sim_opendir(const char *name)
|
|
{
|
|
char buffer[MAX_PATH]; /* sufficiently big */
|
|
DIR_T *dir;
|
|
|
|
#ifndef __PCTOOL__
|
|
if(name[0] == '/')
|
|
{
|
|
snprintf(buffer, sizeof(buffer), "%s%s", SIMULATOR_ARCHOS_ROOT, name);
|
|
dir=(DIR_T *)OPENDIR(buffer);
|
|
}
|
|
else
|
|
#endif
|
|
dir=(DIR_T *)OPENDIR(name);
|
|
|
|
if(dir) {
|
|
MYDIR *my = (MYDIR *)malloc(sizeof(MYDIR));
|
|
my->dir = dir;
|
|
my->name = (char *)strdup(name);
|
|
|
|
return my;
|
|
}
|
|
/* failed open, return NULL */
|
|
return (MYDIR *)0;
|
|
}
|
|
|
|
struct sim_dirent *sim_readdir(MYDIR *dir)
|
|
{
|
|
char buffer[512]; /* sufficiently big */
|
|
static struct sim_dirent secret;
|
|
STAT_T s;
|
|
DIRENT_T *x11 = READDIR(dir->dir);
|
|
struct tm* tm;
|
|
|
|
if(!x11)
|
|
return (struct sim_dirent *)0;
|
|
|
|
strcpy((char *)secret.d_name, OS_TO_UTF8(x11->d_name));
|
|
|
|
/* build file name */
|
|
#ifdef __PCTOOL__
|
|
snprintf(buffer, sizeof(buffer), "%s/%s", dir->name, secret.d_name);
|
|
#else
|
|
snprintf(buffer, sizeof(buffer), SIMULATOR_ARCHOS_ROOT "%s/%s",
|
|
dir->name, secret.d_name);
|
|
#endif
|
|
STAT(buffer, &s); /* get info */
|
|
|
|
#define ATTR_DIRECTORY 0x10
|
|
|
|
secret.attribute = S_ISDIR(s.st_mode)?ATTR_DIRECTORY:0;
|
|
secret.size = s.st_size;
|
|
|
|
tm = localtime(&(s.st_mtime));
|
|
secret.wrtdate = ((tm->tm_year - 80) << 9) |
|
|
((tm->tm_mon + 1) << 5) |
|
|
tm->tm_mday;
|
|
secret.wrttime = (tm->tm_hour << 11) |
|
|
(tm->tm_min << 5) |
|
|
(tm->tm_sec >> 1);
|
|
return &secret;
|
|
}
|
|
|
|
void sim_closedir(MYDIR *dir)
|
|
{
|
|
free(dir->name);
|
|
CLOSEDIR(dir->dir);
|
|
|
|
free(dir);
|
|
}
|
|
|
|
int sim_open(const char *name, int o)
|
|
{
|
|
char buffer[MAX_PATH]; /* sufficiently big */
|
|
int opts = rockbox2sim(o);
|
|
|
|
#ifndef __PCTOOL__
|
|
if(name[0] == '/')
|
|
{
|
|
snprintf(buffer, sizeof(buffer), "%s%s", SIMULATOR_ARCHOS_ROOT, name);
|
|
|
|
debugf("We open the real file '%s'\n", buffer);
|
|
return OPEN(buffer, opts, 0666);
|
|
}
|
|
|
|
fprintf(stderr, "WARNING, bad file name lacks slash: %s\n",
|
|
name);
|
|
return -1;
|
|
#else
|
|
return OPEN(name, opts, 0666);
|
|
#endif
|
|
|
|
}
|
|
|
|
int sim_creat(const char *name)
|
|
{
|
|
#ifndef __PCTOOL__
|
|
char buffer[MAX_PATH]; /* sufficiently big */
|
|
if(name[0] == '/')
|
|
{
|
|
snprintf(buffer, sizeof(buffer), "%s%s", SIMULATOR_ARCHOS_ROOT, name);
|
|
|
|
debugf("We create the real file '%s'\n", buffer);
|
|
return OPEN(buffer, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
}
|
|
fprintf(stderr, "WARNING, bad file name lacks slash: %s\n", name);
|
|
return -1;
|
|
#else
|
|
return OPEN(name, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
#endif
|
|
}
|
|
|
|
int sim_mkdir(const char *name)
|
|
{
|
|
#ifdef __PCTOOL__
|
|
return MKDIR(name, 0777);
|
|
#else
|
|
char buffer[MAX_PATH]; /* sufficiently big */
|
|
|
|
snprintf(buffer, sizeof(buffer), "%s%s", SIMULATOR_ARCHOS_ROOT, name);
|
|
|
|
debugf("We create the real directory '%s'\n", buffer);
|
|
return MKDIR(buffer, 0777);
|
|
#endif
|
|
}
|
|
|
|
int sim_rmdir(const char *name)
|
|
{
|
|
#ifdef __PCTOOL__
|
|
return RMDIR(name);
|
|
#else
|
|
char buffer[MAX_PATH]; /* sufficiently big */
|
|
if(name[0] == '/')
|
|
{
|
|
snprintf(buffer, sizeof(buffer), "%s%s", SIMULATOR_ARCHOS_ROOT, name);
|
|
|
|
debugf("We remove the real directory '%s'\n", buffer);
|
|
return RMDIR(buffer);
|
|
}
|
|
return RMDIR(name);
|
|
#endif
|
|
}
|
|
|
|
int sim_remove(const char *name)
|
|
{
|
|
#ifdef __PCTOOL__
|
|
return REMOVE(name);
|
|
#else
|
|
char buffer[MAX_PATH]; /* sufficiently big */
|
|
|
|
#ifdef HAVE_DIRCACHE
|
|
dircache_remove(name);
|
|
#endif
|
|
|
|
if(name[0] == '/') {
|
|
snprintf(buffer, sizeof(buffer), "%s%s", SIMULATOR_ARCHOS_ROOT, name);
|
|
|
|
debugf("We remove the real file '%s'\n", buffer);
|
|
return REMOVE(buffer);
|
|
}
|
|
return REMOVE(name);
|
|
#endif
|
|
}
|
|
|
|
int sim_rename(const char *oldpath, const char* newpath)
|
|
{
|
|
#ifdef __PCTOOL__
|
|
return RENAME(oldpath, newpath);
|
|
#else
|
|
char buffer1[MAX_PATH];
|
|
char buffer2[MAX_PATH];
|
|
|
|
#ifdef HAVE_DIRCACHE
|
|
dircache_rename(oldpath, newpath);
|
|
#endif
|
|
|
|
if(oldpath[0] == '/') {
|
|
snprintf(buffer1, sizeof(buffer1), "%s%s", SIMULATOR_ARCHOS_ROOT,
|
|
oldpath);
|
|
snprintf(buffer2, sizeof(buffer2), "%s%s", SIMULATOR_ARCHOS_ROOT,
|
|
newpath);
|
|
|
|
debugf("We rename the real file '%s' to '%s'\n", buffer1, buffer2);
|
|
return RENAME(buffer1, buffer2);
|
|
}
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/* rockbox off_t may be different from system off_t */
|
|
long sim_lseek(int fildes, long offset, int whence)
|
|
{
|
|
return lseek(fildes, offset, whence);
|
|
}
|
|
|
|
long sim_filesize(int fd)
|
|
{
|
|
#ifdef WIN32
|
|
return _filelength(fd);
|
|
#else
|
|
struct stat buf;
|
|
|
|
if (!fstat(fd, &buf))
|
|
return buf.st_size;
|
|
else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
void fat_size(unsigned int* size, unsigned int* free)
|
|
{
|
|
#ifdef WIN32
|
|
long secperclus, bytespersec, free_clusters, num_clusters;
|
|
|
|
if (GetDiskFreeSpace(NULL, &secperclus, &bytespersec, &free_clusters,
|
|
&num_clusters)) {
|
|
if (size)
|
|
*size = num_clusters * secperclus / 2 * (bytespersec / 512);
|
|
if (free)
|
|
*free = free_clusters * secperclus / 2 * (bytespersec / 512);
|
|
}
|
|
#else
|
|
struct statfs fs;
|
|
|
|
if (!statfs(".", &fs)) {
|
|
DEBUGF("statfs: bsize=%d blocks=%ld free=%ld\n",
|
|
(int)fs.f_bsize, fs.f_blocks, fs.f_bfree);
|
|
if (size)
|
|
*size = fs.f_blocks * (fs.f_bsize / 1024);
|
|
if (free)
|
|
*free = fs.f_bfree * (fs.f_bsize / 1024);
|
|
}
|
|
#endif
|
|
else {
|
|
if (size)
|
|
*size = 0;
|
|
if (free)
|
|
*free = 0;
|
|
}
|
|
}
|
|
|
|
int sim_fsync(int fd)
|
|
{
|
|
#ifdef WIN32
|
|
return _commit(fd);
|
|
#else
|
|
return fsync(fd);
|
|
#endif
|
|
}
|
|
|
|
#ifdef WIN32
|
|
/* sim-win32 */
|
|
#define dlopen(_x_, _y_) LoadLibraryW(UTF8_TO_OS(_x_))
|
|
#define dlsym(_x_, _y_) (void *)GetProcAddress(_x_, _y_)
|
|
#define dlclose(_x_) FreeLibrary(_x_)
|
|
#else
|
|
/* sim-x11 */
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
#define TEMP_CODEC_FILE "archos/_temp_codec%d.dll"
|
|
|
|
void *sim_codec_load_ram(char* codecptr, int size,
|
|
void* ptr2, int bufwrap, void **pd)
|
|
{
|
|
void *hdr;
|
|
char path[MAX_PATH];
|
|
int fd;
|
|
int copy_n;
|
|
int codec_count;
|
|
#ifdef WIN32
|
|
char buf[MAX_PATH];
|
|
#endif
|
|
|
|
*pd = NULL;
|
|
|
|
/* We have to create the dynamic link library file from ram so we
|
|
can simulate the codec loading. With voice and crossfade,
|
|
multiple codecs may be loaded at the same time, so we need
|
|
to find an unused filename */
|
|
for (codec_count = 0; codec_count < 10; codec_count++)
|
|
{
|
|
snprintf(path, sizeof(path), TEMP_CODEC_FILE, codec_count);
|
|
|
|
fd = OPEN(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRWXU);
|
|
if (fd >= 0)
|
|
break; /* Created a file ok */
|
|
}
|
|
if (fd < 0)
|
|
{
|
|
DEBUGF("failed to open for write: %s\n", path);
|
|
return NULL;
|
|
}
|
|
|
|
if (bufwrap == 0)
|
|
bufwrap = size;
|
|
|
|
copy_n = bufwrap < size ? bufwrap : size;
|
|
if (write(fd, codecptr, copy_n) != copy_n) {
|
|
DEBUGF("write failed");
|
|
return NULL;
|
|
}
|
|
size -= copy_n;
|
|
if (size > 0) {
|
|
if (write(fd, ptr2, size) != size) {
|
|
DEBUGF("write failed [2]");
|
|
return NULL;
|
|
}
|
|
}
|
|
close(fd);
|
|
|
|
/* Now load the library. */
|
|
*pd = dlopen(path, RTLD_NOW);
|
|
if (*pd == NULL) {
|
|
DEBUGF("failed to load %s\n", path);
|
|
#ifdef WIN32
|
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
|
|
buf, sizeof buf, NULL);
|
|
DEBUGF("dlopen(%s): %s\n", path, buf);
|
|
#else
|
|
DEBUGF("dlopen(%s): %s\n", path, dlerror());
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
hdr = dlsym(*pd, "__header");
|
|
if (!hdr)
|
|
hdr = dlsym(*pd, "___header");
|
|
|
|
return hdr; /* maybe NULL if symbol not present */
|
|
}
|
|
|
|
void sim_codec_close(void *pd)
|
|
{
|
|
dlclose(pd);
|
|
}
|
|
|
|
void *sim_plugin_load(char *plugin, void **pd)
|
|
{
|
|
void *hdr;
|
|
char path[MAX_PATH];
|
|
#ifdef WIN32
|
|
char buf[MAX_PATH];
|
|
#endif
|
|
|
|
snprintf(path, sizeof(path), "archos%s", plugin);
|
|
|
|
*pd = NULL;
|
|
|
|
*pd = dlopen(path, RTLD_NOW);
|
|
if (*pd == NULL) {
|
|
DEBUGF("failed to load %s\n", plugin);
|
|
#ifdef WIN32
|
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
|
|
buf, sizeof(buf), NULL);
|
|
DEBUGF("dlopen(%s): %s\n", path, buf);
|
|
#else
|
|
DEBUGF("dlopen(%s): %s\n", path, dlerror());
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
hdr = dlsym(*pd, "__header");
|
|
if (!hdr)
|
|
hdr = dlsym(*pd, "___header");
|
|
|
|
return hdr; /* maybe NULL if symbol not present */
|
|
}
|
|
|
|
void sim_plugin_close(void *pd)
|
|
{
|
|
dlclose(pd);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
static unsigned old_cp;
|
|
|
|
void debug_exit(void)
|
|
{
|
|
/* Reset console output codepage */
|
|
SetConsoleOutputCP(old_cp);
|
|
}
|
|
|
|
void debug_init(void)
|
|
{
|
|
old_cp = GetConsoleOutputCP();
|
|
/* Set console output codepage to UTF8. Only works
|
|
* correctly when the console uses a truetype font. */
|
|
SetConsoleOutputCP(65001);
|
|
atexit(debug_exit);
|
|
}
|
|
#else
|
|
void debug_init(void)
|
|
{
|
|
/* nothing to be done */
|
|
}
|
|
#endif
|
|
|
|
void debugf(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start( ap, fmt );
|
|
vfprintf( stderr, fmt, ap );
|
|
va_end( ap );
|
|
}
|
|
|
|
void ldebugf(const char* file, int line, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start( ap, fmt );
|
|
fprintf( stderr, "%s:%d ", file, line );
|
|
vfprintf( stderr, fmt, ap );
|
|
va_end( ap );
|
|
}
|
|
|
|
/* rockbox off_t may be different from system off_t */
|
|
int sim_ftruncate(int fd, long length)
|
|
{
|
|
#ifdef WIN32
|
|
return _chsize(fd, length);
|
|
#else
|
|
return ftruncate(fd, length);
|
|
#endif
|
|
}
|