cddb19c9a8a4c2b051657603ffd5ee8e93157a43
[kugel-rb.git] / uisimulator / common / io.c
blobcddb19c9a8a4c2b051657603ffd5ee8e93157a43
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 #define MAX_PATH 260
44 #define MAX_OPEN_FILES 11
46 #include <fcntl.h>
47 #include <SDL.h>
48 #include <SDL_thread.h>
49 #include "thread.h"
50 #include "kernel.h"
51 #include "debug.h"
52 #include "config.h"
53 #include "ata.h" /* for IF_MV2 et al. */
54 #include "thread-sdl.h"
57 /* Windows (and potentially other OSes) distinguish binary and text files.
58 * Define a dummy for the others. */
59 #ifndef O_BINARY
60 #define O_BINARY 0
61 #endif
63 /* Unicode compatibility for win32 */
64 #if defined __MINGW32__
65 /* Rockbox unicode functions */
66 extern const unsigned char* utf8decode(const unsigned char *utf8,
67 unsigned short *ucs);
68 extern unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8);
70 /* Static buffers for the conversion results. This isn't thread safe,
71 * but it's sufficient for rockbox. */
72 static unsigned char convbuf1[3*MAX_PATH];
73 static unsigned char convbuf2[3*MAX_PATH];
75 static wchar_t* utf8_to_ucs2(const unsigned char *utf8, void *buffer)
77 wchar_t *ucs = buffer;
79 while (*utf8)
80 utf8 = utf8decode(utf8, ucs++);
82 *ucs = 0;
83 return buffer;
85 static unsigned char *ucs2_to_utf8(const wchar_t *ucs, unsigned char *buffer)
87 unsigned char *utf8 = buffer;
89 while (*ucs)
90 utf8 = utf8encode(*ucs++, utf8);
92 *utf8 = 0;
93 return buffer;
96 #define UTF8_TO_OS(a) utf8_to_ucs2(a,convbuf1)
97 #define OS_TO_UTF8(a) ucs2_to_utf8(a,convbuf1)
98 #define DIR_T _WDIR
99 #define DIRENT_T struct _wdirent
100 #define STAT_T struct _stat
101 extern int _wmkdir(const wchar_t*);
102 extern int _wrmdir(const wchar_t*);
103 #define MKDIR(a,b) (_wmkdir)(UTF8_TO_OS(a))
104 #define RMDIR(a) (_wrmdir)(UTF8_TO_OS(a))
105 #define OPENDIR(a) (_wopendir)(UTF8_TO_OS(a))
106 #define READDIR(a) (_wreaddir)(a)
107 #define CLOSEDIR(a) (_wclosedir)(a)
108 #define STAT(a,b) (_wstat)(UTF8_TO_OS(a),b)
109 /* empty variable parameter list doesn't work for variadic macros,
110 * so pretend the second parameter is variable too */
111 #define OPEN(a,...) (_wopen)(UTF8_TO_OS(a), __VA_ARGS__)
112 #define CLOSE(a) (close)(a)
113 #define REMOVE(a) (_wremove)(UTF8_TO_OS(a))
114 #define RENAME(a,b) (_wrename)(UTF8_TO_OS(a),utf8_to_ucs2(b,convbuf2))
116 #else /* !__MINGW32__ */
118 #define UTF8_TO_OS(a) (a)
119 #define OS_TO_UTF8(a) (a)
120 #define DIR_T DIR
121 #define DIRENT_T struct dirent
122 #define STAT_T struct stat
123 #define MKDIR(a,b) (mkdir)(a,b)
124 #define RMDIR(a) (rmdir)(a)
125 #define OPENDIR(a) (opendir)(a)
126 #define READDIR(a) (readdir)(a)
127 #define CLOSEDIR(a) (closedir)(a)
128 #define STAT(a,b) (stat)(a,b)
129 /* empty variable parameter list doesn't work for variadic macros,
130 * so pretend the second parameter is variable too */
131 #define OPEN(a, ...) (open)(a, __VA_ARGS__)
132 #define CLOSE(x) (close)(x)
133 #define REMOVE(a) (remove)(a)
134 #define RENAME(a,b) (rename)(a,b)
136 #endif /* !__MINGW32__ */
139 #ifdef HAVE_DIRCACHE
140 void dircache_remove(const char *name);
141 void dircache_rename(const char *oldname, const char *newname);
142 #endif
145 #define SIMULATOR_DEFAULT_ROOT "simdisk"
146 extern const char *sim_root_dir;
148 static int num_openfiles = 0;
150 struct sim_dirent {
151 unsigned char d_name[MAX_PATH];
152 int attribute;
153 long size;
154 long startcluster;
155 unsigned short wrtdate; /* Last write date */
156 unsigned short wrttime; /* Last write time */
159 struct dirstruct {
160 void *dir; /* actually a DIR* dir */
161 char *name;
162 } SIM_DIR;
164 struct mydir {
165 DIR_T *dir;
166 char *name;
169 typedef struct mydir MYDIR;
171 #if 1 /* maybe this needs disabling for MSVC... */
172 static unsigned int rockbox2sim(int opt)
174 int newopt = O_BINARY;
176 if(opt & 1)
177 newopt |= O_WRONLY;
178 if(opt & 2)
179 newopt |= O_RDWR;
180 if(opt & 4)
181 newopt |= O_CREAT;
182 if(opt & 8)
183 newopt |= O_APPEND;
184 if(opt & 0x10)
185 newopt |= O_TRUNC;
187 return newopt;
189 #endif
191 /** Simulator I/O engine routines **/
192 #define IO_YIELD_THRESHOLD 512
194 enum
196 IO_READ,
197 IO_WRITE,
200 struct sim_io
202 struct mutex sim_mutex; /* Rockbox mutex */
203 int cmd; /* The command to perform */
204 int ready; /* I/O ready flag - 1= ready */
205 int fd; /* The file to read/write */
206 void *buf; /* The buffer to read/write */
207 size_t count; /* Number of bytes to read/write */
208 size_t accum; /* Acculated bytes transferred */
211 static struct sim_io io;
213 int ata_init(void)
215 /* Initialize the rockbox kernel objects on a rockbox thread */
216 mutex_init(&io.sim_mutex);
217 io.accum = 0;
218 return 1;
221 int ata_spinup_time(void)
223 return HZ;
226 static ssize_t io_trigger_and_wait(int cmd)
228 void *mythread = NULL;
229 ssize_t result;
231 if (io.count > IO_YIELD_THRESHOLD ||
232 (io.accum += io.count) >= IO_YIELD_THRESHOLD)
234 /* Allow other rockbox threads to run */
235 io.accum = 0;
236 mythread = thread_sdl_thread_unlock();
239 switch (cmd)
241 case IO_READ:
242 result = read(io.fd, io.buf, io.count);
243 break;
244 case IO_WRITE:
245 result = write(io.fd, io.buf, io.count);
246 break;
249 /* Regain our status as current */
250 if (mythread != NULL)
252 thread_sdl_thread_lock(mythread);
255 return result;
258 #ifndef __PCTOOL__
259 static const char *get_sim_pathname(const char *name)
261 static char buffer[MAX_PATH]; /* sufficiently big */
263 if(name[0] == '/')
265 snprintf(buffer, sizeof(buffer), "%s%s",
266 sim_root_dir != NULL ? sim_root_dir : SIMULATOR_DEFAULT_ROOT, name);
267 return buffer;
269 fprintf(stderr, "WARNING, bad file name lacks slash: %s\n", name);
270 return name;
272 #else
273 #define get_sim_pathname(name) name
274 #endif
276 MYDIR *sim_opendir(const char *name)
278 DIR_T *dir;
280 dir = (DIR_T *) OPENDIR(get_sim_pathname(name));
282 if (dir)
284 MYDIR *my = (MYDIR *)malloc(sizeof(MYDIR));
285 my->dir = dir;
286 my->name = (char *)malloc(strlen(name)+1);
287 strcpy(my->name, name);
289 return my;
291 /* failed open, return NULL */
292 return (MYDIR *)0;
295 struct sim_dirent *sim_readdir(MYDIR *dir)
297 char buffer[MAX_PATH]; /* sufficiently big */
298 static struct sim_dirent secret;
299 STAT_T s;
300 DIRENT_T *x11 = READDIR(dir->dir);
301 struct tm* tm;
303 if(!x11)
304 return (struct sim_dirent *)0;
306 strcpy((char *)secret.d_name, OS_TO_UTF8(x11->d_name));
308 /* build file name */
309 snprintf(buffer, sizeof(buffer), "%s/%s",
310 get_sim_pathname(dir->name), secret.d_name);
311 STAT(buffer, &s); /* get info */
313 #define ATTR_DIRECTORY 0x10
315 secret.attribute = S_ISDIR(s.st_mode)?ATTR_DIRECTORY:0;
316 secret.size = s.st_size;
318 tm = localtime(&(s.st_mtime));
319 secret.wrtdate = ((tm->tm_year - 80) << 9) |
320 ((tm->tm_mon + 1) << 5) |
321 tm->tm_mday;
322 secret.wrttime = (tm->tm_hour << 11) |
323 (tm->tm_min << 5) |
324 (tm->tm_sec >> 1);
325 return &secret;
328 void sim_closedir(MYDIR *dir)
330 free(dir->name);
331 CLOSEDIR(dir->dir);
333 free(dir);
336 int sim_open(const char *name, int o, ...)
338 int opts = rockbox2sim(o);
339 int ret;
340 if (num_openfiles >= MAX_OPEN_FILES)
341 return -2;
343 if (opts & O_CREAT)
345 va_list ap;
346 va_start(ap, o);
347 mode_t mode = va_arg(ap, unsigned int);
348 ret = OPEN(get_sim_pathname(name), opts, mode);
349 va_end(ap);
351 else
352 ret = OPEN(get_sim_pathname(name), opts);
354 if (ret >= 0)
355 num_openfiles++;
356 return ret;
359 int sim_close(int fd)
361 int ret;
362 ret = CLOSE(fd);
363 if (ret == 0)
364 num_openfiles--;
365 return ret;
368 int sim_creat(const char *name, mode_t mode)
370 return OPEN(get_sim_pathname(name), O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, mode);
373 ssize_t sim_read(int fd, void *buf, size_t count)
375 ssize_t result;
377 mutex_lock(&io.sim_mutex);
379 /* Setup parameters */
380 io.fd = fd;
381 io.buf = buf;
382 io.count = count;
384 result = io_trigger_and_wait(IO_READ);
386 mutex_unlock(&io.sim_mutex);
388 return result;
391 ssize_t sim_write(int fd, const void *buf, size_t count)
393 ssize_t result;
395 mutex_lock(&io.sim_mutex);
397 io.fd = fd;
398 io.buf = (void*)buf;
399 io.count = count;
401 result = io_trigger_and_wait(IO_WRITE);
403 mutex_unlock(&io.sim_mutex);
405 return result;
408 int sim_mkdir(const char *name)
410 return MKDIR(get_sim_pathname(name), 0777);
413 int sim_rmdir(const char *name)
415 return RMDIR(get_sim_pathname(name));
418 int sim_remove(const char *name)
420 #ifdef HAVE_DIRCACHE
421 dircache_remove(name);
422 #endif
423 return REMOVE(get_sim_pathname(name));
426 int sim_rename(const char *oldname, const char *newname)
428 char sim_old[MAX_PATH];
429 char sim_new[MAX_PATH];
430 #ifdef HAVE_DIRCACHE
431 dircache_rename(oldname, newname);
432 #endif
433 // This is needed as get_sim_pathname() has a static buffer
434 strncpy(sim_old, get_sim_pathname(oldname), MAX_PATH);
435 strncpy(sim_new, get_sim_pathname(newname), MAX_PATH);
436 return RENAME(sim_old, sim_new);
439 /* rockbox off_t may be different from system off_t */
440 long sim_lseek(int fildes, long offset, int whence)
442 return lseek(fildes, offset, whence);
445 long sim_filesize(int fd)
447 #ifdef WIN32
448 return _filelength(fd);
449 #else
450 struct stat buf;
452 if (!fstat(fd, &buf))
453 return buf.st_size;
454 else
455 return -1;
456 #endif
459 void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
461 #ifdef HAVE_MULTIVOLUME
462 if (volume != 0) {
463 /* debugf("io.c: fat_size(volume=%d); simulator only supports volume 0\n",volume); */
465 if (size) *size = 0;
466 if (free) *free = 0;
467 return;
469 #endif
471 #ifdef WIN32
472 long secperclus, bytespersec, free_clusters, num_clusters;
474 if (GetDiskFreeSpace(NULL, &secperclus, &bytespersec, &free_clusters,
475 &num_clusters)) {
476 if (size)
477 *size = num_clusters * secperclus / 2 * (bytespersec / 512);
478 if (free)
479 *free = free_clusters * secperclus / 2 * (bytespersec / 512);
481 #else
482 struct statvfs vfs;
484 if (!statvfs(".", &vfs)) {
485 DEBUGF("statvfs: frsize=%d blocks=%ld free=%ld\n",
486 (int)vfs.f_frsize, (long)vfs.f_blocks, (long)vfs.f_bfree);
487 if (size)
488 *size = vfs.f_blocks / 2 * (vfs.f_frsize / 512);
489 if (free)
490 *free = vfs.f_bfree / 2 * (vfs.f_frsize / 512);
492 #endif
493 else {
494 if (size)
495 *size = 0;
496 if (free)
497 *free = 0;
501 int sim_fsync(int fd)
503 #ifdef WIN32
504 return _commit(fd);
505 #else
506 return fsync(fd);
507 #endif
510 #ifdef WIN32
511 /* sim-win32 */
512 #define dlopen(_x_, _y_) LoadLibraryW(UTF8_TO_OS(_x_))
513 #define dlsym(_x_, _y_) (void *)GetProcAddress(_x_, _y_)
514 #define dlclose(_x_) FreeLibrary(_x_)
515 #else
516 /* sim-x11 */
517 #include <dlfcn.h>
518 #endif
520 void *sim_codec_load_ram(char* codecptr, int size, void **pd)
522 void *hdr;
523 char name[MAX_PATH];
524 char path[MAX_PATH];
525 int fd;
526 int codec_count;
527 #ifdef WIN32
528 char buf[MAX_PATH];
529 #endif
531 *pd = NULL;
533 /* We have to create the dynamic link library file from ram so we
534 can simulate the codec loading. With voice and crossfade,
535 multiple codecs may be loaded at the same time, so we need
536 to find an unused filename */
537 for (codec_count = 0; codec_count < 10; codec_count++)
539 snprintf(name, sizeof(name), "/_temp_codec%d.dll", codec_count);
540 snprintf(path, sizeof(path), "%s", get_sim_pathname(name));
541 fd = OPEN(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRWXU);
542 if (fd >= 0)
543 break; /* Created a file ok */
545 if (fd < 0)
547 DEBUGF("failed to open for write: %s\n", path);
548 return NULL;
551 if (write(fd, codecptr, size) != size)
553 DEBUGF("write failed");
554 return NULL;
556 close(fd);
558 /* Now load the library. */
559 *pd = dlopen(path, RTLD_NOW);
560 if (*pd == NULL)
562 DEBUGF("failed to load %s\n", path);
563 #ifdef WIN32
564 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
565 buf, sizeof buf, NULL);
566 DEBUGF("dlopen(%s): %s\n", path, buf);
567 #else
568 DEBUGF("dlopen(%s): %s\n", path, dlerror());
569 #endif
570 return NULL;
573 hdr = dlsym(*pd, "__header");
574 if (!hdr)
575 hdr = dlsym(*pd, "___header");
577 return hdr; /* maybe NULL if symbol not present */
580 void sim_codec_close(void *pd)
582 dlclose(pd);
585 void *sim_plugin_load(char *plugin, void **pd)
587 void *hdr;
588 char path[MAX_PATH];
589 #ifdef WIN32
590 char buf[MAX_PATH];
591 #endif
593 snprintf(path, sizeof(path), "%s", get_sim_pathname(plugin));
595 *pd = NULL;
597 *pd = dlopen(path, RTLD_NOW);
598 if (*pd == NULL) {
599 DEBUGF("failed to load %s\n", plugin);
600 #ifdef WIN32
601 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
602 buf, sizeof(buf), NULL);
603 DEBUGF("dlopen(%s): %s\n", path, buf);
604 #else
605 DEBUGF("dlopen(%s): %s\n", path, dlerror());
606 #endif
607 return NULL;
610 hdr = dlsym(*pd, "__header");
611 if (!hdr)
612 hdr = dlsym(*pd, "___header");
614 return hdr; /* maybe NULL if symbol not present */
617 void sim_plugin_close(void *pd)
619 dlclose(pd);
622 #ifdef WIN32
623 static unsigned old_cp;
625 void debug_exit(void)
627 /* Reset console output codepage */
628 SetConsoleOutputCP(old_cp);
631 void debug_init(void)
633 old_cp = GetConsoleOutputCP();
634 /* Set console output codepage to UTF8. Only works
635 * correctly when the console uses a truetype font. */
636 SetConsoleOutputCP(65001);
637 atexit(debug_exit);
639 #else
640 void debug_init(void)
642 /* nothing to be done */
644 #endif
646 void debugf(const char *fmt, ...)
648 va_list ap;
649 va_start( ap, fmt );
650 vfprintf( stderr, fmt, ap );
651 va_end( ap );
654 void ldebugf(const char* file, int line, const char *fmt, ...)
656 va_list ap;
657 va_start( ap, fmt );
658 fprintf( stderr, "%s:%d ", file, line );
659 vfprintf( stderr, fmt, ap );
660 va_end( ap );
663 /* rockbox off_t may be different from system off_t */
664 int sim_ftruncate(int fd, long length)
666 #ifdef WIN32
667 return _chsize(fd, length);
668 #else
669 return ftruncate(fd, length);
670 #endif