Rockbox as an application: add get_user_file_path().
[kugel-rb.git] / uisimulator / common / io.c
blob260e880b62bdb4942d925817f11d2332e45679f9
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Daniel Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <sys/stat.h>
27 #include <time.h>
28 #ifndef WIN32
29 #include <sys/statvfs.h>
30 #endif
32 #ifdef WIN32
33 #include <windows.h>
34 #endif
36 #ifndef _MSC_VER
37 #include <dirent.h>
38 #include <unistd.h>
39 #else
40 #include "dir-win32.h"
41 #endif
43 #include <fcntl.h>
44 #include <SDL.h>
45 #include <SDL_thread.h>
46 #include "thread.h"
47 #include "kernel.h"
48 #include "debug.h"
49 #include "config.h"
50 #include "ata.h" /* for IF_MV2 et al. */
51 #include "thread-sdl.h"
52 #include "rbpaths.h"
54 /* keep this in sync with file.h! */
55 #undef MAX_PATH /* this avoids problems when building simulator */
56 #define MAX_PATH 260
57 #define MAX_OPEN_FILES 11
59 /* Windows (and potentially other OSes) distinguish binary and text files.
60 * Define a dummy for the others. */
61 #ifndef O_BINARY
62 #define O_BINARY 0
63 #endif
65 /* Unicode compatibility for win32 */
66 #if defined __MINGW32__
67 /* Rockbox unicode functions */
68 extern const unsigned char* utf8decode(const unsigned char *utf8,
69 unsigned short *ucs);
70 extern unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8);
72 /* Static buffers for the conversion results. This isn't thread safe,
73 * but it's sufficient for rockbox. */
74 static unsigned char convbuf1[3*MAX_PATH];
75 static unsigned char convbuf2[3*MAX_PATH];
77 static wchar_t* utf8_to_ucs2(const unsigned char *utf8, void *buffer)
79 wchar_t *ucs = buffer;
81 while (*utf8)
82 utf8 = utf8decode(utf8, ucs++);
84 *ucs = 0;
85 return buffer;
87 static unsigned char *ucs2_to_utf8(const wchar_t *ucs, unsigned char *buffer)
89 unsigned char *utf8 = buffer;
91 while (*ucs)
92 utf8 = utf8encode(*ucs++, utf8);
94 *utf8 = 0;
95 return buffer;
98 #define UTF8_TO_OS(a) utf8_to_ucs2(a,convbuf1)
99 #define OS_TO_UTF8(a) ucs2_to_utf8(a,convbuf1)
100 #define DIR_T _WDIR
101 #define DIRENT_T struct _wdirent
102 #define STAT_T struct _stat
103 extern int _wmkdir(const wchar_t*);
104 extern int _wrmdir(const wchar_t*);
105 #define MKDIR(a,b) (_wmkdir)(UTF8_TO_OS(a))
106 #define RMDIR(a) (_wrmdir)(UTF8_TO_OS(a))
107 #define OPENDIR(a) (_wopendir)(UTF8_TO_OS(a))
108 #define READDIR(a) (_wreaddir)(a)
109 #define CLOSEDIR(a) (_wclosedir)(a)
110 #define STAT(a,b) (_wstat)(UTF8_TO_OS(a),b)
111 /* empty variable parameter list doesn't work for variadic macros,
112 * so pretend the second parameter is variable too */
113 #define OPEN(a,...) (_wopen)(UTF8_TO_OS(a), __VA_ARGS__)
114 #define CLOSE(a) (close)(a)
115 #define REMOVE(a) (_wremove)(UTF8_TO_OS(a))
116 #define RENAME(a,b) (_wrename)(UTF8_TO_OS(a),utf8_to_ucs2(b,convbuf2))
118 #else /* !__MINGW32__ */
120 #define UTF8_TO_OS(a) (a)
121 #define OS_TO_UTF8(a) (a)
122 #define DIR_T DIR
123 #define DIRENT_T struct dirent
124 #define STAT_T struct stat
125 #define MKDIR(a,b) (mkdir)(a,b)
126 #define RMDIR(a) (rmdir)(a)
127 #define OPENDIR(a) (opendir)(a)
128 #define READDIR(a) (readdir)(a)
129 #define CLOSEDIR(a) (closedir)(a)
130 #define STAT(a,b) (stat)(a,b)
131 /* empty variable parameter list doesn't work for variadic macros,
132 * so pretend the second parameter is variable too */
133 #define OPEN(a, ...) (open)(a, __VA_ARGS__)
134 #define CLOSE(x) (close)(x)
135 #define REMOVE(a) (remove)(a)
136 #define RENAME(a,b) (rename)(a,b)
138 #endif /* !__MINGW32__ */
141 #ifdef HAVE_DIRCACHE
142 void dircache_remove(const char *name);
143 void dircache_rename(const char *oldname, const char *newname);
144 #endif
147 #define SIMULATOR_DEFAULT_ROOT "simdisk"
148 extern const char *sim_root_dir;
150 static int num_openfiles = 0;
152 struct sim_dirent {
153 unsigned char d_name[MAX_PATH];
154 int attribute;
155 long size;
156 long startcluster;
157 unsigned short wrtdate; /* Last write date */
158 unsigned short wrttime; /* Last write time */
161 struct dirstruct {
162 void *dir; /* actually a DIR* dir */
163 char *name;
164 } SIM_DIR;
166 struct mydir {
167 DIR_T *dir;
168 char *name;
171 typedef struct mydir MYDIR;
173 #if 1 /* maybe this needs disabling for MSVC... */
174 static unsigned int rockbox2sim(int opt)
176 int newopt = O_BINARY;
178 if(opt & 1)
179 newopt |= O_WRONLY;
180 if(opt & 2)
181 newopt |= O_RDWR;
182 if(opt & 4)
183 newopt |= O_CREAT;
184 if(opt & 8)
185 newopt |= O_APPEND;
186 if(opt & 0x10)
187 newopt |= O_TRUNC;
189 return newopt;
191 #endif
193 /** Simulator I/O engine routines **/
194 #define IO_YIELD_THRESHOLD 512
196 enum
198 IO_READ,
199 IO_WRITE,
202 struct sim_io
204 struct mutex sim_mutex; /* Rockbox mutex */
205 int cmd; /* The command to perform */
206 int ready; /* I/O ready flag - 1= ready */
207 int fd; /* The file to read/write */
208 void *buf; /* The buffer to read/write */
209 size_t count; /* Number of bytes to read/write */
210 size_t accum; /* Acculated bytes transferred */
213 static struct sim_io io;
215 int ata_init(void)
217 /* Initialize the rockbox kernel objects on a rockbox thread */
218 mutex_init(&io.sim_mutex);
219 io.accum = 0;
220 return 1;
223 int ata_spinup_time(void)
225 return HZ;
228 static ssize_t io_trigger_and_wait(int cmd)
230 void *mythread = NULL;
231 ssize_t result;
233 if (io.count > IO_YIELD_THRESHOLD ||
234 (io.accum += io.count) >= IO_YIELD_THRESHOLD)
236 /* Allow other rockbox threads to run */
237 io.accum = 0;
238 mythread = sim_thread_unlock();
241 switch (cmd)
243 case IO_READ:
244 result = read(io.fd, io.buf, io.count);
245 break;
246 case IO_WRITE:
247 result = write(io.fd, io.buf, io.count);
248 break;
251 /* Regain our status as current */
252 if (mythread != NULL)
254 sim_thread_lock(mythread);
257 return result;
260 #if !defined(__PCTOOL__) && !defined(APPLICATION)
261 static const char *get_sim_pathname(const char *name)
263 static char buffer[MAX_PATH]; /* sufficiently big */
265 if(name[0] == '/')
267 snprintf(buffer, sizeof(buffer), "%s%s",
268 sim_root_dir != NULL ? sim_root_dir : SIMULATOR_DEFAULT_ROOT, name);
269 return buffer;
271 fprintf(stderr, "WARNING, bad file name lacks slash: %s\n", name);
272 return name;
274 #else
275 #define get_sim_pathname(name) name
276 #endif
278 MYDIR *sim_opendir(const char *name)
280 DIR_T *dir;
282 dir = (DIR_T *) OPENDIR(get_sim_pathname(name));
284 if (dir)
286 MYDIR *my = (MYDIR *)malloc(sizeof(MYDIR));
287 my->dir = dir;
288 my->name = (char *)malloc(strlen(name)+1);
289 strcpy(my->name, name);
291 return my;
293 /* failed open, return NULL */
294 return (MYDIR *)0;
297 struct sim_dirent *sim_readdir(MYDIR *dir)
299 char buffer[MAX_PATH]; /* sufficiently big */
300 static struct sim_dirent secret;
301 STAT_T s;
302 DIRENT_T *x11 = READDIR(dir->dir);
303 struct tm* tm;
305 if(!x11)
306 return (struct sim_dirent *)0;
308 strcpy((char *)secret.d_name, OS_TO_UTF8(x11->d_name));
310 /* build file name */
311 snprintf(buffer, sizeof(buffer), "%s/%s",
312 get_sim_pathname(dir->name), secret.d_name);
313 STAT(buffer, &s); /* get info */
315 #define ATTR_DIRECTORY 0x10
317 secret.attribute = S_ISDIR(s.st_mode)?ATTR_DIRECTORY:0;
318 secret.size = s.st_size;
320 tm = localtime(&(s.st_mtime));
321 secret.wrtdate = ((tm->tm_year - 80) << 9) |
322 ((tm->tm_mon + 1) << 5) |
323 tm->tm_mday;
324 secret.wrttime = (tm->tm_hour << 11) |
325 (tm->tm_min << 5) |
326 (tm->tm_sec >> 1);
327 return &secret;
330 void sim_closedir(MYDIR *dir)
332 free(dir->name);
333 CLOSEDIR(dir->dir);
335 free(dir);
338 int sim_open(const char *name, int o, ...)
340 int opts = rockbox2sim(o);
341 int ret;
342 if (num_openfiles >= MAX_OPEN_FILES)
343 return -2;
345 if (opts & O_CREAT)
347 va_list ap;
348 va_start(ap, o);
349 mode_t mode = va_arg(ap, unsigned int);
350 ret = OPEN(get_sim_pathname(name), opts, mode);
351 va_end(ap);
353 else
354 ret = OPEN(get_sim_pathname(name), opts);
356 if (ret >= 0)
357 num_openfiles++;
358 return ret;
361 int sim_close(int fd)
363 int ret;
364 ret = CLOSE(fd);
365 if (ret == 0)
366 num_openfiles--;
367 return ret;
370 int sim_creat(const char *name, mode_t mode)
372 return OPEN(get_sim_pathname(name), O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, mode);
375 ssize_t sim_read(int fd, void *buf, size_t count)
377 ssize_t result;
379 mutex_lock(&io.sim_mutex);
381 /* Setup parameters */
382 io.fd = fd;
383 io.buf = buf;
384 io.count = count;
386 result = io_trigger_and_wait(IO_READ);
388 mutex_unlock(&io.sim_mutex);
390 return result;
393 ssize_t sim_write(int fd, const void *buf, size_t count)
395 ssize_t result;
397 mutex_lock(&io.sim_mutex);
399 io.fd = fd;
400 io.buf = (void*)buf;
401 io.count = count;
403 result = io_trigger_and_wait(IO_WRITE);
405 mutex_unlock(&io.sim_mutex);
407 return result;
410 int sim_mkdir(const char *name)
412 return MKDIR(get_sim_pathname(name), 0777);
415 int sim_rmdir(const char *name)
417 return RMDIR(get_sim_pathname(name));
420 int sim_remove(const char *name)
422 #ifdef HAVE_DIRCACHE
423 dircache_remove(name);
424 #endif
425 return REMOVE(get_sim_pathname(name));
428 int sim_rename(const char *oldname, const char *newname)
430 char sim_old[MAX_PATH];
431 char sim_new[MAX_PATH];
432 #ifdef HAVE_DIRCACHE
433 dircache_rename(oldname, newname);
434 #endif
435 // This is needed as get_sim_pathname() has a static buffer
436 strncpy(sim_old, get_sim_pathname(oldname), MAX_PATH);
437 strncpy(sim_new, get_sim_pathname(newname), MAX_PATH);
438 return RENAME(sim_old, sim_new);
441 /* rockbox off_t may be different from system off_t */
442 long sim_lseek(int fildes, long offset, int whence)
444 return lseek(fildes, offset, whence);
447 long sim_filesize(int fd)
449 #ifdef WIN32
450 return _filelength(fd);
451 #else
452 struct stat buf;
454 if (!fstat(fd, &buf))
455 return buf.st_size;
456 else
457 return -1;
458 #endif
461 void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
463 #ifdef HAVE_MULTIVOLUME
464 if (volume != 0) {
465 /* debugf("io.c: fat_size(volume=%d); simulator only supports volume 0\n",volume); */
467 if (size) *size = 0;
468 if (free) *free = 0;
469 return;
471 #endif
473 #ifdef WIN32
474 long secperclus, bytespersec, free_clusters, num_clusters;
476 if (GetDiskFreeSpace(NULL, &secperclus, &bytespersec, &free_clusters,
477 &num_clusters)) {
478 if (size)
479 *size = num_clusters * secperclus / 2 * (bytespersec / 512);
480 if (free)
481 *free = free_clusters * secperclus / 2 * (bytespersec / 512);
483 #else
484 struct statvfs vfs;
486 if (!statvfs(".", &vfs)) {
487 DEBUGF("statvfs: frsize=%d blocks=%ld free=%ld\n",
488 (int)vfs.f_frsize, (long)vfs.f_blocks, (long)vfs.f_bfree);
489 if (size)
490 *size = vfs.f_blocks / 2 * (vfs.f_frsize / 512);
491 if (free)
492 *free = vfs.f_bfree / 2 * (vfs.f_frsize / 512);
494 #endif
495 else {
496 if (size)
497 *size = 0;
498 if (free)
499 *free = 0;
503 int sim_fsync(int fd)
505 #ifdef WIN32
506 return _commit(fd);
507 #else
508 return fsync(fd);
509 #endif
512 #ifdef WIN32
513 /* sim-win32 */
514 #define dlopen(_x_, _y_) LoadLibraryW(UTF8_TO_OS(_x_))
515 #define dlsym(_x_, _y_) (void *)GetProcAddress(_x_, _y_)
516 #define dlclose(_x_) FreeLibrary(_x_)
517 #else
518 /* sim-x11 */
519 #include <dlfcn.h>
520 #endif
522 void *sim_codec_load_ram(char* codecptr, int size, void **pd)
524 void *hdr;
525 char path[MAX_PATH];
526 int fd;
527 int codec_count;
528 #ifdef WIN32
529 char buf[MAX_PATH];
530 #endif
532 *pd = NULL;
534 /* We have to create the dynamic link library file from ram so we
535 can simulate the codec loading. With voice and crossfade,
536 multiple codecs may be loaded at the same time, so we need
537 to find an unused filename */
538 for (codec_count = 0; codec_count < 10; codec_count++)
540 char name[MAX_PATH];
541 const char *_name = get_user_file_path(ROCKBOX_DIR, 0, name, sizeof(name));
542 snprintf(path, sizeof(path), "%s/_temp_codec%d.dll", get_sim_pathname(_name), codec_count);
543 fd = OPEN(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRWXU);
544 if (fd >= 0)
545 break; /* Created a file ok */
547 if (fd < 0)
549 DEBUGF("failed to open for write: %s\n", path);
550 return NULL;
553 if (write(fd, codecptr, size) != size)
555 DEBUGF("write failed");
556 return NULL;
558 close(fd);
560 /* Now load the library. */
561 *pd = dlopen(path, RTLD_NOW);
562 if (*pd == NULL)
564 DEBUGF("failed to load %s\n", path);
565 #ifdef WIN32
566 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
567 buf, sizeof buf, NULL);
568 DEBUGF("dlopen(%s): %s\n", path, buf);
569 #else
570 DEBUGF("dlopen(%s): %s\n", path, dlerror());
571 #endif
572 return NULL;
575 hdr = dlsym(*pd, "__header");
576 if (!hdr)
577 hdr = dlsym(*pd, "___header");
579 return hdr; /* maybe NULL if symbol not present */
582 void sim_codec_close(void *pd)
584 dlclose(pd);
587 void *sim_plugin_load(char *plugin, void **pd)
589 void *hdr;
590 char path[MAX_PATH];
591 #ifdef WIN32
592 char buf[MAX_PATH];
593 #endif
595 snprintf(path, sizeof(path), "%s", get_sim_pathname(plugin));
597 *pd = NULL;
599 *pd = dlopen(path, RTLD_NOW);
600 if (*pd == NULL) {
601 DEBUGF("failed to load %s\n", plugin);
602 #ifdef WIN32
603 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
604 buf, sizeof(buf), NULL);
605 DEBUGF("dlopen(%s): %s\n", path, buf);
606 #else
607 DEBUGF("dlopen(%s): %s\n", path, dlerror());
608 #endif
609 return NULL;
612 hdr = dlsym(*pd, "__header");
613 if (!hdr)
614 hdr = dlsym(*pd, "___header");
616 return hdr; /* maybe NULL if symbol not present */
619 void sim_plugin_close(void *pd)
621 dlclose(pd);
624 #ifdef WIN32
625 static unsigned old_cp;
627 void debug_exit(void)
629 /* Reset console output codepage */
630 SetConsoleOutputCP(old_cp);
633 void debug_init(void)
635 old_cp = GetConsoleOutputCP();
636 /* Set console output codepage to UTF8. Only works
637 * correctly when the console uses a truetype font. */
638 SetConsoleOutputCP(65001);
639 atexit(debug_exit);
641 #else
642 void debug_init(void)
644 /* nothing to be done */
646 #endif
648 void debugf(const char *fmt, ...)
650 va_list ap;
651 va_start( ap, fmt );
652 vfprintf( stderr, fmt, ap );
653 va_end( ap );
656 void ldebugf(const char* file, int line, const char *fmt, ...)
658 va_list ap;
659 va_start( ap, fmt );
660 fprintf( stderr, "%s:%d ", file, line );
661 vfprintf( stderr, fmt, ap );
662 va_end( ap );
665 /* rockbox off_t may be different from system off_t */
666 int sim_ftruncate(int fd, long length)
668 #ifdef WIN32
669 return _chsize(fd, length);
670 #else
671 return ftruncate(fd, length);
672 #endif