Remove Ipod Classic from list of unsupported targets.
[maemo-rb.git] / uisimulator / common / io.c
blobfdacc590691ef0da02f439db56a719ceb9e42cd9
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 #include <errno.h>
29 #include "config.h"
31 #define HAVE_STATVFS (!defined(WIN32))
32 #define HAVE_LSTAT (!defined(WIN32))
34 #if HAVE_STATVFS
35 #include <sys/statvfs.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 #include <fcntl.h>
50 #ifdef HAVE_SDL_THREADS
51 #include "thread-sdl.h"
52 #else
53 #define sim_thread_unlock() NULL
54 #define sim_thread_lock(a)
55 #endif
56 #include "thread.h"
57 #include "kernel.h"
58 #include "debug.h"
59 #include "ata.h" /* for IF_MV2 et al. */
60 #include "rbpaths.h"
61 #include "load_code.h"
63 /* keep this in sync with file.h! */
64 #undef MAX_PATH /* this avoids problems when building simulator */
65 #define MAX_PATH 260
66 #define MAX_OPEN_FILES 11
68 /* Windows (and potentially other OSes) distinguish binary and text files.
69 * Define a dummy for the others. */
70 #ifndef O_BINARY
71 #define O_BINARY 0
72 #endif
74 /* Unicode compatibility for win32 */
75 #if defined __MINGW32__
76 /* Rockbox unicode functions */
77 extern const unsigned char* utf8decode(const unsigned char *utf8,
78 unsigned short *ucs);
79 extern unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8);
81 /* Static buffers for the conversion results. This isn't thread safe,
82 * but it's sufficient for rockbox. */
83 static unsigned char convbuf1[3*MAX_PATH];
84 static unsigned char convbuf2[3*MAX_PATH];
86 static wchar_t* utf8_to_ucs2(const unsigned char *utf8, void *buffer)
88 wchar_t *ucs = buffer;
90 while (*utf8)
91 utf8 = utf8decode(utf8, ucs++);
93 *ucs = 0;
94 return buffer;
96 static unsigned char *ucs2_to_utf8(const wchar_t *ucs, unsigned char *buffer)
98 unsigned char *utf8 = buffer;
100 while (*ucs)
101 utf8 = utf8encode(*ucs++, utf8);
103 *utf8 = 0;
104 return buffer;
107 #define UTF8_TO_OS(a) utf8_to_ucs2(a,convbuf1)
108 #define OS_TO_UTF8(a) ucs2_to_utf8(a,convbuf1)
109 #define DIR_T _WDIR
110 #define DIRENT_T struct _wdirent
111 #define STAT_T struct _stat
112 extern int _wmkdir(const wchar_t*);
113 extern int _wrmdir(const wchar_t*);
114 #define MKDIR(a,b) (_wmkdir)(UTF8_TO_OS(a))
115 #define RMDIR(a) (_wrmdir)(UTF8_TO_OS(a))
116 #define OPENDIR(a) (_wopendir)(UTF8_TO_OS(a))
117 #define READDIR(a) (_wreaddir)(a)
118 #define CLOSEDIR(a) (_wclosedir)(a)
119 #define STAT(a,b) (_wstat)(UTF8_TO_OS(a),b)
120 /* empty variable parameter list doesn't work for variadic macros,
121 * so pretend the second parameter is variable too */
122 #define OPEN(a,...) (_wopen)(UTF8_TO_OS(a), __VA_ARGS__)
123 #define CLOSE(a) (close)(a)
124 #define REMOVE(a) (_wremove)(UTF8_TO_OS(a))
125 #define RENAME(a,b) (_wrename)(UTF8_TO_OS(a),utf8_to_ucs2(b,convbuf2))
127 #else /* !__MINGW32__ */
129 #define UTF8_TO_OS(a) (a)
130 #define OS_TO_UTF8(a) (a)
131 #define DIR_T DIR
132 #define DIRENT_T struct dirent
133 #define STAT_T struct stat
134 #define MKDIR(a,b) (mkdir)(a,b)
135 #define RMDIR(a) (rmdir)(a)
136 #define OPENDIR(a) (opendir)(a)
137 #define READDIR(a) (readdir)(a)
138 #define CLOSEDIR(a) (closedir)(a)
139 #define STAT(a,b) (stat)(a,b)
140 /* empty variable parameter list doesn't work for variadic macros,
141 * so pretend the second parameter is variable too */
142 #define OPEN(a, ...) (open)(a, __VA_ARGS__)
143 #define CLOSE(x) (close)(x)
144 #define REMOVE(a) (remove)(a)
145 #define RENAME(a,b) (rename)(a,b)
147 #endif /* !__MINGW32__ */
150 #ifdef HAVE_DIRCACHE
151 int dircache_get_entry_id(const char *filename);
152 void dircache_add_file(const char *name, long startcluster);
153 void dircache_remove(const char *name);
154 void dircache_rename(const char *oldname, const char *newname);
155 #endif
158 #define SIMULATOR_DEFAULT_ROOT "simdisk"
159 extern const char *sim_root_dir;
161 static int num_openfiles = 0;
163 /* from dir.h */
164 struct dirinfo {
165 int attribute;
166 long size;
167 unsigned short wrtdate;
168 unsigned short wrttime;
171 struct sim_dirent {
172 unsigned char d_name[MAX_PATH];
173 struct dirinfo info;
174 long startcluster;
177 struct dirstruct {
178 void *dir; /* actually a DIR* dir */
179 char *name;
180 } SIM_DIR;
182 struct mydir {
183 DIR_T *dir;
184 char *name;
187 typedef struct mydir MYDIR;
189 static unsigned int rockbox2sim(int opt)
191 #if 0
192 /* this shouldn't be needed since we use the host's versions */
193 int newopt = O_BINARY;
195 if(opt & 1)
196 newopt |= O_WRONLY;
197 if(opt & 2)
198 newopt |= O_RDWR;
199 if(opt & 4)
200 newopt |= O_CREAT;
201 if(opt & 8)
202 newopt |= O_APPEND;
203 if(opt & 0x10)
204 newopt |= O_TRUNC;
206 return newopt;
207 #else
208 return opt|O_BINARY;
209 #endif
212 /** Simulator I/O engine routines **/
213 #define IO_YIELD_THRESHOLD 512
215 enum io_dir
217 IO_READ,
218 IO_WRITE,
221 struct sim_io
223 struct mutex sim_mutex; /* Rockbox mutex */
224 int cmd; /* The command to perform */
225 int ready; /* I/O ready flag - 1= ready */
226 int fd; /* The file to read/write */
227 void *buf; /* The buffer to read/write */
228 size_t count; /* Number of bytes to read/write */
229 size_t accum; /* Acculated bytes transferred */
232 static struct sim_io io;
234 int ata_init(void)
236 /* Initialize the rockbox kernel objects on a rockbox thread */
237 mutex_init(&io.sim_mutex);
238 io.accum = 0;
239 return 1;
242 int ata_spinup_time(void)
244 return HZ;
247 static ssize_t io_trigger_and_wait(enum io_dir cmd)
249 void *mythread = NULL;
250 ssize_t result;
252 if (io.count > IO_YIELD_THRESHOLD ||
253 (io.accum += io.count) >= IO_YIELD_THRESHOLD)
255 /* Allow other rockbox threads to run */
256 io.accum = 0;
257 mythread = sim_thread_unlock();
260 switch (cmd)
262 case IO_READ:
263 result = read(io.fd, io.buf, io.count);
264 break;
265 case IO_WRITE:
266 result = write(io.fd, io.buf, io.count);
267 break;
268 /* shut up gcc */
269 default:
270 result = -1;
273 /* Regain our status as current */
274 if (mythread != NULL)
276 sim_thread_lock(mythread);
279 return result;
282 #if !defined(__PCTOOL__) && !defined(APPLICATION)
283 static const char *get_sim_pathname(const char *name)
285 static char buffer[MAX_PATH]; /* sufficiently big */
287 if(name[0] == '/')
289 snprintf(buffer, sizeof(buffer), "%s%s",
290 sim_root_dir != NULL ? sim_root_dir : SIMULATOR_DEFAULT_ROOT, name);
291 return buffer;
293 fprintf(stderr, "WARNING, bad file name lacks slash: %s\n", name);
294 return name;
296 #else
297 #define get_sim_pathname(name) name
298 #endif
300 MYDIR *sim_opendir(const char *name)
302 DIR_T *dir;
303 dir = (DIR_T *) OPENDIR(get_sim_pathname(name));
305 if (dir)
307 MYDIR *my = (MYDIR *)malloc(sizeof(MYDIR));
308 my->dir = dir;
309 my->name = (char *)malloc(strlen(name)+1);
310 strcpy(my->name, name);
312 return my;
314 /* failed open, return NULL */
315 return (MYDIR *)0;
318 #if defined(WIN32)
319 static inline struct tm* localtime_r (const time_t *clock, struct tm *result) {
320 if (!clock || !result) return NULL;
321 memcpy(result,localtime(clock),sizeof(*result));
322 return result;
324 #endif
326 struct sim_dirent *sim_readdir(MYDIR *dir)
328 char buffer[MAX_PATH]; /* sufficiently big */
329 static struct sim_dirent secret;
330 STAT_T s;
331 struct tm tm;
332 DIRENT_T *x11;
334 #ifdef EOVERFLOW
335 read_next:
336 #endif
337 x11 = READDIR(dir->dir);
339 if(!x11)
340 return (struct sim_dirent *)0;
342 strcpy((char *)secret.d_name, OS_TO_UTF8(x11->d_name));
344 /* build file name */
345 snprintf(buffer, sizeof(buffer), "%s/%s",
346 get_sim_pathname(dir->name), secret.d_name);
348 if (STAT(buffer, &s)) /* get info */
350 #ifdef EOVERFLOW
351 /* File size larger than 2 GB? */
352 if (errno == EOVERFLOW)
354 DEBUGF("stat() overflow for %s. Skipping\n", buffer);
355 goto read_next;
357 #endif
359 return NULL;
362 #define ATTR_DIRECTORY 0x10
364 secret.info.attribute = 0;
366 if (S_ISDIR(s.st_mode))
367 secret.info.attribute = ATTR_DIRECTORY;
369 secret.info.size = s.st_size;
371 if (localtime_r(&(s.st_mtime), &tm) == NULL)
372 return NULL;
373 secret.info.wrtdate = ((tm.tm_year - 80) << 9) |
374 ((tm.tm_mon + 1) << 5) |
375 tm.tm_mday;
376 secret.info.wrttime = (tm.tm_hour << 11) |
377 (tm.tm_min << 5) |
378 (tm.tm_sec >> 1);
380 #if HAVE_LSTAT
381 #define ATTR_LINK 0x80
382 if (!lstat(buffer, &s) && S_ISLNK(s.st_mode))
384 secret.info.attribute |= ATTR_LINK;
386 #endif
388 return &secret;
391 void sim_closedir(MYDIR *dir)
393 free(dir->name);
394 CLOSEDIR(dir->dir);
396 free(dir);
399 int sim_open(const char *name, int o, ...)
401 int opts = rockbox2sim(o);
402 int ret;
403 if (num_openfiles >= MAX_OPEN_FILES)
404 return -2;
406 if (opts & O_CREAT)
408 va_list ap;
409 va_start(ap, o);
410 mode_t mode = va_arg(ap, unsigned int);
411 ret = OPEN(get_sim_pathname(name), opts, mode);
412 #ifdef HAVE_DIRCACHE
413 if (ret >= 0 && (dircache_get_entry_id(name) < 0))
414 dircache_add_file(name, 0);
415 #endif
416 va_end(ap);
418 else
419 ret = OPEN(get_sim_pathname(name), opts);
421 if (ret >= 0)
422 num_openfiles++;
423 return ret;
426 int sim_close(int fd)
428 int ret;
429 ret = CLOSE(fd);
430 if (ret == 0)
431 num_openfiles--;
432 return ret;
435 int sim_creat(const char *name, mode_t mode)
437 int ret = OPEN(get_sim_pathname(name),
438 O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, mode);
439 #ifdef HAVE_DIRCACHE
440 if (ret >= 0 && (dircache_get_entry_id(name) < 0))
441 dircache_add_file(name, 0);
442 #endif
443 return ret;
446 ssize_t sim_read(int fd, void *buf, size_t count)
448 ssize_t result;
450 mutex_lock(&io.sim_mutex);
452 /* Setup parameters */
453 io.fd = fd;
454 io.buf = buf;
455 io.count = count;
457 result = io_trigger_and_wait(IO_READ);
459 mutex_unlock(&io.sim_mutex);
461 return result;
464 ssize_t sim_write(int fd, const void *buf, size_t count)
466 ssize_t result;
468 mutex_lock(&io.sim_mutex);
470 io.fd = fd;
471 io.buf = (void*)buf;
472 io.count = count;
474 result = io_trigger_and_wait(IO_WRITE);
476 mutex_unlock(&io.sim_mutex);
478 return result;
481 int sim_mkdir(const char *name)
483 return MKDIR(get_sim_pathname(name), 0777);
486 int sim_rmdir(const char *name)
488 return RMDIR(get_sim_pathname(name));
491 int sim_remove(const char *name)
493 int ret = REMOVE(get_sim_pathname(name));
494 #ifdef HAVE_DIRCACHE
495 if (ret >= 0)
496 dircache_remove(name);
497 #endif
498 return ret;
501 int sim_rename(const char *oldname, const char *newname)
503 char sim_old[MAX_PATH];
504 char sim_new[MAX_PATH];
505 #ifdef HAVE_DIRCACHE
506 dircache_rename(oldname, newname);
507 #endif
508 // This is needed as get_sim_pathname() has a static buffer
509 strncpy(sim_old, get_sim_pathname(oldname), MAX_PATH);
510 strncpy(sim_new, get_sim_pathname(newname), MAX_PATH);
511 return RENAME(sim_old, sim_new);
514 /* rockbox off_t may be different from system off_t */
515 long sim_lseek(int fildes, long offset, int whence)
517 return lseek(fildes, offset, whence);
520 long filesize(int fd)
522 #ifdef WIN32
523 return _filelength(fd);
524 #else
525 struct stat buf;
527 if (!fstat(fd, &buf))
528 return buf.st_size;
529 else
530 return -1;
531 #endif
534 void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
536 #ifdef HAVE_MULTIVOLUME
537 if (volume != 0) {
538 /* debugf("io.c: fat_size(volume=%d); simulator only supports volume 0\n",volume); */
540 if (size) *size = 0;
541 if (free) *free = 0;
542 return;
544 #endif
546 #ifdef WIN32
547 long secperclus, bytespersec, free_clusters, num_clusters;
549 if (GetDiskFreeSpace(NULL, &secperclus, &bytespersec, &free_clusters,
550 &num_clusters)) {
551 if (size)
552 *size = num_clusters * secperclus / 2 * (bytespersec / 512);
553 if (free)
554 *free = free_clusters * secperclus / 2 * (bytespersec / 512);
555 } else
556 #elif HAVE_STATVFS
557 struct statvfs vfs;
559 if (!statvfs(".", &vfs)) {
560 DEBUGF("statvfs: frsize=%d blocks=%ld free=%ld\n",
561 (int)vfs.f_frsize, (long)vfs.f_blocks, (long)vfs.f_bfree);
562 if (size)
563 *size = vfs.f_blocks / 2 * (vfs.f_frsize / 512);
564 if (free)
565 *free = vfs.f_bfree / 2 * (vfs.f_frsize / 512);
566 } else
567 #endif
569 if (size)
570 *size = 0;
571 if (free)
572 *free = 0;
576 int sim_fsync(int fd)
578 #ifdef WIN32
579 return _commit(fd);
580 #else
581 return fsync(fd);
582 #endif
585 #ifndef __PCTOOL__
587 #include <SDL_loadso.h>
588 void *lc_open(const char *filename, unsigned char *buf, size_t buf_size)
590 (void)buf;
591 (void)buf_size;
592 void *handle = SDL_LoadObject(get_sim_pathname(filename));
593 if (handle == NULL)
595 DEBUGF("failed to load %s\n", filename);
596 DEBUGF("lc_open(%s): %s\n", filename, SDL_GetError());
598 return handle;
601 void *lc_get_header(void *handle)
603 char *ret = SDL_LoadFunction(handle, "__header");
604 if (ret == NULL)
605 ret = SDL_LoadFunction(handle, "___header");
607 return ret;
610 void lc_close(void *handle)
612 SDL_UnloadObject(handle);
615 void *lc_open_from_mem(void *addr, size_t blob_size)
617 #ifndef SIMULATOR
618 (void)addr;
619 (void)blob_size;
620 /* we don't support loading code from memory on application builds,
621 * it doesn't make sense (since it means writing the blob to disk again and
622 * then falling back to load from disk) and requires the ability to write
623 * to an executable directory */
624 return NULL;
625 #else
626 /* support it in the sim for the sake of simulating */
627 int fd, i;
628 char temp_filename[MAX_PATH];
630 /* We have to create the dynamic link library file from ram so we
631 can simulate the codec loading. With voice and crossfade,
632 multiple codecs may be loaded at the same time, so we need
633 to find an unused filename */
634 for (i = 0; i < 10; i++)
636 snprintf(temp_filename, sizeof(temp_filename),
637 ROCKBOX_DIR "/libtemp_binary_%d.dll", i);
638 fd = open(temp_filename, O_WRONLY|O_CREAT|O_TRUNC, 0700);
639 if (fd >= 0)
640 break; /* Created a file ok */
643 if (fd < 0)
645 DEBUGF("open failed\n");
646 return NULL;
649 if (write(fd, addr, blob_size) < (ssize_t)blob_size)
651 DEBUGF("Write failed\n");
652 close(fd);
653 remove(temp_filename);
654 return NULL;
657 close(fd);
658 return lc_open(temp_filename, NULL, 0);
659 #endif
662 #endif /* __PCTOOL__ */
664 /* rockbox off_t may be different from system off_t */
665 int sim_ftruncate(int fd, long length)
667 #ifdef WIN32
668 return _chsize(fd, length);
669 #else
670 return ftruncate(fd, length);
671 #endif