fix FS#9119 (crash if random folder list has no entries)
[Rockbox.git] / uisimulator / common / io.c
blob5827508d9703ca900a0a34b35bff3ccff55773da
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 #ifdef __FreeBSD__
29 #include <sys/param.h>
30 #include <sys/mount.h>
31 #elif defined(__APPLE__)
32 #include <sys/param.h>
33 #include <sys/mount.h>
34 #elif !defined(WIN32)
35 #include <sys/vfs.h>
36 #endif
38 #ifdef WIN32
39 #include <windows.h>
40 #endif
42 #ifndef _MSC_VER
43 #include <dirent.h>
44 #include <unistd.h>
45 #else
46 #include "dir-win32.h"
47 #endif
49 #define MAX_PATH 260
50 #define MAX_OPEN_FILES 11
52 #include <fcntl.h>
53 #include <SDL.h>
54 #include <SDL_thread.h>
55 #include "thread.h"
56 #include "kernel.h"
57 #include "debug.h"
58 #include "config.h"
59 #include "ata.h" /* for IF_MV2 et al. */
60 #include "thread-sdl.h"
63 /* Windows (and potentially other OSes) distinguish binary and text files.
64 * Define a dummy for the others. */
65 #ifndef O_BINARY
66 #define O_BINARY 0
67 #endif
69 /* Unicode compatibility for win32 */
70 #if defined __MINGW32__
71 /* Rockbox unicode functions */
72 extern const unsigned char* utf8decode(const unsigned char *utf8,
73 unsigned short *ucs);
74 extern unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8);
76 /* Static buffers for the conversion results. This isn't thread safe,
77 * but it's sufficient for rockbox. */
78 static unsigned char convbuf1[3*MAX_PATH];
79 static unsigned char convbuf2[3*MAX_PATH];
81 static wchar_t* utf8_to_ucs2(const unsigned char *utf8, void *buffer)
83 wchar_t *ucs = buffer;
85 while (*utf8)
86 utf8 = utf8decode(utf8, ucs++);
88 *ucs = 0;
89 return buffer;
91 static unsigned char *ucs2_to_utf8(const wchar_t *ucs, unsigned char *buffer)
93 unsigned char *utf8 = buffer;
95 while (*ucs)
96 utf8 = utf8encode(*ucs++, utf8);
98 *utf8 = 0;
99 return buffer;
102 #define UTF8_TO_OS(a) utf8_to_ucs2(a,convbuf1)
103 #define OS_TO_UTF8(a) ucs2_to_utf8(a,convbuf1)
104 #define DIR_T _WDIR
105 #define DIRENT_T struct _wdirent
106 #define STAT_T struct _stat
107 extern int _wmkdir(const wchar_t*);
108 extern int _wrmdir(const wchar_t*);
109 #define MKDIR(a,b) (_wmkdir)(UTF8_TO_OS(a))
110 #define RMDIR(a) (_wrmdir)(UTF8_TO_OS(a))
111 #define OPENDIR(a) (_wopendir)(UTF8_TO_OS(a))
112 #define READDIR(a) (_wreaddir)(a)
113 #define CLOSEDIR(a) (_wclosedir)(a)
114 #define STAT(a,b) (_wstat)(UTF8_TO_OS(a),b)
115 #define OPEN(a,b,c) (_wopen)(UTF8_TO_OS(a),b,c)
116 #define CLOSE(a) (close)(a)
117 #define REMOVE(a) (_wremove)(UTF8_TO_OS(a))
118 #define RENAME(a,b) (_wrename)(UTF8_TO_OS(a),utf8_to_ucs2(b,convbuf2))
120 #else /* !__MINGW32__ */
122 #define UTF8_TO_OS(a) (a)
123 #define OS_TO_UTF8(a) (a)
124 #define DIR_T DIR
125 #define DIRENT_T struct dirent
126 #define STAT_T struct stat
127 #define MKDIR(a,b) (mkdir)(a,b)
128 #define RMDIR(a) (rmdir)(a)
129 #define OPENDIR(a) (opendir)(a)
130 #define READDIR(a) (readdir)(a)
131 #define CLOSEDIR(a) (closedir)(a)
132 #define STAT(a,b) (stat)(a,b)
133 #define OPEN(a,b,c) (open)(a,b,c)
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 *oldpath, const char *newpath);
144 #endif
147 #define SIMULATOR_DEFAULT_ROOT "archos"
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 static ssize_t io_trigger_and_wait(int cmd)
225 void *mythread = NULL;
226 ssize_t result;
228 if (io.count > IO_YIELD_THRESHOLD ||
229 (io.accum += io.count) >= IO_YIELD_THRESHOLD)
231 /* Allow other rockbox threads to run */
232 io.accum = 0;
233 mythread = thread_sdl_thread_unlock();
236 switch (cmd)
238 case IO_READ:
239 result = read(io.fd, io.buf, io.count);
240 break;
241 case IO_WRITE:
242 result = write(io.fd, io.buf, io.count);
243 break;
246 /* Regain our status as current */
247 if (mythread != NULL)
249 thread_sdl_thread_lock(mythread);
252 return result;
255 static const char *get_sim_rootdir()
257 if (sim_root_dir != NULL)
258 return sim_root_dir;
259 return SIMULATOR_DEFAULT_ROOT;
262 MYDIR *sim_opendir(const char *name)
264 char buffer[MAX_PATH]; /* sufficiently big */
265 DIR_T *dir;
267 #ifndef __PCTOOL__
268 if(name[0] == '/')
270 snprintf(buffer, sizeof(buffer), "%s%s", get_sim_rootdir(), name);
271 dir=(DIR_T *)OPENDIR(buffer);
273 else
274 #endif
275 dir=(DIR_T *)OPENDIR(name);
277 if(dir) {
278 MYDIR *my = (MYDIR *)malloc(sizeof(MYDIR));
279 my->dir = dir;
280 my->name = (char *)strdup(name);
282 return my;
284 /* failed open, return NULL */
285 return (MYDIR *)0;
288 struct sim_dirent *sim_readdir(MYDIR *dir)
290 char buffer[MAX_PATH]; /* sufficiently big */
291 static struct sim_dirent secret;
292 STAT_T s;
293 DIRENT_T *x11 = READDIR(dir->dir);
294 struct tm* tm;
296 if(!x11)
297 return (struct sim_dirent *)0;
299 strcpy((char *)secret.d_name, OS_TO_UTF8(x11->d_name));
301 /* build file name */
302 #ifdef __PCTOOL__
303 snprintf(buffer, sizeof(buffer), "%s/%s", dir->name, secret.d_name);
304 #else
305 snprintf(buffer, sizeof(buffer), "%s/%s/%s",
306 get_sim_rootdir(), dir->name, secret.d_name);
307 #endif
308 STAT(buffer, &s); /* get info */
310 #define ATTR_DIRECTORY 0x10
312 secret.attribute = S_ISDIR(s.st_mode)?ATTR_DIRECTORY:0;
313 secret.size = s.st_size;
315 tm = localtime(&(s.st_mtime));
316 secret.wrtdate = ((tm->tm_year - 80) << 9) |
317 ((tm->tm_mon + 1) << 5) |
318 tm->tm_mday;
319 secret.wrttime = (tm->tm_hour << 11) |
320 (tm->tm_min << 5) |
321 (tm->tm_sec >> 1);
322 return &secret;
325 void sim_closedir(MYDIR *dir)
327 free(dir->name);
328 CLOSEDIR(dir->dir);
330 free(dir);
333 int sim_open(const char *name, int o)
335 char buffer[MAX_PATH]; /* sufficiently big */
336 int opts = rockbox2sim(o);
337 int ret;
339 if (num_openfiles >= MAX_OPEN_FILES)
340 return -2;
342 #ifndef __PCTOOL__
343 if(name[0] == '/')
345 snprintf(buffer, sizeof(buffer), "%s%s", get_sim_rootdir(), name);
347 /* debugf("We open the real file '%s'\n", buffer); */
348 if (num_openfiles < MAX_OPEN_FILES)
350 ret = OPEN(buffer, opts, 0666);
351 if (ret >= 0) num_openfiles++;
352 return ret;
356 fprintf(stderr, "WARNING, bad file name lacks slash: %s\n",
357 name);
358 return -1;
359 #else
360 if (num_openfiles < MAX_OPEN_FILES)
362 ret = OPEN(buffer, opts, 0666);
363 if (ret >= 0) num_openfiles++;
364 return ret;
366 #endif
369 int sim_close(int fd)
371 int ret;
372 ret = CLOSE(fd);
373 if (ret == 0) num_openfiles--;
374 return ret;
377 int sim_creat(const char *name)
379 #ifndef __PCTOOL__
380 char buffer[MAX_PATH]; /* sufficiently big */
381 if(name[0] == '/')
383 snprintf(buffer, sizeof(buffer), "%s%s", get_sim_rootdir(), name);
385 /* debugf("We create the real file '%s'\n", buffer); */
386 return OPEN(buffer, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0666);
388 fprintf(stderr, "WARNING, bad file name lacks slash: %s\n", name);
389 return -1;
390 #else
391 return OPEN(name, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0666);
392 #endif
395 ssize_t sim_read(int fd, void *buf, size_t count)
397 ssize_t result;
399 mutex_lock(&io.sim_mutex);
401 /* Setup parameters */
402 io.fd = fd;
403 io.buf = buf;
404 io.count = count;
406 result = io_trigger_and_wait(IO_READ);
408 mutex_unlock(&io.sim_mutex);
410 return result;
413 ssize_t sim_write(int fd, const void *buf, size_t count)
415 ssize_t result;
417 mutex_lock(&io.sim_mutex);
419 io.fd = fd;
420 io.buf = (void*)buf;
421 io.count = count;
423 result = io_trigger_and_wait(IO_WRITE);
425 mutex_unlock(&io.sim_mutex);
427 return result;
430 int sim_mkdir(const char *name)
432 #ifdef __PCTOOL__
433 return MKDIR(name, 0777);
434 #else
435 char buffer[MAX_PATH]; /* sufficiently big */
437 snprintf(buffer, sizeof(buffer), "%s%s", get_sim_rootdir(), name);
439 /* debugf("We create the real directory '%s'\n", buffer); */
440 return MKDIR(buffer, 0777);
441 #endif
444 int sim_rmdir(const char *name)
446 #ifdef __PCTOOL__
447 return RMDIR(name);
448 #else
449 char buffer[MAX_PATH]; /* sufficiently big */
450 if(name[0] == '/')
452 snprintf(buffer, sizeof(buffer), "%s%s", get_sim_rootdir(), name);
454 /* debugf("We remove the real directory '%s'\n", buffer); */
455 return RMDIR(buffer);
457 return RMDIR(name);
458 #endif
461 int sim_remove(const char *name)
463 #ifdef __PCTOOL__
464 return REMOVE(name);
465 #else
466 char buffer[MAX_PATH]; /* sufficiently big */
468 #ifdef HAVE_DIRCACHE
469 dircache_remove(name);
470 #endif
472 if(name[0] == '/') {
473 snprintf(buffer, sizeof(buffer), "%s%s", get_sim_rootdir(), name);
475 /* debugf("We remove the real file '%s'\n", buffer); */
476 return REMOVE(buffer);
478 return REMOVE(name);
479 #endif
482 int sim_rename(const char *oldpath, const char* newpath)
484 #ifdef __PCTOOL__
485 return RENAME(oldpath, newpath);
486 #else
487 char buffer1[MAX_PATH];
488 char buffer2[MAX_PATH];
490 #ifdef HAVE_DIRCACHE
491 dircache_rename(oldpath, newpath);
492 #endif
494 if(oldpath[0] == '/') {
495 snprintf(buffer1, sizeof(buffer1), "%s%s", get_sim_rootdir(),
496 oldpath);
497 snprintf(buffer2, sizeof(buffer2), "%s%s", get_sim_rootdir(),
498 newpath);
500 /* debugf("We rename the real file '%s' to '%s'\n", buffer1, buffer2); */
501 return RENAME(buffer1, buffer2);
503 return -1;
504 #endif
507 /* rockbox off_t may be different from system off_t */
508 long sim_lseek(int fildes, long offset, int whence)
510 return lseek(fildes, offset, whence);
513 long sim_filesize(int fd)
515 #ifdef WIN32
516 return _filelength(fd);
517 #else
518 struct stat buf;
520 if (!fstat(fd, &buf))
521 return buf.st_size;
522 else
523 return -1;
524 #endif
527 void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
529 #ifdef HAVE_MULTIVOLUME
530 if (volume != 0) {
531 /* debugf("io.c: fat_size(volume=%d); simulator only supports volume 0\n",volume); */
533 if (size) *size = 0;
534 if (free) *free = 0;
535 return;
537 #endif
539 #ifdef WIN32
540 long secperclus, bytespersec, free_clusters, num_clusters;
542 if (GetDiskFreeSpace(NULL, &secperclus, &bytespersec, &free_clusters,
543 &num_clusters)) {
544 if (size)
545 *size = num_clusters * secperclus / 2 * (bytespersec / 512);
546 if (free)
547 *free = free_clusters * secperclus / 2 * (bytespersec / 512);
549 #else
550 struct statfs fs;
552 if (!statfs(".", &fs)) {
553 DEBUGF("statfs: bsize=%d blocks=%ld free=%ld\n",
554 (int)fs.f_bsize, fs.f_blocks, fs.f_bfree);
555 if (size)
556 *size = fs.f_blocks * (fs.f_bsize / 1024);
557 if (free)
558 *free = fs.f_bfree * (fs.f_bsize / 1024);
560 #endif
561 else {
562 if (size)
563 *size = 0;
564 if (free)
565 *free = 0;
569 int sim_fsync(int fd)
571 #ifdef WIN32
572 return _commit(fd);
573 #else
574 return fsync(fd);
575 #endif
578 #ifdef WIN32
579 /* sim-win32 */
580 #define dlopen(_x_, _y_) LoadLibraryW(UTF8_TO_OS(_x_))
581 #define dlsym(_x_, _y_) (void *)GetProcAddress(_x_, _y_)
582 #define dlclose(_x_) FreeLibrary(_x_)
583 #else
584 /* sim-x11 */
585 #include <dlfcn.h>
586 #endif
588 #define TEMP_CODEC_FILE "archos/_temp_codec%d.dll"
590 void *sim_codec_load_ram(char* codecptr, int size, void **pd)
592 void *hdr;
593 char path[MAX_PATH];
594 int fd;
595 int codec_count;
596 #ifdef WIN32
597 char buf[MAX_PATH];
598 #endif
600 *pd = NULL;
602 /* We have to create the dynamic link library file from ram so we
603 can simulate the codec loading. With voice and crossfade,
604 multiple codecs may be loaded at the same time, so we need
605 to find an unused filename */
606 for (codec_count = 0; codec_count < 10; codec_count++)
608 snprintf(path, sizeof(path), TEMP_CODEC_FILE, codec_count);
610 fd = OPEN(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRWXU);
611 if (fd >= 0)
612 break; /* Created a file ok */
614 if (fd < 0)
616 DEBUGF("failed to open for write: %s\n", path);
617 return NULL;
620 if (write(fd, codecptr, size) != size) {
621 DEBUGF("write failed");
622 return NULL;
624 close(fd);
626 /* Now load the library. */
627 *pd = dlopen(path, RTLD_NOW);
628 if (*pd == NULL) {
629 DEBUGF("failed to load %s\n", path);
630 #ifdef WIN32
631 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
632 buf, sizeof buf, NULL);
633 DEBUGF("dlopen(%s): %s\n", path, buf);
634 #else
635 DEBUGF("dlopen(%s): %s\n", path, dlerror());
636 #endif
637 return NULL;
640 hdr = dlsym(*pd, "__header");
641 if (!hdr)
642 hdr = dlsym(*pd, "___header");
644 return hdr; /* maybe NULL if symbol not present */
647 void sim_codec_close(void *pd)
649 dlclose(pd);
652 void *sim_plugin_load(char *plugin, void **pd)
654 void *hdr;
655 char path[MAX_PATH];
656 #ifdef WIN32
657 char buf[MAX_PATH];
658 #endif
660 snprintf(path, sizeof(path), "archos%s", plugin);
662 *pd = NULL;
664 *pd = dlopen(path, RTLD_NOW);
665 if (*pd == NULL) {
666 DEBUGF("failed to load %s\n", plugin);
667 #ifdef WIN32
668 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
669 buf, sizeof(buf), NULL);
670 DEBUGF("dlopen(%s): %s\n", path, buf);
671 #else
672 DEBUGF("dlopen(%s): %s\n", path, dlerror());
673 #endif
674 return NULL;
677 hdr = dlsym(*pd, "__header");
678 if (!hdr)
679 hdr = dlsym(*pd, "___header");
681 return hdr; /* maybe NULL if symbol not present */
684 void sim_plugin_close(void *pd)
686 dlclose(pd);
689 #ifdef WIN32
690 static unsigned old_cp;
692 void debug_exit(void)
694 /* Reset console output codepage */
695 SetConsoleOutputCP(old_cp);
698 void debug_init(void)
700 old_cp = GetConsoleOutputCP();
701 /* Set console output codepage to UTF8. Only works
702 * correctly when the console uses a truetype font. */
703 SetConsoleOutputCP(65001);
704 atexit(debug_exit);
706 #else
707 void debug_init(void)
709 /* nothing to be done */
711 #endif
713 void debugf(const char *fmt, ...)
715 va_list ap;
716 va_start( ap, fmt );
717 vfprintf( stderr, fmt, ap );
718 va_end( ap );
721 void ldebugf(const char* file, int line, const char *fmt, ...)
723 va_list ap;
724 va_start( ap, fmt );
725 fprintf( stderr, "%s:%d ", file, line );
726 vfprintf( stderr, fmt, ap );
727 va_end( ap );
730 /* rockbox off_t may be different from system off_t */
731 int sim_ftruncate(int fd, long length)
733 #ifdef WIN32
734 return _chsize(fd, length);
735 #else
736 return ftruncate(fd, length);
737 #endif