From 1d130feff7d2c8dfd237a35457971f39c105cb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Wed, 23 Aug 2006 09:12:05 +0000 Subject: [PATCH] copied dosfs R5 version to tests git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18582 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/file_systems/dos/r5/Jamfile | 16 + .../kernel/file_systems/dos/r5/LICENSE | 31 + .../add-ons/kernel/file_systems/dos/r5/attr.c | 265 +++ .../add-ons/kernel/file_systems/dos/r5/attr.h | 22 + .../kernel/file_systems/dos/r5/cache.h | 108 ++ .../add-ons/kernel/file_systems/dos/r5/dir.c | 1236 +++++++++++++ .../add-ons/kernel/file_systems/dos/r5/dir.h | 36 + .../kernel/file_systems/dos/r5/dlist.c | 162 ++ .../kernel/file_systems/dos/r5/dlist.h | 16 + .../kernel/file_systems/dos/r5/dosfs.c | 1080 +++++++++++ .../kernel/file_systems/dos/r5/dosfs.h | 211 +++ .../kernel/file_systems/dos/r5/encodings.cpp | 1600 +++++++++++++++++ .../kernel/file_systems/dos/r5/encodings.h | 31 + .../add-ons/kernel/file_systems/dos/r5/fat.c | 644 +++++++ .../add-ons/kernel/file_systems/dos/r5/fat.h | 31 + .../add-ons/kernel/file_systems/dos/r5/file.c | 1406 +++++++++++++++ .../add-ons/kernel/file_systems/dos/r5/file.h | 30 + .../kernel/file_systems/dos/r5/fsproto.h | 245 +++ .../add-ons/kernel/file_systems/dos/r5/iter.c | 310 ++++ .../add-ons/kernel/file_systems/dos/r5/iter.h | 47 + .../kernel/file_systems/dos/r5/kalloc.h | 23 + .../add-ons/kernel/file_systems/dos/r5/lock.h | 46 + .../kernel/file_systems/dos/r5/mime_table.c | 105 ++ .../kernel/file_systems/dos/r5/mime_table.h | 16 + .../kernel/file_systems/dos/r5/rtc_info.h | 23 + .../kernel/file_systems/dos/r5/settings/dos | 17 + .../add-ons/kernel/file_systems/dos/r5/util.c | 149 ++ .../add-ons/kernel/file_systems/dos/r5/util.h | 43 + .../kernel/file_systems/dos/r5/vcache.c | 443 +++++ .../kernel/file_systems/dos/r5/vcache.h | 30 + .../kernel/file_systems/dos/r5/version.c | 6 + 31 files changed, 8428 insertions(+) create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/Jamfile create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/LICENSE create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/attr.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/attr.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/cache.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/dir.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/dir.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/dlist.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/dlist.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/encodings.cpp create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/encodings.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/fat.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/fat.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/file.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/file.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/fsproto.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/iter.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/iter.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/kalloc.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/lock.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/rtc_info.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/settings/dos create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/util.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/util.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/vcache.c create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/vcache.h create mode 100644 src/tests/add-ons/kernel/file_systems/dos/r5/version.c diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/Jamfile b/src/tests/add-ons/kernel/file_systems/dos/r5/Jamfile new file mode 100644 index 0000000000..2939121ed0 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src add-ons kernel file_systems dos ; + +KernelAddon dos : kernel file_systems : + attr.c + dir.c + dlist.c + dosfs.c + encodings.cpp + fat.c + file.c + iter.c + mime_table.c + util.c + vcache.c + version.c +; diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/LICENSE b/src/tests/add-ons/kernel/file_systems/dos/r5/LICENSE new file mode 100644 index 0000000000..a32b62b750 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/LICENSE @@ -0,0 +1,31 @@ +---------------------- +Be Sample Code License +---------------------- + +Copyright 1991-2001, Be Incorporated. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/attr.c b/src/tests/add-ons/kernel/file_systems/dos/r5/attr.c new file mode 100644 index 0000000000..f394910946 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/attr.c @@ -0,0 +1,265 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* attr.c + * handles mime type information for dosfs + * gets/sets mime information in vnode + */ + + +#define MIME_STRING_TYPE 'MIMS' + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "dosfs.h" +#include "attr.h" +#include "mime_table.h" + +#define DPRINTF(a,b) if (debug_attr > (a)) dprintf b + +status_t set_mime_type(vnode *node, const char *filename) +{ + struct ext_mime *p; + int32 namelen, ext_len; + + DPRINTF(0, ("get_mime_type of (%s)\n", filename)); + + node->mime = NULL; + + namelen = strlen(filename); + + for (p=mimes;p->extension;p++) { + ext_len = strlen(p->extension); + + if (namelen <= ext_len) + continue; + + if (filename[namelen-ext_len-1] != '.') + continue; + + if (!strcasecmp(filename + namelen - ext_len, p->extension)) + break; + } + + node->mime = p->mime; + + return B_OK; +} + +int dosfs_open_attrdir(void *_vol, void *_node, void **_cookie) +{ + nspace *vol = (nspace *)_vol; + + TOUCH(_node); + + DPRINTF(0, ("dosfs_open_attrdir called\n")); + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_open_attrdir")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + if ((*_cookie = malloc(sizeof(uint32))) == NULL) { + UNLOCK_VOL(vol); + return ENOMEM; + } + *(int32 *)(*_cookie) = 0; + + UNLOCK_VOL(vol); + + return 0; +} + +int dosfs_close_attrdir(void *_vol, void *_node, void *_cookie) +{ + nspace *vol = (nspace *)_vol; + + TOUCH(_node); + + DPRINTF(0, ("dosfs_close_attrdir called\n")); + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_open_attrdir")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + *(int32 *)_cookie = 1; + + UNLOCK_VOL(vol); + + return 0; +} + +int dosfs_free_attrcookie(void *_vol, void *_node, void *_cookie) +{ + TOUCH(_vol); TOUCH(_node); + + DPRINTF(0, ("dosfs_free_attrcookie called\n")); + + if (_cookie == NULL) { + dprintf("error: dosfs_free_attrcookie called with null cookie\n"); + return EINVAL; + } + + *(int32 *)_cookie = 0x87654321; + free(_cookie); + + return 0; +} + +int dosfs_rewind_attrdir(void *_vol, void *_node, void *_cookie) +{ + TOUCH(_vol); TOUCH(_node); + + DPRINTF(0, ("dosfs_rewind_attrdir called\n")); + + if (_cookie == NULL) { + dprintf("error: dosfs_rewind_attrcookie called with null cookie\n"); + return EINVAL; + } + + *(uint32 *)_cookie = 0; + return 0; +} + +int dosfs_read_attrdir(void *_vol, void *_node, void *_cookie, long *num, + struct dirent *entry, size_t bufsize) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + int32 *cookie = (int32 *)_cookie; + + TOUCH(bufsize); + + DPRINTF(0, ("dosfs_read_attrdir called\n")); + + *num = 0; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_read_attrdir") || + check_vnode_magic(node, "dosfs_read_attrdir")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + if ((*cookie == 0) && (node->mime)) { + *num = 1; + + entry->d_ino = node->vnid; + entry->d_dev = vol->id; + entry->d_reclen = 10; + strcpy(entry->d_name, "BEOS:TYPE"); + } + + *cookie = 1; + + UNLOCK_VOL(vol); + + return 0; +} + +int dosfs_stat_attr(void *_vol, void *_node, const char *name, struct attr_info *buf) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + + DPRINTF(0, ("dosfs_stat_attr (%s)\n", name)); + + if (strcmp(name, "BEOS:TYPE")) + return ENOENT; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_read_attr") || + check_vnode_magic(node, "dosfs_read_attr")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + if (node->mime == NULL) { + UNLOCK_VOL(vol); + return ENOENT; + } + + buf->type = MIME_STRING_TYPE; + buf->size = strlen(node->mime) + 1; + + UNLOCK_VOL(vol); + return 0; +} + +int dosfs_read_attr(void *_vol, void *_node, const char *name, int type, void *buf, + size_t *len, off_t pos) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + + DPRINTF(0, ("dosfs_read_attr (%s)\n", name)); + + if (strcmp(name, "BEOS:TYPE")) + return ENOENT; + + if (type != MIME_STRING_TYPE) + return ENOENT; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_read_attr") || + check_vnode_magic(node, "dosfs_read_attr")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + if (node->mime == NULL) { + UNLOCK_VOL(vol); + return ENOENT; + } + + if ((pos < 0) || (pos > strlen(node->mime))) { + UNLOCK_VOL(vol); + return EINVAL; + } + + strncpy(buf, node->mime + pos, *len - 1); + ((char *)buf)[*len - 1] = 0; + *len = strlen(buf) + 1; + + UNLOCK_VOL(vol); + return 0; +} + +// suck up application attempts to set mime types; this hides an unsightly +// error message printed out by zip +int dosfs_write_attr(void *_vol, void *_node, const char *name, int type, + const void *buf, size_t *len, off_t pos) +{ + TOUCH(_vol); TOUCH(_node); TOUCH(name); TOUCH(type); TOUCH(buf); + TOUCH(len); TOUCH(pos); + + DPRINTF(0, ("dosfs_write_attr (%s)\n", name)); + + *len = 0; + + if (strcmp(name, "BEOS:TYPE")) + return ENOSYS; + + if (type != MIME_STRING_TYPE) + return ENOSYS; + + return 0; +} diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/attr.h b/src/tests/add-ons/kernel/file_systems/dos/r5/attr.h new file mode 100644 index 0000000000..6f9c60dcb3 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/attr.h @@ -0,0 +1,22 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_ATTR_H_ +#define _DOSFS_ATTR_H_ + +status_t set_mime_type(vnode *node, const char *filename); + +int dosfs_open_attrdir(void *_vol, void *_node, void **_cookie); +int dosfs_close_attrdir(void *_vol, void *_node, void *_cookie); +int dosfs_free_attrcookie(void *_vol, void *_node, void *_cookie); +int dosfs_rewind_attrdir(void *_vol, void *_node, void *_cookie); +int dosfs_read_attrdir(void *_vol, void *_node, void *_cookie, long *num, + struct dirent *buf, size_t bufsize); +int dosfs_stat_attr(void *_vol, void *_node, const char *name, struct attr_info *buf); +int dosfs_read_attr(void *_vol, void *_node, const char *name, int type, void *buf, + size_t *len, off_t pos); +int dosfs_write_attr(void *_vol, void *_node, const char *name, int type, + const void *buf, size_t *len, off_t pos); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/cache.h b/src/tests/add-ons/kernel/file_systems/dos/r5/cache.h new file mode 100644 index 0000000000..19e5c9f6f5 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/cache.h @@ -0,0 +1,108 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _CACHE_H_ +#define _CACHE_H_ + +#include + +typedef struct hash_ent { + int dev; + off_t bnum; + off_t hash_val; + void *data; + struct hash_ent *next; +} hash_ent; + + +typedef struct hash_table { + hash_ent **table; + int max; + int mask; /* == max - 1 */ + int num_elements; +} hash_table; + + +#define HT_DEFAULT_MAX 128 + + +typedef struct cache_ent { + int dev; + off_t block_num; + int bsize; + volatile int flags; + + void *data; + void *clone; /* copy of data by set_block_info() */ + int lock; + + void (*func)(off_t bnum, size_t num_blocks, void *arg); + off_t logged_bnum; + void *arg; + + struct cache_ent *next, /* points toward mru end of list */ + *prev; /* points toward lru end of list */ + +} cache_ent; + +#define CE_NORMAL 0x0000 /* a nice clean pristine page */ +#define CE_DIRTY 0x0002 /* needs to be written to disk */ +#define CE_BUSY 0x0004 /* this block has i/o happening, don't touch it */ + + +typedef struct cache_ent_list { + cache_ent *lru; /* tail of the list */ + cache_ent *mru; /* head of the list */ +} cache_ent_list; + + +typedef struct block_cache { + lock lock; + int flags; + int cur_blocks; + int max_blocks; + hash_table ht; + + cache_ent_list normal, /* list of "normal" blocks (clean & dirty) */ + locked; /* list of clean and locked blocks */ +} block_cache; + +#if 0 /* XXXdbg -- need to deal with write through caches */ +#define DC_WRITE_THROUGH 0x0001 /* cache is write-through (for floppies) */ +#endif + +#define ALLOW_WRITES 1 +#define NO_WRITES 0 + +extern _IMPEXP_KERNEL int init_block_cache(int max_blocks, int flags); +extern _IMPEXP_KERNEL void shutdown_block_cache(void); + +extern _IMPEXP_KERNEL void force_cache_flush(int dev, int prefer_log_blocks); +extern _IMPEXP_KERNEL int flush_blocks(int dev, off_t bnum, int nblocks); +extern _IMPEXP_KERNEL int flush_device(int dev, int warn_locked); + +extern _IMPEXP_KERNEL int init_cache_for_device(int fd, off_t max_blocks); +extern _IMPEXP_KERNEL int remove_cached_device_blocks(int dev, int allow_write); + +extern _IMPEXP_KERNEL void *get_block(int dev, off_t bnum, int bsize); +extern _IMPEXP_KERNEL void *get_empty_block(int dev, off_t bnum, int bsize); +extern _IMPEXP_KERNEL int release_block(int dev, off_t bnum); +extern _IMPEXP_KERNEL int mark_blocks_dirty(int dev, off_t bnum, int nblocks); + + +extern _IMPEXP_KERNEL int cached_read(int dev, off_t bnum, void *data, off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int cached_write(int dev, off_t bnum, const void *data, + off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int cached_write_locked(int dev, off_t bnum, const void *data, + off_t num_blocks, int bsize); +extern _IMPEXP_KERNEL int set_blocks_info(int dev, off_t *blocks, int nblocks, + void (*func)(off_t bnum, size_t nblocks, void *arg), + void *arg); + + +extern _IMPEXP_KERNEL size_t read_phys_blocks (int fd, off_t bnum, void *data, uint num_blocks, int bsize); +extern _IMPEXP_KERNEL size_t write_phys_blocks(int fd, off_t bnum, void *data, uint num_blocks, int bsize); + +#endif /* _CACHE_H_ */ diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/dir.c b/src/tests/add-ons/kernel/file_systems/dos/r5/dir.c new file mode 100644 index 0000000000..875ce35be5 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/dir.c @@ -0,0 +1,1236 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "iter.h" +#include "dosfs.h" +#include "attr.h" +#include "dir.h" +#include "dlist.h" +#include "fat.h" +#include "util.h" +#include "vcache.h" + +#include "encodings.h" + +#define DPRINTF(a,b) if (debug_dir > (a)) dprintf b + +// used here and in encodings.cpp +const char acceptable[]="!#$%&'()-0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`{}~"; +const char illegal[] = "\\/:*?\"<>|"; + +#define DIRCOOKIE_MAGIC 'AiC ' + +typedef struct dircookie +{ + uint32 magic; + uint32 current_index; +} dircookie; + +static CHECK_MAGIC(dircookie,struct dircookie, DIRCOOKIE_MAGIC) +static status_t findfile(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node, bool check_case, + bool check_dups, bool *dups_exist); + +// private structure for returning data from _next_dirent_() +struct _dirent_info_ { + uint32 sindex; + uint32 eindex; + uint32 mode; + uint32 cluster; + uint32 size; + uint32 time; +}; + +// scans dir for the next entry, using the state stored in a struct diri. +static status_t _next_dirent_(struct diri *iter, struct _dirent_info_ *oinfo, + char *filename, int len) +{ + uint8 *buffer, hash = 0; /* quiet warning */ + uchar uni[1024]; + uint16 *puni; + uint32 i; + + // lfn state + uint32 start_index = 0xffff, filename_len = 0 /* quiet warning */, + lfn_count = 0 /* quiet warning */; + + if (check_diri_magic(iter, "_next_dirent_")) return EINVAL; + + if (iter->current_block == NULL) + return ENOENT; + + if (len < 15) { + DPRINTF(0, ("_next_dirent_: len too short (%x)\n", len)); + return ENOMEM; + } + + buffer = iter->current_block + ((iter->current_index) % (iter->csi.vol->bytes_per_sector / 0x20)) * 0x20; + + for (;buffer != NULL;buffer = diri_next_entry(iter)) { + DPRINTF(2, ("_next_dirent_: %lx/%lx/%lx\n", iter->csi.cluster, iter->csi.sector, iter->current_index)); + if (buffer[0] == 0) { // quit if at end of table + if (start_index != 0xffff) { + dprintf("lfn entry (%s) with no alias\n", filename); + } + return ENOENT; + } + + if (buffer[0] == 0xe5) { // skip erased entries + if (start_index != 0xffff) { + dprintf("lfn entry (%s) with intervening erased entries\n", filename); + start_index = 0xffff; + } + DPRINTF(2, ("entry erased, skipping...\n")); + continue; + } + + if (buffer[0xb] == 0xf) { // long file name + if ((buffer[0xc] != 0) || + (buffer[0x1a] != 0) || (buffer[0x1b] != 0)) { + dprintf("invalid long file name: reserved fields munged\n"); + continue; + } + if (start_index == 0xffff) { + if ((buffer[0] & 0x40) == 0) { + dprintf("bad lfn start entry in directory\n"); + continue; + } + hash = buffer[0xd]; + lfn_count = buffer[0] & 0x1f; + start_index = iter->current_index; + puni = (uint16 *)(uni + 2*13*(lfn_count - 1)); + for (i=1;i<0x20;i+=2) { + if (*(uint16 *)&buffer[i] == 0xffff) + break; + *puni++ = *(uint16 *)&buffer[i]; + if (i == 0x9) i+=3; + if (i == 0x18) i+=2; + } + *puni++ = 0; + filename_len = (uchar *)(puni) - uni; + + continue; + } else { + if (buffer[0xd] != hash) { + dprintf("error in long file name: hash values don't match\n"); + start_index = 0xffff; + continue; + } + if (buffer[0] != --lfn_count) { + dprintf("bad lfn entry in directory\n"); + start_index = 0xffff; + continue; + } + + puni = (uint16 *)(uni + 2*13*(lfn_count - 1)); + for (i=1;i<0x20;i+=2) { + if ((buffer[i] == 0xff) && (buffer[i+1] == 0xff)) { + dprintf("bad lfn entry in directory\n"); + start_index = 0xffff; + break; + } + *puni++ = *(uint16 *)&buffer[i]; + if (i == 0x9) i+=3; + if (i == 0x18) i+=2; + } + continue; + } + } + + break; + } + + // hit end of directory entries with no luck + if (buffer == NULL) + return ENOENT; + + // process long name + if (start_index != 0xffff) { + if (lfn_count != 1) { + dprintf("unfinished lfn in directory\n"); + start_index = 0xffff; + } else { + if (unicode_to_utf8(uni, filename_len, (uint8*)filename, len)) { + // rewind to beginning of call + dprintf("error: long file name too long\n"); + + diri_free(iter); + diri_init(iter->csi.vol, iter->starting_cluster, start_index, iter); + return ENAMETOOLONG; + } else if (hash_msdos_name((const char *)buffer) != hash) { + dprintf("error: long file name (%s) hash and short file name don't match\n", filename); + start_index = 0xffff; + } + } + } + + // process short name + if (start_index == 0xffff) { + start_index = iter->current_index; + // korli : seen on FreeBSD /src/sys/fs/msdosfs/direntry.h + msdos_to_utf8(buffer, (uchar *)filename, len, buffer[0xc] & 0x18); + } + + if (oinfo) { + oinfo->sindex = start_index; + oinfo->eindex = iter->current_index; + oinfo->mode = buffer[0xb]; + oinfo->cluster = read16(buffer,0x1a); + if (iter->csi.vol->fat_bits == 32) + oinfo->cluster += 0x10000*read16(buffer,0x14); + oinfo->size = read32(buffer,0x1c); + oinfo->time = read32(buffer,0x16); + } + + diri_next_entry(iter); + + return B_NO_ERROR; +} + +static status_t get_next_dirent(nspace *vol, vnode *dir, struct diri *iter, + vnode_id *vnid, char *filename, int len) +{ + struct _dirent_info_ info; + status_t result; + + if (check_nspace_magic(vol, "get_next_dirent")) return EINVAL; + + do { + result = _next_dirent_(iter, &info, filename, len); + + if (result < 0) return result; + // only hide volume label entries in the root directory + } while ((info.mode & FAT_VOLUME) && (dir->vnid == vol->root_vnode.vnid)); + + if (!strcmp(filename, ".")) { + // assign vnode based on parent + if (vnid) *vnid = dir->vnid; + } else if (!strcmp(filename, "..")) { + // assign vnode based on parent of parent + if (vnid) *vnid = dir->dir_vnid; + } else { + if (vnid) { + vnode_id loc = (IS_DATA_CLUSTER(info.cluster)) ? + GENERATE_DIR_CLUSTER_VNID(dir->vnid, info.cluster) : + GENERATE_DIR_INDEX_VNID(dir->vnid, info.sindex); + bool added_to_vcache = false; + + /* if it matches a loc in the lookup table, we are done. */ + result = vcache_loc_to_vnid(vol, loc, vnid); + if (result == ENOENT) { + /* ...else check if it matches any vnid's in the lookup table */ + if (find_vnid_in_vcache(vol, loc) == B_OK) { + /* if it does, create a random one since we can't reuse + * existing vnid's */ + *vnid = generate_unique_vnid(vol); + /* and add it to the vcache */ + if ((result = add_to_vcache(vol, *vnid, loc)) < 0) + return result; + added_to_vcache = true; + } else { + /* otherwise we are free to use it */ + *vnid = loc; + } + } else if (result != B_OK) { + dprintf("get_next_dirent: unknown error (%s)\n", strerror(result)); + return result; + } + + if (info.mode & FAT_SUBDIR) { + if (dlist_find(vol, info.cluster) == -1LL) { + if ((result = dlist_add(vol, *vnid)) < 0) { + if (added_to_vcache) + remove_from_vcache(vol, *vnid); + return result; + } + } + } + } + } + + DPRINTF(2, ("get_next_dirent: found %s (vnid %Lx)\n", filename, *vnid)); + + return B_NO_ERROR; +} + +status_t check_dir_empty(nspace *vol, vnode *dir) +{ + uint32 i; + struct diri iter; + status_t result = B_ERROR; /* quiet warning */ + + if (check_nspace_magic(vol, "check_dir_empty")) return EINVAL; + if (check_vnode_magic(dir, "check_dir_empty")) return EINVAL; + + if (diri_init(vol, dir->cluster, 0, &iter) == NULL) { + dprintf("check_dir_empty: error opening directory\n"); + ASSERT(0); + return B_ERROR; + } + + i = (dir->vnid == vol->root_vnode.vnid) ? 2 : 0; + + for (;i<3;i++) { + char filename[512]; + result = _next_dirent_(&iter, NULL, filename, 512); + + if (result < 0) { + if ((i == 2) && (result == ENOENT)) result = B_OK; + break; + } + + if (((i == 0) && strcmp(filename, ".")) || + ((i == 1) && strcmp(filename, "..")) || + // weird case where ./.. are stored as long file names + ((i < 2) && (iter.current_index != i+1))) { + dprintf("check_dir_empty: malformed directory\n"); + result = ENOTDIR; + break; + } + + result = ENOTEMPTY; + } + + diri_free(&iter); + + return result; +} + +status_t findfile_case(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node) +{ + return findfile(vol, dir, file, vnid, node, true, false, NULL); +} + +status_t findfile_nocase(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node) +{ + return findfile(vol, dir, file, vnid, node, false, false, NULL); +} + +status_t findfile_nocase_duplicates(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node, bool *dups_exist) +{ + return findfile(vol, dir, file, vnid, node, false, true, dups_exist); +} + +status_t findfile_case_duplicates(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node, bool *dups_exist) +{ + return findfile(vol, dir, file, vnid, node, true, true, dups_exist); +} + +static status_t findfile(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node, bool check_case, + bool check_dups, bool *dups_exist) +{ + /* Starting at the base, find the file in the subdir + and return its vnode id */ + /* The check_case flags determines whether or not the search + is done for the exact case or not. If it is not, it will + return the first occurance of the match. */ + /* The check_dups flag instructs the function to find the + first filename match based on the case sensitivity in + check_case, but continue searching to see if there are + any other case-insensitive matches. If there are, the + dups_exist flag is set to true. */ + int result = 0; + vnode_id found_vnid = 0; + bool found_file = false; + +// dprintf("findfile: %s in %Lx, case %d dups %d\n", file, dir->vnid, check_case, check_dups); + + if (check_nspace_magic(vol, "findfile")) return EINVAL; + if (check_vnode_magic(dir, "findfile")) return EINVAL; + + DPRINTF(1, ("findfile: %s in %Lx\n", file, dir->vnid)); + + if(dups_exist != NULL) *dups_exist = false; + else check_dups = false; + + if ((strcmp(file,".") == 0) && (dir->vnid == vol->root_vnode.vnid)) { + found_file = true; + found_vnid = dir->vnid; + } else if ((strcmp(file, "..") == 0) && (dir->vnid == vol->root_vnode.vnid)) { + found_file = true; + found_vnid = dir->dir_vnid; + } else { + struct diri diri; + + // XXX: do it in a smarter way + diri_init(vol, dir->cluster, 0, &diri); + + while (1) + { + char filename[512]; + vnode_id _vnid; + + result = get_next_dirent(vol, dir, &diri, &_vnid, filename, 512); + + if (result != B_NO_ERROR) + break; + + if(check_case) { + if (!found_file && !strcmp(filename, file)) { + found_file = true; + found_vnid = _vnid; + } else if(check_dups && !strcasecmp(filename, file)) { + *dups_exist = true; + } + } else { + if (!strcasecmp(filename, file)) { + if(check_dups && found_file) { + *dups_exist = true; + } + found_file = true; + found_vnid = _vnid; + } + } + + if(found_file && (!check_dups || (check_dups && *dups_exist))) { + break; + } + } + diri_free(&diri); + } + if (found_file) { + if (vnid) *vnid = found_vnid; + if (node) result = get_vnode(vol->id, found_vnid, (void **)node); + result = B_OK; + } else { + result = ENOENT; + } +#if 0 + dprintf("findfile: returning %d", result); + if(dups_exist) + dprintf(" dups_exist %d\n", *dups_exist); + else + dprintf("\n"); +#endif + return result; +} + +status_t erase_dir_entry(nspace *vol, vnode *node) +{ + status_t result; + uint32 i; + char filename[512]; + uint8 *buffer; + struct _dirent_info_ info; + struct diri diri; + + DPRINTF(0, ("erasing directory entries %lx through %lx\n", node->sindex, node->eindex)); + buffer = diri_init(vol,VNODE_PARENT_DIR_CLUSTER(node), node->sindex, &diri); + + // first pass: check if the entry is still valid + if (buffer == NULL) { + dprintf("erase_dir_entry: error reading directory\n"); + return ENOENT; + } + + result = _next_dirent_(&diri, &info, filename, 512); + diri_free(&diri); + + if (result < 0) return result; + + if ((info.sindex != node->sindex) || + (info.eindex != node->eindex)) { + // any other attributes may be in a state of flux due to wstat calls + dprintf("erase_dir_entry: directory entry doesn't match\n"); + return B_ERROR; + } + + // second pass: actually erase the entry + buffer = diri_init(vol, VNODE_PARENT_DIR_CLUSTER(node), node->sindex, &diri); + for (i=node->sindex;(i<=node->eindex)&&(buffer);buffer=diri_next_entry(&diri),i++) { + buffer[0] = 0xe5; // mark entry erased + diri_mark_dirty(&diri); + } + diri_free(&diri); + + return 0; +} + +// shrink directory to the size needed +// errors here are neither likely nor problematic +// w95 doesn't seem to do this, so it's possible to create a +// really large directory that consumes all available space! +status_t compact_directory(nspace *vol, vnode *dir) +{ + uint32 last = 0; + struct diri diri; + status_t error = B_ERROR; /* quiet warning */ + + DPRINTF(0, ("compacting directory with vnode id %Lx\n", dir->vnid)); + + // root directory can't shrink in fat12 and fat16 + if (IS_FIXED_ROOT(dir->cluster)) + return 0; + + diri_init(vol, dir->cluster, 0, &diri); + while (diri.current_block) { + char filename[512]; + struct _dirent_info_ info; + + error = _next_dirent_(&diri, &info, filename, 512); + + if (error == B_OK) { + // don't compact away volume labels in the root dir + if (!(info.mode & FAT_VOLUME) || (dir->vnid != vol->root_vnode.vnid)) + last = diri.current_index; + } else if (error == ENOENT) { + uint32 clusters = (last + vol->bytes_per_sector / 0x20 * vol->sectors_per_cluster - 1) / (vol->bytes_per_sector / 0x20) / vol->sectors_per_cluster; + error = 0; + + // special case for fat32 root directory; we don't want + // it to disappear + if (clusters == 0) clusters = 1; + + if (clusters * vol->bytes_per_sector * vol->sectors_per_cluster < dir->st_size) { + DPRINTF(0, ("shrinking directory to %lx clusters\n", clusters)); + error = set_fat_chain_length(vol, dir, clusters); + dir->st_size = clusters*vol->bytes_per_sector*vol->sectors_per_cluster; + dir->iteration++; + } + break; + } else { + dprintf("compact_directory: unknown error from _next_dirent_ (%s)\n", strerror(error)); + break; + } + } + diri_free(&diri); + + return error; +} + +// name is array of char[11] as returned by findfile +static status_t find_short_name(nspace *vol, vnode *dir, const uchar *name) +{ + struct diri diri; + uint8 *buffer; + status_t result = ENOENT; + + buffer = diri_init(vol, dir->cluster, 0, &diri); + while (buffer) { + if (buffer[0] == 0) + break; + + if (buffer[0xb] != 0xf) { // not long file name + if (!memcmp(name, buffer, 11)) { + result = B_OK; + break; + } + } + + buffer = diri_next_entry(&diri); + } + + diri_free(&diri); + + return result; +} + +struct _entry_info_ { + uint32 mode; + uint32 cluster; + uint32 size; + time_t time; +}; + +static status_t _create_dir_entry_(nspace *vol, vnode *dir, struct _entry_info_ *info, + const char nshort[11], const char *nlong, uint32 len, uint32 *ns, uint32 *ne) +{ + status_t error = B_ERROR; /* quiet warning */ + uint32 required_entries, i; + uint8 *buffer, hash; + bool last_entry; + struct diri diri; + + // short name cannot be the same as that of a device + // this list was created by running strings on io.sys + const char *device_names[] = { + "CON ", + "AUX ", + "PRN ", + "CLOCK$ ", + "COM1 ", + "LPT1 ", + "LPT2 ", + "LPT3 ", + "COM2 ", + "COM3 ", + "COM4 ", + "CONFIG$ ", + NULL + }; + + // check short name against device names + for (i=0;device_names[i];i++) { + // only first 8 characters seem to matter + if (!memcmp(nshort, device_names[i], 8)) + return EPERM; + } + + if ((info->cluster != 0) && !IS_DATA_CLUSTER(info->cluster)) { + dprintf("_create_dir_entry_ for bad cluster (%lx)\n", info->cluster); + return EINVAL; + } + + /* convert byte length of unicode name to directory entries */ + required_entries = (len + 24) / 26 + 1; + + // find a place to put the entries + *ns = 0; + last_entry = true; + diri_init(vol, dir->cluster, 0, &diri); + while (diri.current_block) { + char filename[512]; + struct _dirent_info_ info; + error = _next_dirent_(&diri, &info, filename, 512); + if (error == B_OK) { + if (info.sindex - *ns >= required_entries) { + last_entry = false; + break; + } + *ns = diri.current_index; + } else if (error == ENOENT) { + // hit end of directory marker + break; + } else { + dprintf("_create_dir_entry_: unknown error from _next_dirent_ (%s)\n", strerror(error)); + break; + } + } + + // if at end of directory, last_entry flag will be true as it should be + + diri_free(&diri); + + if ((error != B_OK) && (error != ENOENT)) return error; + + *ne = *ns + required_entries - 1; + + for (i=*ns;i<=*ne;i++) { + ASSERT(find_loc_in_vcache(vol, \ + GENERATE_DIR_INDEX_VNID(dir->cluster, i)) == ENOENT); + } + + DPRINTF(0, ("directory entry runs from %lx to %lx (dirsize = %Lx) (is%s last entry)\n", *ns, *ne, dir->st_size, last_entry ? "" : "n't")); + + // check if the directory needs to be expanded + if (*ne * 0x20 >= dir->st_size) { + uint32 clusters_needed; + + // can't expand fat12 and fat16 root directories :( + if (IS_FIXED_ROOT(dir->cluster)) { + DPRINTF(0, ("_create_dir_entry_: out of space in root directory\n")); + return ENOSPC; + } + + // otherwise grow directory to fit + clusters_needed = ((*ne + 1) * 0x20 + + vol->bytes_per_sector*vol->sectors_per_cluster - 1) / + vol->bytes_per_sector / vol->sectors_per_cluster; + + DPRINTF(0, ("expanding directory from %Lx to %lx clusters\n", dir->st_size/vol->bytes_per_sector/vol->sectors_per_cluster, clusters_needed)); + if ((error = set_fat_chain_length(vol, dir, clusters_needed)) < 0) + return error; + dir->st_size = vol->bytes_per_sector*vol->sectors_per_cluster*clusters_needed; + dir->iteration++; + } + + // starting blitting entries + buffer = diri_init(vol,dir->cluster, *ns, &diri); + hash = hash_msdos_name(nshort); + + // write lfn entries + for (i=1;(imode; + memset(buffer+0xc, 0, 0x16-0xc); + i = time_t2dos(info->time); + buffer[0x16] = i & 0xff; + buffer[0x17] = (i >> 8) & 0xff; + buffer[0x18] = (i >> 16) & 0xff; + buffer[0x19] = (i >> 24) & 0xff; + i = info->cluster; + if (info->size == 0) i = 0; // cluster = 0 for 0 byte files + buffer[0x1a] = i & 0xff; + buffer[0x1b] = (i >> 8) & 0xff; + if (vol->fat_bits == 32) { + buffer[0x14] = (i >> 16) & 0xff; + buffer[0x15] = (i >> 24) & 0xff; + } + i = (info->mode & FAT_SUBDIR) ? 0 : info->size; + buffer[0x1c] = i & 0xff; + buffer[0x1d] = (i >> 8) & 0xff; + buffer[0x1e] = (i >> 16) & 0xff; + buffer[0x1f] = (i >> 24) & 0xff; + diri_mark_dirty(&diri); + + if (last_entry) { + // add end of directory markers to the rest of the + // cluster; need to clear all the other entries or else + // scandisk will complain. + while ((buffer = diri_next_entry(&diri)) != NULL) { + memset(buffer, 0, 0x20); + diri_mark_dirty(&diri); + } + } + + diri_free(&diri); + + return 0; +} + +// doesn't do any name checking +status_t create_volume_label(nspace *vol, const char name[11], uint32 *index) +{ + status_t err; + uint32 dummy; + struct _entry_info_ info = { + FAT_ARCHIVE | FAT_VOLUME, 0, 0, 0 + }; + time(&info.time); + + // check if name already exists + err = find_short_name(vol, &(vol->root_vnode), (uchar *)name); + if (err == B_OK) + return EEXIST; + if (err != ENOENT) + return err; + + return _create_dir_entry_(vol, &(vol->root_vnode), &info, name, NULL, 0, index, &dummy); +} + +bool is_filename_legal(const char *name) +{ + unsigned int i; + unsigned int len = strlen(name); + + if (len <= 0) return false; + + // names ending with a dot are not allowed + if (name[len - 1] == '.') return false; + // names ending with a space are not allowed + if (name[len - 1] == ' ') return false; + + // XXX illegal character search can be made faster + for(i=0; ivnid)); + return EEXIST; + } + if (error != ENOENT) + return error; + + // check name legality while converting. we ignore the case conversion + // flag, i.e. (filename "blah" will always have a patched short name), + // because the whole case conversion system in dos is brain damaged; + // remanants of CP/M no less. + + // existing names pose a problem; in these cases, we'll just live with + // two identical short names. not a great solution, but there's little + // we can do about it. + len = utf8_to_unicode(name, nlong, 512); + if (len <= 0) { + DPRINTF(0, ("Error converting utf8 name '%s' to unicode\n", name)); + return len ? len : B_ERROR; + } + memset(nlong + len, 0xff, 512 - len); /* pad with 0xff */ + + error = generate_short_name((uchar *)name, nlong, len, nshort, &encoding); + if (error) { + DPRINTF(0, ("Error generating short name for '%s'\n", name)); + return error; + } + + // if there is a long name, patch short name if necessary and check for duplication + if (requires_long_name(name, nlong)) { + char tshort[11]; // temporary short name + int iter = 1; + + memcpy(tshort, nshort, 11); + + if (requires_munged_short_name((uchar *)name, nshort, encoding)) + error = B_OK; + else + error = find_short_name(vol, dir, nshort); + + if (error == B_OK) { + do { + memcpy(nshort, tshort, 11); + DPRINTF(0, ("trying short name %11.11s\n", nshort)); + munge_short_name1(nshort, iter, encoding); + } while (((error = find_short_name(vol, dir, nshort)) == B_OK) && (++iter < 10)); + } + + if ((error != B_OK) && (error != ENOENT)) return error; + + if (error == B_OK) { + // XXX: possible infinite loop here + do { + memcpy(nshort, tshort, 11); + DPRINTF(0, ("trying short name %11.11s\n", nshort)); + munge_short_name2(nshort, encoding); + } while ((error = find_short_name(vol, dir, nshort)) == B_OK); + + if (error != ENOENT) return error; + } + } else { + len = 0; /* entry doesn't need a long name */ + } + + DPRINTF(0, ("creating directory entry (%11.11s)\n", nshort)); + + info.mode = node->mode; + info.cluster = node->cluster; + info.size = node->st_size; + info.time = node->st_time; + + return _create_dir_entry_(vol, dir, &info, (char *)nshort, (char *)nlong, len, ns, ne); +} + +int dosfs_read_vnode(void *_vol, vnode_id vnid, char reenter, void **_node) +{ + nspace *vol = (nspace*)_vol; + int result = B_NO_ERROR; + vnode_id loc, dir_vnid; + vnode *entry; + struct _dirent_info_ info; + struct diri iter; + char filename[512]; /* need this for setting mime type */ + + if (!reenter) { LOCK_VOL(vol); } + + *_node = NULL; + + if (check_nspace_magic(vol, "dosfs_read_vnode")) { + if (!reenter) UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_read_vnode (vnode id %Lx)\n", vnid)); + + if (vnid == vol->root_vnode.vnid) + { + dprintf("??? dosfs_read_vnode called on root node ???\n"); + *_node = (void *)&(vol->root_vnode); + goto bi; + } + + if (vcache_vnid_to_loc(vol, vnid, &loc) != B_OK) + loc = vnid; + + if (IS_ARTIFICIAL_VNID(loc) || IS_INVALID_VNID(loc)) { + DPRINTF(0, ("dosfs_read_vnode: unknown vnid %Lx (loc %Lx)\n", vnid, loc)); + result = ENOENT; + goto bi; + } + + if ((dir_vnid = dlist_find(vol, DIR_OF_VNID(loc))) == -1LL) { + DPRINTF(0, ("dosfs_read_vnode: unknown directory at cluster %lx\n", DIR_OF_VNID(loc))); + result = ENOENT; + goto bi; + } + + if (diri_init(vol, DIR_OF_VNID(loc), + IS_DIR_CLUSTER_VNID(loc) ? 0 : INDEX_OF_DIR_INDEX_VNID(loc), + &iter) == NULL) { + dprintf("dosfs_read_vnode: error initializing directory for vnid %Lx (loc %Lx)\n", vnid, loc); + result = ENOENT; + goto bi; + } + + while (1) { + result = _next_dirent_(&iter, &info, filename, 512); + if (result < 0) { + dprintf("dosfs_read_vnode: error finding vnid %Lx (loc %Lx) (%s)\n", vnid, loc, strerror(result)); + goto bi2; + } + + if (IS_DIR_CLUSTER_VNID(loc)) { + if (info.cluster == CLUSTER_OF_DIR_CLUSTER_VNID(loc)) + break; + } else { + if (info.sindex == INDEX_OF_DIR_INDEX_VNID(loc)) + break; + dprintf("dosfs_read_vnode: error finding vnid %Lx (loc %Lx) (%s)\n", vnid, loc, strerror(result)); + result = ENOENT; + goto bi2; + } + } + + if ((entry = calloc(sizeof(struct vnode), 1)) == NULL) { + DPRINTF(0, ("dosfs_read_vnode: out of memory\n")); + result = ENOMEM; + goto bi2; + } + + entry->magic = VNODE_MAGIC; + entry->vnid = vnid; + entry->dir_vnid = dir_vnid; + entry->disk_image = 0; + if (vol->respect_disk_image) { + if ((dir_vnid == vol->root_vnode.vnid) && !strcmp(filename, "BEOS")) { + vol->beos_vnid = vnid; + entry->disk_image = 1; + } + if ((dir_vnid == vol->beos_vnid) && !strcmp(filename, "IMAGE.BE")) { + entry->disk_image = 2; + } + } + entry->iteration = 0; + entry->sindex = info.sindex; + entry->eindex = info.eindex; + entry->cluster = info.cluster; + entry->mode = info.mode; + entry->st_size = info.size; + entry->dirty = false; + if (info.mode & FAT_SUBDIR) { + entry->st_size = count_clusters(vol,entry->cluster) * vol->sectors_per_cluster * vol->bytes_per_sector; + } + if (entry->cluster) + entry->end_cluster = get_nth_fat_entry(vol, info.cluster, + (entry->st_size + vol->bytes_per_sector * vol->sectors_per_cluster - 1) / + vol->bytes_per_sector / vol->sectors_per_cluster - 1); + else + entry->end_cluster = 0; + entry->st_time = dos2time_t(info.time); +#if TRACK_FILENAME + entry->filename = malloc(sizeof(filename) + 1); + if (entry->filename) strcpy(entry->filename, filename); +#endif + if(!(entry->mode & FAT_SUBDIR)) + set_mime_type(entry, filename); + + *_node = entry; + +bi2:diri_free(&iter); +bi: if (!reenter) UNLOCK_VOL(vol); + + if (result != B_OK) DPRINTF(0, ("dosfs_read_vnode (%s)\n", strerror(result))); + + return result; +} + +int dosfs_walk(void *_vol, void *_dir, const char *file, char **newpath, vnode_id *vnid) +{ + /* Starting at the base, find file in the subdir, and return path + string and vnode id of file. */ + nspace *vol = (nspace*)_vol; + vnode *dir = (vnode*)_dir; + vnode *vnode = NULL; + int result = ENOENT; + + /* we can ignore newpath since there are no symbolic links */ + TOUCH(newpath); + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_walk") || + check_vnode_magic(dir, "dosfs_walk")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_walk: find %Lx/%s\n", dir->vnid, file)); + + result = findfile_case(vol, dir, file, vnid, &vnode); + if (result != B_OK) { + DPRINTF(0, ("dosfs_walk (%s)\n", strerror(result))); + } else { + DPRINTF(0, ("dosfs_walk: found vnid %Lx\n", *vnid)); + } + + UNLOCK_VOL(vol); + + return result; +} + +int dosfs_access(void *_vol, void *_node, int mode) +{ + status_t result = B_OK; + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_access") || + check_vnode_magic(node, "dosfs_access")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_access (vnode id %Lx, mode %x)\n", node->vnid, mode)); + + if ((mode & O_RWMASK) != O_RDONLY) { + if (vol->flags & B_FS_IS_READONLY) { + dprintf("dosfs_access: can't write on read-only volume\n"); + result = EROFS; + } else if (node->mode & FAT_READ_ONLY) { + dprintf("can't open read-only file for writing\n"); + result = EPERM; + } else if (node->disk_image != 0) { + dprintf("can't open disk image file for writing\n"); + result = EPERM; + } + } + + UNLOCK_VOL(vol); + + return result; +} + +int dosfs_readlink(void *_vol, void *_node, char *buf, size_t *bufsize) +{ + TOUCH(_vol); TOUCH(_node); TOUCH(buf); TOUCH(bufsize); + + // no links in fat... + DPRINTF(0, ("dosfs_readlink called\n")); + + return EINVAL; +} + +int dosfs_opendir(void *_vol, void *_node, void **_cookie) +{ + nspace *vol = (nspace*)_vol; + vnode *node = (vnode*)_node; + dircookie *cookie = NULL; + int result; + + if (_cookie == NULL) { + dprintf("dosfs_opendir called with null _cookie\n"); + return EINVAL; + } + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_opendir") || + check_vnode_magic(node, "dosfs_opendir")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_opendir (vnode id %Lx)\n", node->vnid)); + + *_cookie = NULL; + + if (!(node->mode & FAT_SUBDIR)) { + /* bash will try to opendir files unless OPENDIR_NOT_ROBUST is + * defined, so we'll suppress this message; it's more of a problem + * with the application than with the file system, anyway + */ + DPRINTF(0, ("dosfs_opendir error: vnode not a directory\n")); + result = ENOTDIR; + goto bi; + } + + if ((cookie = (dircookie *)malloc(sizeof(dircookie))) == NULL) { + dprintf("dosfs_opendir: out of memory error\n"); + result = ENOMEM; + goto bi; + } + + cookie->magic = DIRCOOKIE_MAGIC; + cookie->current_index = 0; + + result = B_NO_ERROR; + +bi: + *_cookie = (void*)cookie; + + if (result != B_OK) DPRINTF(0, ("dosfs_opendir (%s)\n", strerror(result))); + + UNLOCK_VOL(vol); + + return result; +} + +int dosfs_readdir(void *_vol, void *_dir, void *_cookie, long *num, + struct dirent *entry, size_t bufsize) +{ + int result = ENOENT; + nspace* vol = (nspace*)_vol; + vnode *dir = (vnode *)_dir; + dircookie* cookie = (dircookie*)_cookie; + struct diri diri; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_readdir") || + check_vnode_magic(dir, "dosfs_readdir") || + check_dircookie_magic(cookie, "dosfs_readdir")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_readdir: vnode id %Lx, index %lx\n", dir->vnid, cookie->current_index)); + + // simulate '.' and '..' entries for root directory + if (dir->vnid == vol->root_vnode.vnid) { + if (cookie->current_index >= 2) { + cookie->current_index -= 2; + } else { + if (cookie->current_index++ == 0) { + strcpy(entry->d_name, "."); + entry->d_reclen = 2; + } else { + strcpy(entry->d_name, ".."); + entry->d_reclen = 3; + } + *num = 1; + entry->d_ino = vol->root_vnode.vnid; + entry->d_dev = vol->id; + result = B_NO_ERROR; + goto bi; + } + } + + diri_init(vol, dir->cluster, cookie->current_index, &diri); + result = get_next_dirent(vol, dir, &diri, &(entry->d_ino), entry->d_name, bufsize - sizeof(struct dirent) - 1); + cookie->current_index = diri.current_index; + diri_free(&diri); + + if (dir->vnid == vol->root_vnode.vnid) + cookie->current_index += 2; + + if (result == B_NO_ERROR) { + *num = 1; + entry->d_dev = vol->id; + entry->d_reclen = strlen(entry->d_name) + 1; + DPRINTF(0, ("dosfs_readdir: found file %s\n", entry->d_name)); + } else if (result == ENOENT) { + // When you get to the end, don't return an error, just return 0 + // in *num. + *num = 0; + result = B_NO_ERROR; + } else { + dprintf("dosfs_readdir: error returned by get_next_dirent (%s)\n", strerror(result)); + } +bi: + if (result != B_OK) DPRINTF(0, ("dosfs_readdir (%s)\n", strerror(result))); + + UNLOCK_VOL(vol); + + return result; +} + +int dosfs_rewinddir(void *_vol, void *_node, void* _cookie) +{ + nspace *vol = _vol; + vnode *node = _node; + dircookie *cookie = (dircookie*)_cookie; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_rewinddir") || + check_vnode_magic(node, "dosfs_rewinddir") || + check_dircookie_magic(cookie, "dosfs_rewinddir")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_rewinddir (vnode id %Lx)\n", node->vnid)); + + cookie->current_index = 0; + + UNLOCK_VOL(vol); + + return B_OK; +} + +int dosfs_closedir(void *_vol, void *_node, void *_cookie) +{ + TOUCH(_vol); TOUCH(_node); TOUCH(_cookie); + + DPRINTF(0, ("dosfs_closedir called\n")); + + return 0; +} + +int dosfs_free_dircookie(void *_vol, void *node, void *_cookie) +{ + nspace *vol = _vol; + dircookie *cookie = _cookie; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_free_dircookie") || + check_vnode_magic((vnode *)node, "dosfs_free_dircookie") || + check_dircookie_magic((dircookie *)cookie, "dosfs_free_dircookie")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + if (cookie == NULL) { + dprintf("error: dosfs_free_dircookie called with null cookie\n"); + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_free_dircookie (vnode id %Lx)\n", ((vnode*)node)->vnid)); + + cookie->magic = ~DIRCOOKIE_MAGIC; + free(cookie); + + UNLOCK_VOL(vol); + + return 0; +} + diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/dir.h b/src/tests/add-ons/kernel/file_systems/dos/r5/dir.h new file mode 100644 index 0000000000..5683c7b398 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/dir.h @@ -0,0 +1,36 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_DIR_H_ +#define _DOSFS_DIR_H_ + +bool is_filename_legal(const char *name); +status_t check_dir_empty(nspace *vol, vnode *dir); +status_t findfile_case(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node); +status_t findfile_nocase(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node); +status_t findfile_nocase_duplicates(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node, bool *dups_exist); +status_t findfile_case_duplicates(nspace *vol, vnode *dir, const char *file, + vnode_id *vnid, vnode **node, bool *dups_exist); +status_t erase_dir_entry(nspace *vol, vnode *node); +status_t compact_directory(nspace *vol, vnode *dir); +status_t create_volume_label(nspace *vol, const char name[11], uint32 *index); +status_t create_dir_entry(nspace *vol, vnode *dir, vnode *node, + const char *name, uint32 *ns, uint32 *ne); + +int dosfs_read_vnode(void *_vol, vnode_id vnid, char r, void **node); +int dosfs_walk(void *_vol, void *_dir, const char *file, + char **newpath, vnode_id *vnid); +int dosfs_access(void *_vol, void *_node, int mode); +int dosfs_readlink(void *_vol, void *_node, char *buf, size_t *bufsize); +int dosfs_opendir(void *_vol, void *_node, void **cookie); +int dosfs_readdir(void *_vol, void *_node, void *cookie, + long *num, struct dirent *buf, size_t bufsize); +int dosfs_rewinddir(void *_vol, void *_node, void *cookie); +int dosfs_closedir(void *_vol, void *_node, void *cookie); +int dosfs_free_dircookie(void *_vol, void *_node, void *cookie); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/dlist.c b/src/tests/add-ons/kernel/file_systems/dos/r5/dlist.c new file mode 100644 index 0000000000..c9a4512970 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/dlist.c @@ -0,0 +1,162 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* +directory vnode id list + +We only add to this list as we encounter directories; there is no need to +scan through the directories ourselves since we aren't worried about preserving +vnid's across reboots. + +We don't worry about aliases for directories since their cluster values will +always be the same -- searches are performed only on the starting cluster +number of the directories. + +TODO: + XXX: make this more efficient +*/ + +#define DPRINTF(a,b) if (debug_dlist > (a)) dprintf b + +#include +#include + +#include + +#include "dosfs.h" +#include "dlist.h" +#include "util.h" +#include "vcache.h" + +#if DEBUG + #define DLIST_ENTRY_QUANTUM 1 +#else + #define DLIST_ENTRY_QUANTUM 0x20 +#endif + +status_t dlist_init(nspace *vol) +{ + DPRINTF(0, ("dlist_init called\n")); + + vol->dlist.entries = 0; + vol->dlist.allocated = DLIST_ENTRY_QUANTUM; + vol->dlist.vnid_list = malloc(sizeof(vnode_id) * vol->dlist.allocated); + if (vol->dlist.vnid_list == NULL) { + vol->dlist.allocated = 0; + dprintf("dlist_init: out of core\n"); + return ENOMEM; + } + + return B_OK; +} + +status_t dlist_uninit(nspace *vol) +{ + DPRINTF(0, ("dlist_uninit called\n")); + + if (vol->dlist.vnid_list) + free(vol->dlist.vnid_list); + vol->dlist.entries = vol->dlist.allocated = 0; + vol->dlist.vnid_list = NULL; + + return B_OK; +} + +static status_t dlist_realloc(nspace *vol, uint32 allocate) +{ + vnode_id *vnid_list; + + DPRINTF(0, ("dlist_realloc %lx -> %lx\n", vol->dlist.allocated, allocate)); + + ASSERT(allocate != vol->dlist.allocated); + ASSERT(allocate > vol->dlist.entries); + + vnid_list = malloc(sizeof(vnode_id) * allocate); + if (vnid_list == NULL) { + dprintf("dlist_realloc: out of core\n"); + return ENOMEM; + } + + memcpy(vnid_list, vol->dlist.vnid_list, sizeof(vnode_id) * vol->dlist.entries); + free(vol->dlist.vnid_list); + vol->dlist.vnid_list = vnid_list; + vol->dlist.allocated = allocate; + + return B_OK; +} + +status_t dlist_add(nspace *vol, vnode_id vnid) +{ + DPRINTF(0, ("dlist_add vnid %Lx\n", vnid)); + + ASSERT(IS_DIR_CLUSTER_VNID(vnid) || IS_ARTIFICIAL_VNID(vnid)); + ASSERT(vnid != 0); + // XXX: check for duplicate entries + + if (vol->dlist.entries == vol->dlist.allocated) { + if (dlist_realloc(vol, vol->dlist.allocated + DLIST_ENTRY_QUANTUM) < 0) + return B_ERROR; + } + vol->dlist.vnid_list[vol->dlist.entries++] = vnid; + + return B_OK; +} + +status_t dlist_remove(nspace *vol, vnode_id vnid) +{ + uint32 i; + + DPRINTF(0, ("dlist_remove vnid %Lx\n", vnid)); + + for (i=0;idlist.entries;i++) + if (vol->dlist.vnid_list[i] == vnid) + break; + ASSERT(i < vol->dlist.entries); + if (i == vol->dlist.entries) + return ENOENT; + for (;idlist.entries-1;i++) + vol->dlist.vnid_list[i] = vol->dlist.vnid_list[i+1]; + vol->dlist.entries--; + + if (vol->dlist.allocated - vol->dlist.entries > 2*DLIST_ENTRY_QUANTUM) + return dlist_realloc(vol, vol->dlist.allocated - DLIST_ENTRY_QUANTUM); + + return B_OK; +} + +vnode_id dlist_find(nspace *vol, uint32 cluster) +{ + uint32 i; + + DPRINTF(1, ("dlist_find cluster %lx\n", cluster)); + + ASSERT(((cluster >= 2) && (cluster < vol->total_clusters + 2)) || (cluster == 1)); + + for (i=0;idlist.entries;i++) { + vnode_id loc; + + if (vcache_vnid_to_loc(vol, vol->dlist.vnid_list[i], &loc) < B_OK) + loc = vol->dlist.vnid_list[i]; + ASSERT(IS_DIR_CLUSTER_VNID(loc)); + if (CLUSTER_OF_DIR_CLUSTER_VNID(loc) == cluster) + return vol->dlist.vnid_list[i]; + } + + DPRINTF(1, ("dlist_find cluster %lx not found\n", cluster)); + + return -1LL; +} + +void dlist_dump(nspace *vol) +{ + uint32 i; + + dprintf("%lx/%lx dlist entries filled, QUANTUM = %x\n", + vol->dlist.entries, vol->dlist.allocated, DLIST_ENTRY_QUANTUM); + + for (i=0;idlist.entries;i++) + dprintf("%s %Lx", ((i == 0) ? "entries:" : ","), vol->dlist.vnid_list[i]); + + dprintf("\n"); +} diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/dlist.h b/src/tests/add-ons/kernel/file_systems/dos/r5/dlist.h new file mode 100644 index 0000000000..55fb39d0f3 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/dlist.h @@ -0,0 +1,16 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_DLIST_H_ +#define _DOSFS_DLIST_H_ + +status_t dlist_init(nspace *vol); +status_t dlist_uninit(nspace *vol); +status_t dlist_add(nspace *vol, vnode_id vnid); +status_t dlist_remove(nspace *vol, vnode_id vnid); +vnode_id dlist_find(nspace *vol, uint32 cluster); + +void dlist_dump(nspace *vol); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.c b/src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.c new file mode 100644 index 0000000000..f08da2345c --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.c @@ -0,0 +1,1080 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#ifndef COMPILE_IN_BEOS +#include +#endif +#include +#include + +#include "dosfs.h" +#include "attr.h" +#include "dir.h" +#include "dlist.h" +#include "fat.h" +#include "file.h" +#include "iter.h" +#include "util.h" +#include "vcache.h" + +extern const char *build_time, *build_date; + +/* debug levels */ +int debug_attr = 0, debug_dir = 0, debug_dlist = 0, debug_dosfs = 0, + debug_encodings = 0, debug_fat = 0, debug_file = 0, + debug_iter = 0, debug_vcache = 0; + +#define DPRINTF(a,b) if (debug_dosfs > (a)) dprintf b + +CHECK_MAGIC(vnode,struct vnode,VNODE_MAGIC) +CHECK_MAGIC(nspace,struct _nspace, NSPACE_MAGIC) + +static status_t get_fsinfo(nspace *vol, uint32 *free_count, uint32 *last_allocated); + +#if DEBUG + +int32 instances = 0; + +int debug_dos(int argc, char **argv) +{ + int i; + for (i=1;imagic); + kprintf("id: %lx, fd: %x, device: %s, flags %lx\n", + vol->id, vol->fd, vol->device, vol->flags); + kprintf("bytes/sector = %lx, sectors/cluster = %lx, reserved sectors = %lx\n", + vol->bytes_per_sector, vol->sectors_per_cluster, + vol->reserved_sectors); + kprintf("%lx fats, %lx root entries, %lx total sectors, %lx sectors/fat\n", + vol->fat_count, vol->root_entries_count, vol->total_sectors, + vol->sectors_per_fat); + kprintf("media descriptor %x, fsinfo sector %x, %lx clusters, %lx free\n", + vol->media_descriptor, vol->fsinfo_sector, vol->total_clusters, + vol->free_clusters); + kprintf("%x-bit fat, mirrored %x, active %x\n", + vol->fat_bits, vol->fat_mirrored, vol->active_fat); + kprintf("root start %lx, %lx root sectors, root vnode @ %p\n", + vol->root_start, vol->root_sectors, &(vol->root_vnode)); + kprintf("label entry %lx, label %s\n", vol->vol_entry, vol->vol_label); + kprintf("data start %lx, last allocated %lx\n", + vol->data_start, vol->last_allocated); + kprintf("last fake vnid %Lx, vnid cache %lx entries @ (%p %p)\n", + vol->vcache.cur_vnid, vol->vcache.cache_size, + vol->vcache.by_vnid, vol->vcache.by_loc); + kprintf("dlist entries: %lx/%lx @ %p\n", + vol->dlist.entries, vol->dlist.allocated, vol->dlist.vnid_list); + } + return B_OK; +} + +int debug_dvnode(int argc, char **argv) +{ + int i; + + if (argc < 2) { + kprintf("dvnode vnode\n"); + return B_OK; + } + + for (i=1;ifilename); +#endif + kprintf("\nmagic %lx, vnid %Lx, dir vnid %Lx\n", + n->magic, n->vnid, n->dir_vnid); + kprintf("iteration %lx, si=%lx, ei=%lx, cluster=%lx\n", + n->iteration, n->sindex, n->eindex, n->cluster); + kprintf("mode %lx, size %Lx, time %lx\n", + n->mode, n->st_size, n->st_time); + kprintf("end cluster = %lx\n", n->end_cluster); + if (n->mime) kprintf("mime type %s\n", n->mime); + } + + return B_OK; +} + +int debug_dc2s(int argc, char **argv) +{ + int i; + nspace *vol; + + if (argc < 3) { + kprintf("dc2s nspace cluster\n"); + return B_OK; + } + + vol = (nspace *)strtoul(argv[1], NULL, 0); + if (vol == NULL) + return B_OK; + + for (i=2;idata_start + + (off_t)(cluster - 2) * vol->sectors_per_cluster); + } + + return B_OK; +} + +#endif + +static int lock_removable_device(int fd, bool state) +{ + return ioctl(fd, B_SCSI_PREVENT_ALLOW, &state, sizeof(state)); +} + +static status_t mount_fat_disk(const char *path, nspace_id nsid, + const int flags, nspace** newVol, int fs_flags, int op_sync_mode) +{ + nspace *vol = NULL; + uint8 buf[512]; + int i; + device_geometry geo; + status_t err; + + *newVol = NULL; + if ((vol = (nspace*)calloc(sizeof(nspace), 1)) == NULL) { + dprintf("dosfs error: out of memory\n"); + return ENOMEM; + } + + vol->magic = NSPACE_MAGIC; + vol->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME; + vol->fs_flags = fs_flags; + + // open read-only for now + if ((err = (vol->fd = open(path, O_RDONLY))) < 0) { + dprintf("dosfs: unable to open %s (%s)\n", path, strerror(err)); + goto error0; + } + + // get device characteristics + if (ioctl(vol->fd, B_GET_GEOMETRY, &geo) < 0) { + struct stat st; + if ((fstat(vol->fd, &st) >= 0) && + S_ISREG(st.st_mode)) { + /* support mounting disk images */ + geo.bytes_per_sector = 0x200; + geo.sectors_per_track = 1; + geo.cylinder_count = st.st_size / 0x200; + geo.head_count = 1; + geo.read_only = !(st.st_mode & S_IWUSR); + geo.removable = true; + } else { + dprintf("dosfs: error getting device geometry\n"); + goto error0; + } + } + if ((geo.bytes_per_sector != 0x200) && (geo.bytes_per_sector != 0x400) && (geo.bytes_per_sector != 0x800)) { + dprintf("dosfs: unsupported device block size (%lu)\n", geo.bytes_per_sector); + goto error0; + } + if (geo.removable) { + DPRINTF(0, ("%s is removable\n", path)); + vol->flags |= B_FS_IS_REMOVABLE; + } + if (geo.read_only || (flags & B_MOUNT_READ_ONLY)) { + DPRINTF(0, ("%s is read-only\n", path)); + vol->flags |= B_FS_IS_READONLY; + } else { + // reopen it with read/write permissions + close(vol->fd); + if ((err = (vol->fd = open(path, O_RDWR))) < 0) { + dprintf("dosfs: unable to open %s (%s)\n", path, strerror(err)); + goto error0; + } + if ((vol->flags & B_FS_IS_REMOVABLE) && (vol->fs_flags & FS_FLAGS_LOCK_DOOR)) + lock_removable_device(vol->fd, true); + } + + // see if we need to go into op sync mode + vol->fs_flags &= ~FS_FLAGS_OP_SYNC; + switch(op_sync_mode) { + case 1: + if((vol->flags & B_FS_IS_REMOVABLE) == 0) { + // we're not removable, so skip op_sync + break; + } + case 2: + dprintf("dosfs: mounted with op_sync enabled\n"); + vol->fs_flags |= FS_FLAGS_OP_SYNC; + break; + case 0: + default: + ; + } + + // read in the boot sector + if ((err = read_pos(vol->fd, 0, (void*)buf, 512)) != 512) { + dprintf("dosfs: error reading boot sector\n"); + goto error; + } + + // only check boot signature on hard disks to account for broken mtools + // behavior + if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8)) + goto error; + + if (!memcmp(buf+3, "NTFS ", 8) || !memcmp(buf+3, "HPFS ", 8)) { + dprintf("%4.4s, not FAT\n", buf+3); + goto error; + } + + // first fill in the universal fields from the bpb + vol->bytes_per_sector = read16(buf,0xb); + if ((vol->bytes_per_sector != 0x200) && (vol->bytes_per_sector != 0x400) && (vol->bytes_per_sector != 0x800)) { + dprintf("dosfs error: unsupported bytes per sector (%lu)\n", + vol->bytes_per_sector); + goto error; + } + + vol->sectors_per_cluster = i = buf[0xd]; + if ((i != 1) && (i != 2) && (i != 4) && (i != 8) && + (i != 0x10) && (i != 0x20) && (i != 0x40) && (i != 0x80)) { + dprintf("dosfs: sectors/cluster = %d\n", i); + goto error; + } + + vol->reserved_sectors = read16(buf,0xe); + + vol->fat_count = buf[0x10]; + if ((vol->fat_count == 0) || (vol->fat_count > 8)) { + dprintf("dosfs: unreasonable fat count (%lu)\n", vol->fat_count); + goto error; + } + + vol->media_descriptor = buf[0x15]; + // check media descriptor versus known types + if ((buf[0x15] != 0xF0) && (buf[0x15] < 0xf8)) { + dprintf("dosfs error: invalid media descriptor byte\n"); + goto error; + } + + vol->vol_entry = -2; // for now, assume there is no volume entry + memset(vol->vol_label, ' ', 11); + + // now become more discerning citizens + vol->sectors_per_fat = read16(buf,0x16); + if (vol->sectors_per_fat == 0) { + // fat32 land + vol->fat_bits = 32; + vol->sectors_per_fat = read32(buf,0x24); + vol->total_sectors = read32(buf,0x20); + + vol->fsinfo_sector = read16(buf, 0x30); + if ((vol->fsinfo_sector != 0xffff) && (vol->fsinfo_sector >= vol->reserved_sectors)) { + dprintf("dosfs: fsinfo sector too large (%x)\n", vol->fsinfo_sector); + goto error; + } + + vol->fat_mirrored = !(buf[0x28] & 0x80); + vol->active_fat = (vol->fat_mirrored) ? (buf[0x28] & 0xf) : 0; + + vol->data_start = vol->reserved_sectors + vol->fat_count*vol->sectors_per_fat; + vol->total_clusters = (vol->total_sectors - vol->data_start) / vol->sectors_per_cluster; + + vol->root_vnode.cluster = read32(buf,0x2c); + if (vol->root_vnode.cluster >= vol->total_clusters) { + dprintf("dosfs: root vnode cluster too large (%lx)\n", vol->root_vnode.cluster); + goto error; + } + } else { + // fat12 & fat16 + if (vol->fat_count != 2) { + dprintf("dosfs error: claims %ld fat tables\n", vol->fat_count); + goto error; + } + + vol->root_entries_count = read16(buf,0x11); + if (vol->root_entries_count % (vol->bytes_per_sector / 0x20)) { + dprintf("dosfs error: invalid number of root entries\n"); + goto error; + } + + vol->fsinfo_sector = 0xffff; + vol->total_sectors = read16(buf,0x13); // partition size + if (vol->total_sectors == 0) + vol->total_sectors = read32(buf,0x20); + + { + /* + Zip disks that were formatted at iomega have an incorrect number + of sectors. They say that they have 196576 sectors but they + really only have 196192. This check is a work-around for their + brain-deadness. + */ + unsigned char bogus_zip_data[] = { + 0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, + 0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00 + }; + + if (memcmp(buf+0x0b, bogus_zip_data, sizeof(bogus_zip_data)) == 0 && + vol->total_sectors == 196576 && + ((off_t)geo.sectors_per_track * + (off_t)geo.cylinder_count * + (off_t)geo.head_count) == 196192) { + + vol->total_sectors = 196192; + } + } + + + if (buf[0x26] == 0x29) { + // fill in the volume label + if (memcmp(buf+0x2b, " ", 11)) { + memcpy(vol->vol_label, buf+0x2b, 11); + vol->vol_entry = -1; + } + } + + vol->fat_mirrored = true; + vol->active_fat = 0; + + vol->root_start = vol->reserved_sectors + vol->fat_count * vol->sectors_per_fat; + vol->root_sectors = vol->root_entries_count * 0x20 / vol->bytes_per_sector; + vol->root_vnode.cluster = 1; + vol->root_vnode.end_cluster = 1; + vol->root_vnode.st_size = vol->root_sectors * vol->bytes_per_sector; + + vol->data_start = vol->root_start + vol->root_sectors; + vol->total_clusters = (vol->total_sectors - vol->data_start) / vol->sectors_per_cluster; + + // XXX: uncertain about border cases; win32 sdk says cutoffs are at + // at ff6/ff7 (or fff6/fff7), but that doesn't make much sense + if (vol->total_clusters > 0xff1) + vol->fat_bits = 16; + else + vol->fat_bits = 12; + } + + /* check that the partition is large enough to contain the file system */ + if (vol->total_sectors > geo.sectors_per_track * geo.cylinder_count * + geo.head_count) { + dprintf("dosfs: volume extends past end of partition\n"); + err = B_PARTITION_TOO_SMALL; + goto error; + } + + // perform sanity checks on the FAT + + // the media descriptor in active FAT should match the one in the BPB + if ((err = read_pos(vol->fd, vol->bytes_per_sector*(vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat), (void *)buf, 0x200)) != 0x200) { + dprintf("dosfs: error reading FAT\n"); + goto error; + } + + if (buf[0] != vol->media_descriptor) { + dprintf("dosfs error: media descriptor mismatch (%x != %x)\n", buf[0], vol->media_descriptor); + goto error; + } + + if (vol->fat_mirrored) { + uint32 i; + uint8 buf2[512]; + for (i=0;ifat_count;i++) { + if (i != vol->active_fat) { + DPRINTF(1, ("checking fat #%ld\n", i)); + buf2[0] = ~buf[0]; + if ((err = read_pos(vol->fd, vol->bytes_per_sector*(vol->reserved_sectors + vol->sectors_per_fat*i), (void *)buf2, 0x200)) != 0x200) { + dprintf("dosfs: error reading FAT %ld\n", i); + goto error; + } + + if (buf2[0] != vol->media_descriptor) { + dprintf("dosfs error: media descriptor mismatch in fat # %ld (%x != %x)\n", i, buf2[0], vol->media_descriptor); + goto error; + } +#if 0 + // checking for exact matches of fats is too + // restrictive; allow these to go through in + // case the fat is corrupted for some reason + if (memcmp(buf, buf2, 0x200)) { + dprintf("dosfs error: fat %d doesn't match active fat (%d)\n", i, vol->active_fat); + goto error; + } +#endif + } + } + } + + // now we are convinced of the drive's validity + + vol->id = nsid; + strncpy(vol->device,path,256); + + // this will be updated later if fsinfo exists + vol->last_allocated = 2; + + vol->beos_vnid = INVALID_VNID_BITS_MASK; + { + void *handle; + handle = load_driver_settings("dos"); + vol->respect_disk_image = + get_driver_boolean_parameter(handle, "respect", true, true); + unload_driver_settings(handle); + } + + // initialize block cache + if (init_cache_for_device(vol->fd, (off_t)vol->total_sectors) < 0) { + dprintf("error initializing block cache\n"); + goto error; + } + + // as well as the vnode cache + if (init_vcache(vol) != B_OK) { + dprintf("error initializing vnode cache\n"); + goto error1; + } + + // and the dlist cache + if (dlist_init(vol) != B_OK) { + dprintf("error initializing dlist cache\n"); + goto error2; + } + + if (vol->flags & B_FS_IS_READONLY) + vol->free_clusters = 0; + else { + uint32 free_count, last_allocated; + err = get_fsinfo(vol, &free_count, &last_allocated); + if (err >= 0) { + if (free_count < vol->total_clusters) + vol->free_clusters = free_count; + else { + dprintf("free cluster count from fsinfo block invalid %lx\n", free_count); + err = -1; + } + if (last_allocated < vol->total_clusters) + vol->last_allocated = last_allocated; //update to a closer match + } + if (err < 0) { + if ((err = count_free_clusters(vol)) < 0) { + dprintf("error counting free clusters (%s)\n", strerror(err)); + goto error3; + } + vol->free_clusters = err; + } + } + + DPRINTF(0, ("built at %s on %s\n", build_time, build_date)); + DPRINTF(0, ("mounting %s (id %lx, device %x, media descriptor %x)\n", vol->device, vol->id, vol->fd, vol->media_descriptor)); + DPRINTF(0, ("%lx bytes/sector, %lx sectors/cluster\n", vol->bytes_per_sector, vol->sectors_per_cluster)); + DPRINTF(0, ("%lx reserved sectors, %lx total sectors\n", vol->reserved_sectors, vol->total_sectors)); + DPRINTF(0, ("%lx %d-bit fats, %lx sectors/fat, %lx root entries\n", vol->fat_count, vol->fat_bits, vol->sectors_per_fat, vol->root_entries_count)); + DPRINTF(0, ("root directory starts at sector %lx (cluster %lx), data at sector %lx\n", vol->root_start, vol->root_vnode.cluster, vol->data_start)); + DPRINTF(0, ("%lx total clusters, %lx free\n", vol->total_clusters, vol->free_clusters)); + DPRINTF(0, ("fat mirroring is %s, fs info sector at sector %x\n", (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector)); + DPRINTF(0, ("last allocated cluster = %lx\n", vol->last_allocated)); + + if (vol->fat_bits == 32) { + // now that the block cache has been initialised, we can figure + // out the length of the root directory with count_clusters() + vol->root_vnode.st_size = count_clusters(vol, vol->root_vnode.cluster) * vol->bytes_per_sector * vol->sectors_per_cluster; + vol->root_vnode.end_cluster = get_nth_fat_entry(vol, vol->root_vnode.cluster, vol->root_vnode.st_size / vol->bytes_per_sector / vol->sectors_per_cluster - 1); + } + + // initialize root vnode + vol->root_vnode.magic = VNODE_MAGIC; + vol->root_vnode.vnid = vol->root_vnode.dir_vnid = GENERATE_DIR_CLUSTER_VNID(vol->root_vnode.cluster,vol->root_vnode.cluster); + vol->root_vnode.sindex = vol->root_vnode.eindex = 0xffffffff; + vol->root_vnode.mode = FAT_SUBDIR; + time(&(vol->root_vnode.st_time)); + vol->root_vnode.mime = NULL; + vol->root_vnode.dirty = false; + dlist_add(vol, vol->root_vnode.vnid); + + // find volume label (supercedes any label in the bpb) + { + struct diri diri; + uint8 *buffer; + buffer = diri_init(vol, vol->root_vnode.cluster, 0, &diri); + for (;buffer;buffer=diri_next_entry(&diri)) { + if ((buffer[0x0b] & FAT_VOLUME) && (buffer[0x0b] != 0xf) && (buffer[0] != 0xe5)) { + vol->vol_entry = diri.current_index; + memcpy(vol->vol_label, buffer, 11); + break; + } + } + diri_free(&diri); + } + + DPRINTF(0, ("root vnode id = %Lx\n", vol->root_vnode.vnid)); + DPRINTF(0, ("volume label [%11.11s] (%lx)\n", vol->vol_label, vol->vol_entry)); + + // steal a trick from bfs + if (!memcmp(vol->vol_label, "__RO__ ", 11)) { + vol->flags |= B_FS_IS_READONLY; + } + + *newVol = vol; + return B_NO_ERROR; + +error3: + dlist_uninit(vol); +error2: + uninit_vcache(vol); +error1: + remove_cached_device_blocks(vol->fd, NO_WRITES); +error: + if (!(vol->flags & B_FS_IS_READONLY) && (vol->flags & B_FS_IS_REMOVABLE) && (vol->fs_flags & FS_FLAGS_LOCK_DOOR)) + lock_removable_device(vol->fd, false); +error0: + close(vol->fd); + free(vol); + return (err >= B_NO_ERROR) ? EINVAL : err; +} + +static int dosfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms, + size_t len, void **data, vnode_id *vnid) +{ + int result; + nspace *vol; + void *handle; + int op_sync_mode; + int fs_flags = 0; + + handle = load_driver_settings("dos"); + debug_attr = strtoul(get_driver_parameter(handle, "debug_attr", "0", "0"), NULL, 0); + debug_dir = strtoul(get_driver_parameter(handle, "debug_dir", "0", "0"), NULL, 0); + debug_dlist = strtoul(get_driver_parameter(handle, "debug_dlist", "0", "0"), NULL, 0); + debug_dosfs = strtoul(get_driver_parameter(handle, "debug_dosfs", "0", "0"), NULL, 0); + debug_encodings = strtoul(get_driver_parameter(handle, "debug_encodings", "0", "0"), NULL, 0); + debug_fat = strtoul(get_driver_parameter(handle, "debug_fat", "0", "0"), NULL, 0); + debug_file = strtoul(get_driver_parameter(handle, "debug_file", "0", "0"), NULL, 0); + debug_iter = strtoul(get_driver_parameter(handle, "debug_iter", "0", "0"), NULL, 0); + debug_vcache = strtoul(get_driver_parameter(handle, "debug_vcache", "0", "0"), NULL, 0); + + op_sync_mode = strtoul(get_driver_parameter(handle, "op_sync_mode", "0", "0"), NULL, 0); + if (op_sync_mode < 0 || op_sync_mode > 2) { + op_sync_mode = 0; + } + if (strcasecmp(get_driver_parameter(handle, "lock_device", "true", "true"), "false") == 0) { + dprintf("dosfs: mounted with lock_device = false\n"); + } else { + dprintf("dosfs: mounted with lock_device = true\n"); + fs_flags |= FS_FLAGS_LOCK_DOOR; + } + + unload_driver_settings(handle); + + /* parms and len are command line options; dosfs doesn't use any so + we can ignore these arguments */ + TOUCH(parms); TOUCH(len); + +#if __RO__ + // make it read-only + flags |= 1; +#endif + + if (data == NULL) { + dprintf("dosfs_mount passed NULL data pointer\n"); + return EINVAL; + } + + // Try and mount volume as a FAT volume + if ((result = mount_fat_disk(device, nsid, flags, &vol, fs_flags, op_sync_mode)) == B_NO_ERROR) { + char name[32]; + + if (check_nspace_magic(vol, "dosfs_mount")) return EINVAL; + + *vnid = vol->root_vnode.vnid; + *data = (void*)vol; + + // You MUST do this. Create the vnode for the root. + result = new_vnode(nsid, *vnid, (void*)&(vol->root_vnode)); + if (result != B_NO_ERROR) { + dprintf("error creating new vnode (%s)\n", strerror(result)); + goto error; + } + sprintf(name, "fat lock %lx", vol->id); + if ((result = new_lock(&(vol->vlock), name)) != 0) { + dprintf("error creating lock (%s)\n", strerror(result)); + goto error; + } + +#if DEBUG + load_driver_symbols("dos"); + + if (atomic_add(&instances, 1) == 0) { + add_debugger_command("dos", debug_dos, "dump a dos nspace structure"); + add_debugger_command("dvnode", debug_dvnode, "dump a dos vnode structure"); + add_debugger_command("dfvnid", debug_dfvnid, "find a vnid in the vnid cache"); + add_debugger_command("dfloc", debug_dfloc, "find a loc in the vnid cache"); + add_debugger_command("dc2s", debug_dc2s, "calculate sector for cluster"); + } +#endif + } + + return result; + +error: + remove_cached_device_blocks(vol->fd, NO_WRITES); + dlist_uninit(vol); + uninit_vcache(vol); + free(vol); + return EINVAL; +} + +static void update_fsinfo(nspace *vol) +{ + if ((vol->fat_bits == 32) && (vol->fsinfo_sector != 0xffff) && + ((vol->flags & B_FS_IS_READONLY) == false)) { + uchar *buffer; + if ((buffer = (uchar *)get_block(vol->fd, vol->fsinfo_sector, vol->bytes_per_sector)) != NULL) { + if ((read32(buffer,0) == 0x41615252) && (read32(buffer,0x1e4) == 0x61417272) && (read16(buffer,0x1fe) == 0xaa55)) { + //number of free clusters + buffer[0x1e8] = (vol->free_clusters & 0xff); + buffer[0x1e9] = ((vol->free_clusters >> 8) & 0xff); + buffer[0x1ea] = ((vol->free_clusters >> 16) & 0xff); + buffer[0x1eb] = ((vol->free_clusters >> 24) & 0xff); + //cluster number of most recently allocated cluster + buffer[0x1ec] = (vol->last_allocated & 0xff); + buffer[0x1ed] = ((vol->last_allocated >> 8) & 0xff); + buffer[0x1ee] = ((vol->last_allocated >> 16) & 0xff); + buffer[0x1ef] = ((vol->last_allocated >> 24) & 0xff); + mark_blocks_dirty(vol->fd, vol->fsinfo_sector, 1); + } else { + dprintf("update_fsinfo: fsinfo block has invalid magic number\n"); + } + release_block(vol->fd, vol->fsinfo_sector); + } else { + dprintf("update_fsinfo: error getting fsinfo sector %x\n", vol->fsinfo_sector); + } + } +} + +static status_t get_fsinfo(nspace *vol, uint32 *free_count, uint32 *last_allocated) +{ + uchar *buffer; + int32 result; + + if ((vol->fat_bits != 32) || (vol->fsinfo_sector == 0xffff)) + return B_ERROR; + + if ((buffer = (uchar *)get_block(vol->fd, vol->fsinfo_sector, vol->bytes_per_sector)) == NULL) { + dprintf("get_fsinfo: error getting fsinfo sector %x\n", vol->fsinfo_sector); + return EIO; + } + + if ((read32(buffer,0) == 0x41615252) && (read32(buffer,0x1e4) == 0x61417272) && (read16(buffer,0x1fe) == 0xaa55)) { + *free_count = read32(buffer,0x1e8); + *last_allocated = read32(buffer,0x1ec); + result = B_OK; + } else { + dprintf("get_fsinfo: fsinfo block has invalid magic number\n"); + result = B_ERROR; + } + + release_block(vol->fd, vol->fsinfo_sector); + return result; +} + +static int dosfs_unmount(void *_vol) +{ + int result = B_NO_ERROR; + + nspace* vol = (nspace*)_vol; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_unmount")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_unmount volume %lx\n", vol->id)); + + update_fsinfo(vol); + flush_device(vol->fd, 0); + remove_cached_device_blocks(vol->fd, ALLOW_WRITES); + +#if DEBUG + if (atomic_add(&instances, -1) == 1) { + remove_debugger_command("dos", debug_dos); + remove_debugger_command("dvnode", debug_dvnode); + remove_debugger_command("dfvnid", debug_dfvnid); + remove_debugger_command("dfloc", debug_dfloc); + remove_debugger_command("dc2s", debug_dc2s); + } +#endif + + dlist_uninit(vol); + uninit_vcache(vol); + + if (!(vol->flags & B_FS_IS_READONLY) && (vol->flags & B_FS_IS_REMOVABLE) && (vol->fs_flags & FS_FLAGS_LOCK_DOOR)) + lock_removable_device(vol->fd, false); + result = close(vol->fd); + free_lock(&(vol->vlock)); + vol->magic = ~VNODE_MAGIC; + free(vol); + +#if USE_DMALLOC + check_mem(); +#endif + + return result; +} + +// dosfs_rfsstat - Fill in fs_info struct for device. +static int dosfs_rfsstat(void *_vol, struct fs_info * fss) +{ + nspace* vol = (nspace*)_vol; + int i; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_rfsstat")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(1, ("dosfs_rfsstat called\n")); + + // fss->dev and fss->root filled in by kernel + + // File system flags. + fss->flags = vol->flags; + + // FS block size. + fss->block_size = vol->bytes_per_sector * vol->sectors_per_cluster; + + // IO size - specifies buffer size for file copying + fss->io_size = 65536; + + // Total blocks + fss->total_blocks = vol->total_clusters; + + // Free blocks + fss->free_blocks = vol->free_clusters; + + // Device name. + strncpy(fss->device_name, vol->device, sizeof(fss->device_name)); + + if (vol->vol_entry > -2) + strncpy(fss->volume_name, vol->vol_label, sizeof(fss->volume_name)); + else + strcpy(fss->volume_name, "no name "); + + // XXX: should sanitize name as well + for (i=10;i>0;i--) + if (fss->volume_name[i] != ' ') + break; + fss->volume_name[i+1] = 0; + for (;i>=0;i--) { + if ((fss->volume_name[i] >= 'A') && (fss->volume_name[i] <= 'Z')) + fss->volume_name[i] += 'a' - 'A'; + } + + // File system name + strcpy(fss->fsh_name, "fat"); + + UNLOCK_VOL(vol); + + return 0; +} + +static int dosfs_wfsstat(void *_vol, struct fs_info * fss, long mask) +{ + int result = B_ERROR; + nspace* vol = (nspace*)_vol; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_wfsstat")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_wfsstat called\n")); + + /* if it's a r/o file system and not the special hack, then don't allow + * volume renaming */ + if ((vol->flags & B_FS_IS_READONLY) && memcmp(vol->vol_label, "__RO__ ", 11)) { + UNLOCK_VOL(vol); + return EROFS; + } + + if (mask & WFSSTAT_NAME) { + // sanitize name + char name[11]; + int i,j; + memset(name, ' ', 11); + DPRINTF(1, ("wfsstat: setting name to %s\n", fss->volume_name)); + for (i=j=0;(i<11)&&(fss->volume_name[j]);j++) { + static char acceptable[] = "!#$%&'()-0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`{}~"; + char c = fss->volume_name[j]; + if ((c >= 'a') && (c <= 'z')) c += 'A' - 'a'; + // spaces acceptable in volume names + if (strchr(acceptable, c) || (c == ' ')) + name[i++] = c; + } + if (i == 0) { // bad name, kiddo + result = EINVAL; + goto bi; + } + DPRINTF(1, ("wfsstat: sanitized to [%11.11s]\n", name)); + + if (vol->vol_entry == -1) { + // stored in the bpb + uchar *buffer; + if ((buffer = get_block(vol->fd, 0, vol->bytes_per_sector)) == NULL) { + result = EIO; + goto bi; + } + if ((buffer[0x26] != 0x29) || memcmp(buffer + 0x2b, vol->vol_label, 11)) { + dprintf("dosfs_wfsstat: label mismatch\n"); + result = B_ERROR; + } else { + memcpy(buffer + 0x2b, name, 11); + mark_blocks_dirty(vol->fd, 0, 1); + result = 0; + } + release_block(vol->fd, 0); + } else if (vol->vol_entry >= 0) { + struct diri diri; + uint8 *buffer; + buffer = diri_init(vol, vol->root_vnode.cluster, vol->vol_entry, &diri); + + // check if it is the same as the old volume label + if ((buffer == NULL) || (memcmp(buffer, vol->vol_label, 11))) { + dprintf("dosfs_wfsstat: label mismatch\n"); + diri_free(&diri); + result = B_ERROR; + goto bi; + } + memcpy(buffer, name, 11); + diri_mark_dirty(&diri); + diri_free(&diri); + result = 0; + } else { + uint32 index; + result = create_volume_label(vol, name, &index); + if (result == B_OK) vol->vol_entry = index; + } + + if (result == 0) + memcpy(vol->vol_label, name, 11); + } + + if (vol->fs_flags & FS_FLAGS_OP_SYNC) + _dosfs_sync(vol); + +bi: UNLOCK_VOL(vol); + + return result; +} + +static int dosfs_ioctl(void *_vol, void *_node, void *cookie, int code, + void *buf, size_t len) +{ + status_t result = B_OK; + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + + TOUCH(cookie); TOUCH(buf); TOUCH(len); + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_ioctl") || + check_vnode_magic(node, "dosfs_ioctl")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + switch (code) { + case 10002 : /* return real creation time */ + if (buf) *(bigtime_t *)buf = node->st_time; + break; + case 10003 : /* return real last modification time */ + if (buf) *(bigtime_t *)buf = node->st_time; + break; + + case 69666 : + result = fragment(vol, buf); + break; + + case 100000 : + dprintf("built at %s on %s\n", build_time, build_date); + dprintf("vol info: %s (device %x, media descriptor %x)\n", vol->device, vol->fd, vol->media_descriptor); + dprintf("%lx bytes/sector, %lx sectors/cluster\n", vol->bytes_per_sector, vol->sectors_per_cluster); + dprintf("%lx reserved sectors, %lx total sectors\n", vol->reserved_sectors, vol->total_sectors); + dprintf("%lx %d-bit fats, %lx sectors/fat, %lx root entries\n", vol->fat_count, vol->fat_bits, vol->sectors_per_fat, vol->root_entries_count); + dprintf("root directory starts at sector %lx (cluster %lx), data at sector %lx\n", vol->root_start, vol->root_vnode.cluster, vol->data_start); + dprintf("%lx total clusters, %lx free\n", vol->total_clusters, vol->free_clusters); + dprintf("fat mirroring is %s, fs info sector at sector %x\n", (vol->fat_mirrored) ? "on" : "off", vol->fsinfo_sector); + dprintf("last allocated cluster = %lx\n", vol->last_allocated); + dprintf("root vnode id = %Lx\n", vol->root_vnode.vnid); + dprintf("volume label [%11.11s]\n", vol->vol_label); + break; + + case 100001 : + dprintf("vnode id %Lx, dir vnid = %Lx\n", node->vnid, node->dir_vnid); + dprintf("si = %lx, ei = %lx\n", node->sindex, node->eindex); + dprintf("cluster = %lx (%lx), mode = %lx, size = %Lx\n", node->cluster, vol->data_start + vol->sectors_per_cluster * (node->cluster - 2), node->mode, node->st_size); + dprintf("mime = %s\n", node->mime ? node->mime : "(null)"); + dump_fat_chain(vol, node->cluster); + break; + + case 100002 : + {struct diri diri; + uint8 *buffer; + uint32 i; + for (i=0,buffer=diri_init(vol,node->cluster, 0, &diri);buffer;buffer=diri_next_entry(&diri),i++) { + if (buffer[0] == 0) break; + dprintf("entry %lx:\n", i); + dump_directory(buffer); + } + diri_free(&diri);} + break; + + case 100003 : + dprintf("vcache validation not yet implemented\n"); +#if 0 + dprintf("validating vcache for %lx\n", vol->id); + validate_vcache(vol); + dprintf("validation complete for %lx\n", vol->id); +#endif + break; + + case 100004 : + dprintf("dumping vcache for %lx\n", vol->id); + dump_vcache(vol); + break; + + case 100005 : + dprintf("dumping dlist for %lx\n", vol->id); + dlist_dump(vol); + break; + + default : + dprintf("dosfs_ioctl: vol %lx, vnode %Lx code = %d\n", vol->id, node->vnid, code); + result = EINVAL; + break; + } + + UNLOCK_VOL(vol); + + return result; +} + +int _dosfs_sync(nspace *vol) +{ + if (check_nspace_magic(vol, "dosfs_sync")) + return EINVAL; + + update_fsinfo(vol); + flush_device(vol->fd, 0); + + return 0; +} + +static int dosfs_sync(void *_vol) +{ + nspace *vol = (nspace *)_vol; + int err; + + DPRINTF(0, ("dosfs_sync called on volume %lx\n", vol->id)); + + LOCK_VOL(vol); + err = _dosfs_sync(vol); + UNLOCK_VOL(vol); + + return err; +} + +static int dosfs_fsync(void *vol, void *node) +{ + TOUCH(node); + + return dosfs_sync(vol); +} + +vnode_ops fs_entry = { + &dosfs_read_vnode, + &dosfs_write_vnode, + &dosfs_remove_vnode, + NULL, + &dosfs_walk, + &dosfs_access, + &dosfs_create, + &dosfs_mkdir, + NULL, + NULL, + &dosfs_rename, + &dosfs_unlink, + &dosfs_rmdir, + &dosfs_readlink, + &dosfs_opendir, + &dosfs_closedir, + &dosfs_free_dircookie, + &dosfs_rewinddir, + &dosfs_readdir, + &dosfs_open, + &dosfs_close, + &dosfs_free_cookie, + &dosfs_read, + &dosfs_write, + NULL, + NULL, + &dosfs_ioctl, + NULL, + &dosfs_rstat, + &dosfs_wstat, + &dosfs_fsync, + NULL, + &dosfs_mount, + &dosfs_unmount, + &dosfs_sync, + &dosfs_rfsstat, + &dosfs_wfsstat, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &dosfs_open_attrdir, + &dosfs_close_attrdir, + &dosfs_free_attrcookie, + &dosfs_rewind_attrdir, + &dosfs_read_attrdir, + &dosfs_write_attr, + &dosfs_read_attr, + NULL, + NULL, + &dosfs_stat_attr, + NULL, + NULL, + NULL, + NULL +}; + +int32 api_version = B_CUR_FS_API_VERSION; diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.h b/src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.h new file mode 100644 index 0000000000..8dcba9b644 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/dosfs.h @@ -0,0 +1,211 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_H_ +#define _DOSFS_H_ + +#if USE_DMALLOC + #include +#else + +/* allocate memory from swappable heap */ +#define malloc smalloc +#define free sfree +#define calloc scalloc +#define realloc srealloc + +#include + +#endif + +#include + +/* for multiple reader/single writer locks */ +#define READERS 100000 + +/* Unfortunately, vnode_id's are defined as signed. This causes problems with + * programs (notably cp) that use the modulo of a vnode_id (or ino_t) as a + * hash function to index an array. This means the high bit of every vnode_id + * is off-limits. Luckily, FAT32 is actually FAT28, so dosfs can make do with + * only 63 bits. + */ +#define ARTIFICIAL_VNID_BITS (0x6LL << 60) +#define DIR_CLUSTER_VNID_BITS (0x4LL << 60) +#define DIR_INDEX_VNID_BITS 0 +#define INVALID_VNID_BITS_MASK (0x9LL << 60) + +#define IS_DIR_CLUSTER_VNID(vnid) \ + (((vnid) & ARTIFICIAL_VNID_BITS) == DIR_CLUSTER_VNID_BITS) + +#define IS_DIR_INDEX_VNID(vnid) \ + (((vnid) & ARTIFICIAL_VNID_BITS) == DIR_INDEX_VNID_BITS) + +#define IS_ARTIFICIAL_VNID(vnid) \ + (((vnid) & ARTIFICIAL_VNID_BITS) == ARTIFICIAL_VNID_BITS) + +#define IS_INVALID_VNID(vnid) \ + ((!IS_DIR_CLUSTER_VNID((vnid)) && \ + !IS_DIR_INDEX_VNID((vnid)) && \ + !IS_ARTIFICIAL_VNID((vnid))) || \ + ((vnid) & INVALID_VNID_BITS_MASK)) + +#define GENERATE_DIR_INDEX_VNID(dircluster, index) \ + (DIR_INDEX_VNID_BITS | ((vnode_id)(dircluster) << 32) | (index)) + +#define GENERATE_DIR_CLUSTER_VNID(dircluster, filecluster) \ + (DIR_CLUSTER_VNID_BITS | ((vnode_id)(dircluster) << 32) | (filecluster)) + +#define CLUSTER_OF_DIR_CLUSTER_VNID(vnid) \ + ((uint32)((vnid) & 0xffffffff)) + +#define INDEX_OF_DIR_INDEX_VNID(vnid) \ + ((uint32)((vnid) & 0xffffffff)) + +#define DIR_OF_VNID(vnid) \ + ((uint32)(((vnid) >> 32) & ~0xf0000000)) + +#define VNODE_PARENT_DIR_CLUSTER(vnode) \ + CLUSTER_OF_DIR_CLUSTER_VNID((vnode)->dir_vnid) + +#include + +#define VNODE_MAGIC 'treB' + +typedef struct vnode +{ + uint32 magic; + vnode_id vnid; // self id + vnode_id dir_vnid; // parent vnode id (directory containing entry) + + uint32 disk_image; // 0 = no, 1 = BEOS, 2 = IMAGE.BE + + /* iteration is incremented each time the fat chain changes. it's used by + * the file read/write code to determine if it needs to retraverse the + * fat chain + */ + uint32 iteration; + + /* any changes to this block of information should immediately be reflected + * on the disk (or at least in the cache) so that get_next_dirent continues + * to function properly + */ + uint32 sindex, eindex; // starting and ending index of directory entry + uint32 cluster; // starting cluster of the data + uint32 mode; // dos-style attributes + off_t st_size; // in bytes + time_t st_time; + + uint32 end_cluster; // last cluster of the data + + const char *mime; // mime type (null if none) + + bool dirty; // track if vnode had been written to + +#if TRACK_FILENAME + char *filename; +#endif +} vnode; + +// mode bits +#define FAT_READ_ONLY 1 +#define FAT_HIDDEN 2 +#define FAT_SYSTEM 4 +#define FAT_VOLUME 8 +#define FAT_SUBDIR 16 +#define FAT_ARCHIVE 32 + +#define NSPACE_MAGIC 'smaI' + +struct vcache_entry; + +typedef struct _nspace +{ + uint32 magic; + nspace_id id; // ID passed in to fs_mount + int fd; // File descriptor + char device[256]; + uint32 flags; // see for modes + + // info from bpb + uint32 bytes_per_sector; + uint32 sectors_per_cluster; + uint32 reserved_sectors; + uint32 fat_count; + uint32 root_entries_count; + uint32 total_sectors; + uint32 sectors_per_fat; + uint8 media_descriptor; + uint16 fsinfo_sector; + + uint32 total_clusters; // data clusters, that is + uint32 free_clusters; + uint8 fat_bits; + bool fat_mirrored; // true if fat mirroring on + uint8 active_fat; + + uint32 root_start; // for fat12 + fat16 only + uint32 root_sectors; // for fat12 + fat16 only + vnode root_vnode; // root directory + int32 vol_entry; // index in root directory + char vol_label[12]; // lfn's need not apply + + uint32 data_start; + uint32 last_allocated; // last allocated cluster + + vnode_id beos_vnid; // vnid of \BEOS directory + bool respect_disk_image; + + int fs_flags; // flags for this mount + + lock vlock; // volume lock + + // vcache state + struct { + sem_id vc_sem; + vnode_id cur_vnid; + uint32 cache_size; + struct vcache_entry **by_vnid, **by_loc; + } vcache; + + struct { + uint32 entries; + uint32 allocated; + vnode_id *vnid_list; + } dlist; +} nspace; + +#define FS_FLAGS_OP_SYNC 0x1 +#define FS_FLAGS_LOCK_DOOR 0x2 + +#define LOCK_VOL(vol) \ + if (vol == NULL) { dprintf("null vol\n"); return EINVAL; } else LOCK((vol)->vlock) + +#define UNLOCK_VOL(vol) \ + UNLOCK((vol)->vlock) + +#define CHECK_MAGIC(name,struc,magick) \ + int check_##name##_magic(struc *t, char *funcname) \ + { \ + if (t == NULL) { \ + dprintf("%s passed null " #name " pointer\n", funcname); \ + return EINVAL; \ + } else if (t->magic != magick) { \ + dprintf(#name " (%x) passed to %s has invalid magic number\n", (int)t, funcname); \ + return EINVAL; \ + } else \ + return 0; \ + } + +int check_vnode_magic(struct vnode *t, char *funcname); +int check_nspace_magic(struct _nspace *t, char *funcname); + +#define TOUCH(x) ((void)(x)) + +/* debug levels */ +extern int debug_attr, debug_dir, debug_dlist, debug_dosfs, debug_encodings, + debug_fat, debug_file, debug_iter, debug_vcache; + +int _dosfs_sync(nspace *vol); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/encodings.cpp b/src/tests/add-ons/kernel/file_systems/dos/r5/encodings.cpp new file mode 100644 index 0000000000..346a1121cb --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/encodings.cpp @@ -0,0 +1,1600 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include + +#include +#include +#include +#include + +#include "encodings.h" +extern "C" { +#include "util.h" +extern int debug_encodings; +} + +#define TOUCH(x) ((void)(x)) + +#ifdef USER + #include + #define dprintf printf + #undef DEBUG + int _assert_(char *,int,char *) {return 0;} +#endif + +#define DPRINTF(a,b) if (debug_encodings > (a)) dprintf b + +enum { + MS_DOS_CONVERSION = 1, + SJIS_CONVERSION +}; + +#define BEGINS_UTF8CHAR(byte) (((byte) & 0xc0) != 0x80) + +const uint16 msdostou[] = { +0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, +0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, +0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, +0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, +0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, +0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, +0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, +0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, +0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, +0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, +0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, +0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, +0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, +0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, +0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, +0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0, +0xFFFF +}; + +const uint16 sjis00tou[] = { +0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, +0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, +0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, +0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, +0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, +0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C/*0x00A5*/, 0x005D, 0x005E, 0x005F, +0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, +0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E /*??0x203E??*/, 0x007F, +0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, +0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, +0x00A0, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, +0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, +0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, +0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, +0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, +0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, +0xFFFF +}; + +const uint16 sjis81tou[] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x3000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A, 0xFF1B, 0xFF1F, 0xFF01, 0x309B, 0x309C, 0x00B4, 0xFF40, 0x00A8, 0xFF3E, +0xFFE3, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003, 0x4EDD, 0x3005, 0x3006, 0x3007, 0x30FC, 0x2015, 0x2010, 0xFF0F, 0x005C, +0xFF5E, 0x2016, 0xFF5C, 0x2026, 0x2025, 0x2018, 0x2019, 0x201C, 0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D, 0xFF5B, +0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F, 0x3010, 0x3011, 0xFF0B, 0x002D /*??0x2212??*/, 0x00B1, 0x00D7, 0x0000, +0x00F7, 0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E, 0x2234, 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFFE5, +0xFF04, 0x00A2, 0x00A3, 0xFF05, 0xFF03, 0xFF06, 0xFF0A, 0xFF20, 0x00A7, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7, 0x25C6, +0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC, 0x203B, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283, 0x222A, 0x2229, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2227, 0x2228, 0x00AC, 0x21D2, 0x21D4, 0x2200, 0x2203, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2220, 0x22A5, 0x2312, 0x2202, 0x2207, 0x2261, +0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D, 0x2235, 0x222B, 0x222C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020, 0x2021, 0x00B6, 0x0000, 0x0000, 0x0000, 0x0000, 0x25EF, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFF10, +0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, +0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, +0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0x0000, 0x0000, 0x0000, 0x0000, 0x3041, +0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F, 0x3050, 0x3051, +0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F, 0x3060, 0x3061, +0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067, 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070, 0x3071, +0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F, 0x3080, 0x3081, +0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091, +0x3092, 0x3093, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7, 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF, 0x30B0, +0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7, 0x30B8, 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF, 0x30C0, +0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7, 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D0, +0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7, 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF, 0x0000, +0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, +0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0391, +0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, +0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03B1, +0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, +0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, +0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, +0x042F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x0000, +0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, +0x044E, 0x044F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2500, +0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513, 0x251B, 0x2517, +0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520, 0x252F, 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538, 0x2542, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4E9C, +0x5516, 0x5A03, 0x963F, 0x54C0, 0x611B, 0x6328, 0x59F6, 0x9022, 0x8475, 0x831C, 0x7A50, 0x60AA, 0x63E1, 0x6E25, 0x65ED, 0x8466, +0x82A6, 0x9BF5, 0x6893, 0x5727, 0x65A1, 0x6271, 0x5B9B, 0x59D0, 0x867B, 0x98F4, 0x7D62, 0x7DBE, 0x9B8E, 0x6216, 0x7C9F, 0x88B7, +0x5B89, 0x5EB5, 0x6309, 0x6697, 0x6848, 0x95C7, 0x978D, 0x674F, 0x4EE5, 0x4F0A, 0x4F4D, 0x4F9D, 0x5049, 0x56F2, 0x5937, 0x59D4, +0x5A01, 0x5C09, 0x60DF, 0x610F, 0x6170, 0x6613, 0x6905, 0x70BA, 0x754F, 0x7570, 0x79FB, 0x7DAD, 0x7DEF, 0x80C3, 0x840E, 0x8863, +0x8B02, 0x9055, 0x907A, 0x533B, 0x4E95, 0x4EA5, 0x57DF, 0x80B2, 0x90C1, 0x78EF, 0x4E00, 0x58F1, 0x6EA2, 0x9038, 0x7A32, 0x8328, +0x828B, 0x9C2F, 0x5141, 0x5370, 0x54BD, 0x54E1, 0x56E0, 0x59FB, 0x5F15, 0x98F2, 0x6DEB, 0x80E4, 0x852D, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x9662, 0x9670, 0x96A0, 0x97FB, 0x540B, 0x53F3, 0x5B87, 0x70CF, 0x7FBD, 0x8FC2, 0x96E8, 0x536F, 0x9D5C, 0x7ABA, 0x4E11, 0x7893, +0x81FC, 0x6E26, 0x5618, 0x5504, 0x6B1D, 0x851A, 0x9C3B, 0x59E5, 0x53A9, 0x6D66, 0x74DC, 0x958F, 0x5642, 0x4E91, 0x904B, 0x96F2, +0x834F, 0x990C, 0x53E1, 0x55B6, 0x5B30, 0x5F71, 0x6620, 0x66F3, 0x6804, 0x6C38, 0x6CF3, 0x6D29, 0x745B, 0x76C8, 0x7A4E, 0x9834, +0x82F1, 0x885B, 0x8A60, 0x92ED, 0x6DB2, 0x75AB, 0x76CA, 0x99C5, 0x60A6, 0x8B01, 0x8D8A, 0x95B2, 0x698E, 0x53AD, 0x5186, 0x0000, +0x5712, 0x5830, 0x5944, 0x5BB4, 0x5EF6, 0x6028, 0x63A9, 0x63F4, 0x6CBF, 0x6F14, 0x708E, 0x7114, 0x7159, 0x71D5, 0x733F, 0x7E01, +0x8276, 0x82D1, 0x8597, 0x9060, 0x925B, 0x9D1B, 0x5869, 0x65BC, 0x6C5A, 0x7525, 0x51F9, 0x592E, 0x5965, 0x5F80, 0x5FDC, 0x62BC, +0x65FA, 0x6A2A, 0x6B27, 0x6BB4, 0x738B, 0x7FC1, 0x8956, 0x9D2C, 0x9D0E, 0x9EC4, 0x5CA1, 0x6C96, 0x837B, 0x5104, 0x5C4B, 0x61B6, +0x81C6, 0x6876, 0x7261, 0x4E59, 0x4FFA, 0x5378, 0x6069, 0x6E29, 0x7A4F, 0x97F3, 0x4E0B, 0x5316, 0x4EEE, 0x4F55, 0x4F3D, 0x4FA1, +0x4F73, 0x52A0, 0x53EF, 0x5609, 0x590F, 0x5AC1, 0x5BB6, 0x5BE1, 0x79D1, 0x6687, 0x679C, 0x67B6, 0x6B4C, 0x6CB3, 0x706B, 0x73C2, +0x798D, 0x79BE, 0x7A3C, 0x7B87, 0x82B1, 0x82DB, 0x8304, 0x8377, 0x83EF, 0x83D3, 0x8766, 0x8AB2, 0x5629, 0x8CA8, 0x8FE6, 0x904E, +0x971E, 0x868A, 0x4FC4, 0x5CE8, 0x6211, 0x7259, 0x753B, 0x81E5, 0x82BD, 0x86FE, 0x8CC0, 0x96C5, 0x9913, 0x99D5, 0x4ECB, 0x4F1A, +0x89E3, 0x56DE, 0x584A, 0x58CA, 0x5EFB, 0x5FEB, 0x602A, 0x6094, 0x6062, 0x61D0, 0x6212, 0x62D0, 0x6539, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x9B41, 0x6666, 0x68B0, 0x6D77, 0x7070, 0x754C, 0x7686, 0x7D75, 0x82A5, 0x87F9, 0x958B, 0x968E, 0x8C9D, 0x51F1, 0x52BE, 0x5916, +0x54B3, 0x5BB3, 0x5D16, 0x6168, 0x6982, 0x6DAF, 0x788D, 0x84CB, 0x8857, 0x8A72, 0x93A7, 0x9AB8, 0x6D6C, 0x99A8, 0x86D9, 0x57A3, +0x67FF, 0x86CE, 0x920E, 0x5283, 0x5687, 0x5404, 0x5ED3, 0x62E1, 0x64B9, 0x683C, 0x6838, 0x6BBB, 0x7372, 0x78BA, 0x7A6B, 0x899A, +0x89D2, 0x8D6B, 0x8F03, 0x90ED, 0x95A3, 0x9694, 0x9769, 0x5B66, 0x5CB3, 0x697D, 0x984D, 0x984E, 0x639B, 0x7B20, 0x6A2B, 0x0000, +0x6A7F, 0x68B6, 0x9C0D, 0x6F5F, 0x5272, 0x559D, 0x6070, 0x62EC, 0x6D3B, 0x6E07, 0x6ED1, 0x845B, 0x8910, 0x8F44, 0x4E14, 0x9C39, +0x53F6, 0x691B, 0x6A3A, 0x9784, 0x682A, 0x515C, 0x7AC3, 0x84B2, 0x91DC, 0x938C, 0x565B, 0x9D28, 0x6822, 0x8305, 0x8431, 0x7CA5, +0x5208, 0x82C5, 0x74E6, 0x4E7E, 0x4F83, 0x51A0, 0x5BD2, 0x520A, 0x52D8, 0x52E7, 0x5DFB, 0x559A, 0x582A, 0x59E6, 0x5B8C, 0x5B98, +0x5BDB, 0x5E72, 0x5E79, 0x60A3, 0x611F, 0x6163, 0x61BE, 0x63DB, 0x6562, 0x67D1, 0x6853, 0x68FA, 0x6B3E, 0x6B53, 0x6C57, 0x6F22, +0x6F97, 0x6F45, 0x74B0, 0x7518, 0x76E3, 0x770B, 0x7AFF, 0x7BA1, 0x7C21, 0x7DE9, 0x7F36, 0x7FF0, 0x809D, 0x8266, 0x839E, 0x89B3, +0x8ACC, 0x8CAB, 0x9084, 0x9451, 0x9593, 0x9591, 0x95A2, 0x9665, 0x97D3, 0x9928, 0x8218, 0x4E38, 0x542B, 0x5CB8, 0x5DCC, 0x73A9, +0x764C, 0x773C, 0x5CA9, 0x7FEB, 0x8D0B, 0x96C1, 0x9811, 0x9854, 0x9858, 0x4F01, 0x4F0E, 0x5371, 0x559C, 0x5668, 0x57FA, 0x5947, +0x5B09, 0x5BC4, 0x5C90, 0x5E0C, 0x5E7E, 0x5FCC, 0x63EE, 0x673A, 0x65D7, 0x65E2, 0x671F, 0x68CB, 0x68C4, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x6A5F, 0x5E30, 0x6BC5, 0x6C17, 0x6C7D, 0x757F, 0x7948, 0x5B63, 0x7A00, 0x7D00, 0x5FBD, 0x898F, 0x8A18, 0x8CB4, 0x8D77, 0x8ECC, +0x8F1D, 0x98E2, 0x9A0E, 0x9B3C, 0x4E80, 0x507D, 0x5100, 0x5993, 0x5B9C, 0x622F, 0x6280, 0x64EC, 0x6B3A, 0x72A0, 0x7591, 0x7947, +0x7FA9, 0x87FB, 0x8ABC, 0x8B70, 0x63AC, 0x83CA, 0x97A0, 0x5409, 0x5403, 0x55AB, 0x6854, 0x6A58, 0x8A70, 0x7827, 0x6775, 0x9ECD, +0x5374, 0x5BA2, 0x811A, 0x8650, 0x9006, 0x4E18, 0x4E45, 0x4EC7, 0x4F11, 0x53CA, 0x5438, 0x5BAE, 0x5F13, 0x6025, 0x6551, 0x0000, +0x673D, 0x6C42, 0x6C72, 0x6CE3, 0x7078, 0x7403, 0x7A76, 0x7AAE, 0x7B08, 0x7D1A, 0x7CFE, 0x7D66, 0x65E7, 0x725B, 0x53BB, 0x5C45, +0x5DE8, 0x62D2, 0x62E0, 0x6319, 0x6E20, 0x865A, 0x8A31, 0x8DDD, 0x92F8, 0x6F01, 0x79A6, 0x9B5A, 0x4EA8, 0x4EAB, 0x4EAC, 0x4F9B, +0x4FA0, 0x50D1, 0x5147, 0x7AF6, 0x5171, 0x51F6, 0x5354, 0x5321, 0x537F, 0x53EB, 0x55AC, 0x5883, 0x5CE1, 0x5F37, 0x5F4A, 0x602F, +0x6050, 0x606D, 0x631F, 0x6559, 0x6A4B, 0x6CC1, 0x72C2, 0x72ED, 0x77EF, 0x80F8, 0x8105, 0x8208, 0x854E, 0x90F7, 0x93E1, 0x97FF, +0x9957, 0x9A5A, 0x4EF0, 0x51DD, 0x5C2D, 0x6681, 0x696D, 0x5C40, 0x66F2, 0x6975, 0x7389, 0x6850, 0x7C81, 0x50C5, 0x52E4, 0x5747, +0x5DFE, 0x9326, 0x65A4, 0x6B23, 0x6B3D, 0x7434, 0x7981, 0x79BD, 0x7B4B, 0x7DCA, 0x82B9, 0x83CC, 0x887F, 0x895F, 0x8B39, 0x8FD1, +0x91D1, 0x541F, 0x9280, 0x4E5D, 0x5036, 0x53E5, 0x533A, 0x72D7, 0x7396, 0x77E9, 0x82E6, 0x8EAF, 0x99C6, 0x99C8, 0x99D2, 0x5177, +0x611A, 0x865E, 0x55B0, 0x7A7A, 0x5076, 0x5BD3, 0x9047, 0x9685, 0x4E32, 0x6ADB, 0x91E7, 0x5C51, 0x5C48, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x6398, 0x7A9F, 0x6C93, 0x9774, 0x8F61, 0x7AAA, 0x718A, 0x9688, 0x7C82, 0x6817, 0x7E70, 0x6851, 0x936C, 0x52F2, 0x541B, 0x85AB, +0x8A13, 0x7FA4, 0x8ECD, 0x90E1, 0x5366, 0x8888, 0x7941, 0x4FC2, 0x50BE, 0x5211, 0x5144, 0x5553, 0x572D, 0x73EA, 0x578B, 0x5951, +0x5F62, 0x5F84, 0x6075, 0x6176, 0x6167, 0x61A9, 0x63B2, 0x643A, 0x656C, 0x666F, 0x6842, 0x6E13, 0x7566, 0x7A3D, 0x7CFB, 0x7D4C, +0x7D99, 0x7E4B, 0x7F6B, 0x830E, 0x834A, 0x86CD, 0x8A08, 0x8A63, 0x8B66, 0x8EFD, 0x981A, 0x9D8F, 0x82B8, 0x8FCE, 0x9BE8, 0x0000, +0x5287, 0x621F, 0x6483, 0x6FC0, 0x9699, 0x6841, 0x5091, 0x6B20, 0x6C7A, 0x6F54, 0x7A74, 0x7D50, 0x8840, 0x8A23, 0x6708, 0x4EF6, +0x5039, 0x5026, 0x5065, 0x517C, 0x5238, 0x5263, 0x55A7, 0x570F, 0x5805, 0x5ACC, 0x5EFA, 0x61B2, 0x61F8, 0x62F3, 0x6372, 0x691C, +0x6A29, 0x727D, 0x72AC, 0x732E, 0x7814, 0x786F, 0x7D79, 0x770C, 0x80A9, 0x898B, 0x8B19, 0x8CE2, 0x8ED2, 0x9063, 0x9375, 0x967A, +0x9855, 0x9A13, 0x9E78, 0x5143, 0x539F, 0x53B3, 0x5E7B, 0x5F26, 0x6E1B, 0x6E90, 0x7384, 0x73FE, 0x7D43, 0x8237, 0x8A00, 0x8AFA, +0x9650, 0x4E4E, 0x500B, 0x53E4, 0x547C, 0x56FA, 0x59D1, 0x5B64, 0x5DF1, 0x5EAB, 0x5F27, 0x6238, 0x6545, 0x67AF, 0x6E56, 0x72D0, +0x7CCA, 0x88B4, 0x80A1, 0x80E1, 0x83F0, 0x864E, 0x8A87, 0x8DE8, 0x9237, 0x96C7, 0x9867, 0x9F13, 0x4E94, 0x4E92, 0x4F0D, 0x5348, +0x5449, 0x543E, 0x5A2F, 0x5F8C, 0x5FA1, 0x609F, 0x68A7, 0x6A8E, 0x745A, 0x7881, 0x8A9E, 0x8AA4, 0x8B77, 0x9190, 0x4E5E, 0x9BC9, +0x4EA4, 0x4F7C, 0x4FAF, 0x5019, 0x5016, 0x5149, 0x516C, 0x529F, 0x52B9, 0x52FE, 0x539A, 0x53E3, 0x5411, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x540E, 0x5589, 0x5751, 0x57A2, 0x597D, 0x5B54, 0x5B5D, 0x5B8F, 0x5DE5, 0x5DE7, 0x5DF7, 0x5E78, 0x5E83, 0x5E9A, 0x5EB7, 0x5F18, +0x6052, 0x614C, 0x6297, 0x62D8, 0x63A7, 0x653B, 0x6602, 0x6643, 0x66F4, 0x676D, 0x6821, 0x6897, 0x69CB, 0x6C5F, 0x6D2A, 0x6D69, +0x6E2F, 0x6E9D, 0x7532, 0x7687, 0x786C, 0x7A3F, 0x7CE0, 0x7D05, 0x7D18, 0x7D5E, 0x7DB1, 0x8015, 0x8003, 0x80AF, 0x80B1, 0x8154, +0x818F, 0x822A, 0x8352, 0x884C, 0x8861, 0x8B1B, 0x8CA2, 0x8CFC, 0x90CA, 0x9175, 0x9271, 0x783F, 0x92FC, 0x95A4, 0x964D, 0x0000, +0x9805, 0x9999, 0x9AD8, 0x9D3B, 0x525B, 0x52AB, 0x53F7, 0x5408, 0x58D5, 0x62F7, 0x6FE0, 0x8C6A, 0x8F5F, 0x9EB9, 0x514B, 0x523B, +0x544A, 0x56FD, 0x7A40, 0x9177, 0x9D60, 0x9ED2, 0x7344, 0x6F09, 0x8170, 0x7511, 0x5FFD, 0x60DA, 0x9AA8, 0x72DB, 0x8FBC, 0x6B64, +0x9803, 0x4ECA, 0x56F0, 0x5764, 0x58BE, 0x5A5A, 0x6068, 0x61C7, 0x660F, 0x6606, 0x6839, 0x68B1, 0x6DF7, 0x75D5, 0x7D3A, 0x826E, +0x9B42, 0x4E9B, 0x4F50, 0x53C9, 0x5506, 0x5D6F, 0x5DE6, 0x5DEE, 0x67FB, 0x6C99, 0x7473, 0x7802, 0x8A50, 0x9396, 0x88DF, 0x5750, +0x5EA7, 0x632B, 0x50B5, 0x50AC, 0x518D, 0x6700, 0x54C9, 0x585E, 0x59BB, 0x5BB0, 0x5F69, 0x624D, 0x63A1, 0x683D, 0x6B73, 0x6E08, +0x707D, 0x91C7, 0x7280, 0x7815, 0x7826, 0x796D, 0x658E, 0x7D30, 0x83DC, 0x88C1, 0x8F09, 0x969B, 0x5264, 0x5728, 0x6750, 0x7F6A, +0x8CA1, 0x51B4, 0x5742, 0x962A, 0x583A, 0x698A, 0x80B4, 0x54B2, 0x5D0E, 0x57FC, 0x7895, 0x9DFA, 0x4F5C, 0x524A, 0x548B, 0x643E, +0x6628, 0x6714, 0x67F5, 0x7A84, 0x7B56, 0x7D22, 0x932F, 0x685C, 0x9BAD, 0x7B39, 0x5319, 0x518A, 0x5237, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x5BDF, 0x62F6, 0x64AE, 0x64E6, 0x672D, 0x6BBA, 0x85A9, 0x96D1, 0x7690, 0x9BD6, 0x634C, 0x9306, 0x9BAB, 0x76BF, 0x6652, 0x4E09, +0x5098, 0x53C2, 0x5C71, 0x60E8, 0x6492, 0x6563, 0x685F, 0x71E6, 0x73CA, 0x7523, 0x7B97, 0x7E82, 0x8695, 0x8B83, 0x8CDB, 0x9178, +0x9910, 0x65AC, 0x66AB, 0x6B8B, 0x4ED5, 0x4ED4, 0x4F3A, 0x4F7F, 0x523A, 0x53F8, 0x53F2, 0x55E3, 0x56DB, 0x58EB, 0x59CB, 0x59C9, +0x59FF, 0x5B50, 0x5C4D, 0x5E02, 0x5E2B, 0x5FD7, 0x601D, 0x6307, 0x652F, 0x5B5C, 0x65AF, 0x65BD, 0x65E8, 0x679D, 0x6B62, 0x0000, +0x6B7B, 0x6C0F, 0x7345, 0x7949, 0x79C1, 0x7CF8, 0x7D19, 0x7D2B, 0x80A2, 0x8102, 0x81F3, 0x8996, 0x8A5E, 0x8A69, 0x8A66, 0x8A8C, +0x8AEE, 0x8CC7, 0x8CDC, 0x96CC, 0x98FC, 0x6B6F, 0x4E8B, 0x4F3C, 0x4F8D, 0x5150, 0x5B57, 0x5BFA, 0x6148, 0x6301, 0x6642, 0x6B21, +0x6ECB, 0x6CBB, 0x723E, 0x74BD, 0x75D4, 0x78C1, 0x793A, 0x800C, 0x8033, 0x81EA, 0x8494, 0x8F9E, 0x6C50, 0x9E7F, 0x5F0F, 0x8B58, +0x9D2B, 0x7AFA, 0x8EF8, 0x5B8D, 0x96EB, 0x4E03, 0x53F1, 0x57F7, 0x5931, 0x5AC9, 0x5BA4, 0x6089, 0x6E7F, 0x6F06, 0x75BE, 0x8CEA, +0x5B9F, 0x8500, 0x7BE0, 0x5072, 0x67F4, 0x829D, 0x5C61, 0x854A, 0x7E1E, 0x820E, 0x5199, 0x5C04, 0x6368, 0x8D66, 0x659C, 0x716E, +0x793E, 0x7D17, 0x8005, 0x8B1D, 0x8ECA, 0x906E, 0x86C7, 0x90AA, 0x501F, 0x52FA, 0x5C3A, 0x6753, 0x707C, 0x7235, 0x914C, 0x91C8, +0x932B, 0x82E5, 0x5BC2, 0x5F31, 0x60F9, 0x4E3B, 0x53D6, 0x5B88, 0x624B, 0x6731, 0x6B8A, 0x72E9, 0x73E0, 0x7A2E, 0x816B, 0x8DA3, +0x9152, 0x9996, 0x5112, 0x53D7, 0x546A, 0x5BFF, 0x6388, 0x6A39, 0x7DAC, 0x9700, 0x56DA, 0x53CE, 0x5468, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x5B97, 0x5C31, 0x5DDE, 0x4FEE, 0x6101, 0x62FE, 0x6D32, 0x79C0, 0x79CB, 0x7D42, 0x7E4D, 0x7FD2, 0x81ED, 0x821F, 0x8490, 0x8846, +0x8972, 0x8B90, 0x8E74, 0x8F2F, 0x9031, 0x914B, 0x916C, 0x96C6, 0x919C, 0x4EC0, 0x4F4F, 0x5145, 0x5341, 0x5F93, 0x620E, 0x67D4, +0x6C41, 0x6E0B, 0x7363, 0x7E26, 0x91CD, 0x9283, 0x53D4, 0x5919, 0x5BBF, 0x6DD1, 0x795D, 0x7E2E, 0x7C9B, 0x587E, 0x719F, 0x51FA, +0x8853, 0x8FF0, 0x4FCA, 0x5CFB, 0x6625, 0x77AC, 0x7AE3, 0x821C, 0x99FF, 0x51C6, 0x5FAA, 0x65EC, 0x696F, 0x6B89, 0x6DF3, 0x0000, +0x6E96, 0x6F64, 0x76FE, 0x7D14, 0x5DE1, 0x9075, 0x9187, 0x9806, 0x51E6, 0x521D, 0x6240, 0x6691, 0x66D9, 0x6E1A, 0x5EB6, 0x7DD2, +0x7F72, 0x66F8, 0x85AF, 0x85F7, 0x8AF8, 0x52A9, 0x53D9, 0x5973, 0x5E8F, 0x5F90, 0x6055, 0x92E4, 0x9664, 0x50B7, 0x511F, 0x52DD, +0x5320, 0x5347, 0x53EC, 0x54E8, 0x5546, 0x5531, 0x5617, 0x5968, 0x59BE, 0x5A3C, 0x5BB5, 0x5C06, 0x5C0F, 0x5C11, 0x5C1A, 0x5E84, +0x5E8A, 0x5EE0, 0x5F70, 0x627F, 0x6284, 0x62DB, 0x638C, 0x6377, 0x6607, 0x660C, 0x662D, 0x6676, 0x677E, 0x68A2, 0x6A1F, 0x6A35, +0x6CBC, 0x6D88, 0x6E09, 0x6E58, 0x713C, 0x7126, 0x7167, 0x75C7, 0x7701, 0x785D, 0x7901, 0x7965, 0x79F0, 0x7AE0, 0x7B11, 0x7CA7, +0x7D39, 0x8096, 0x83D6, 0x848B, 0x8549, 0x885D, 0x88F3, 0x8A1F, 0x8A3C, 0x8A54, 0x8A73, 0x8C61, 0x8CDE, 0x91A4, 0x9266, 0x937E, +0x9418, 0x969C, 0x9798, 0x4E0A, 0x4E08, 0x4E1E, 0x4E57, 0x5197, 0x5270, 0x57CE, 0x5834, 0x58CC, 0x5B22, 0x5E38, 0x60C5, 0x64FE, +0x6761, 0x6756, 0x6D44, 0x72B6, 0x7573, 0x7A63, 0x84B8, 0x8B72, 0x91B8, 0x9320, 0x5631, 0x57F4, 0x98FE, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x62ED, 0x690D, 0x6B96, 0x71ED, 0x7E54, 0x8077, 0x8272, 0x89E6, 0x98DF, 0x8755, 0x8FB1, 0x5C3B, 0x4F38, 0x4FE1, 0x4FB5, 0x5507, +0x5A20, 0x5BDD, 0x5BE9, 0x5FC3, 0x614E, 0x632F, 0x65B0, 0x664B, 0x68EE, 0x699B, 0x6D78, 0x6DF1, 0x7533, 0x75B9, 0x771F, 0x795E, +0x79E6, 0x7D33, 0x81E3, 0x82AF, 0x85AA, 0x89AA, 0x8A3A, 0x8EAB, 0x8F9B, 0x9032, 0x91DD, 0x9707, 0x4EBA, 0x4EC1, 0x5203, 0x5875, +0x58EC, 0x5C0B, 0x751A, 0x5C3D, 0x814E, 0x8A0A, 0x8FC5, 0x9663, 0x976D, 0x7B25, 0x8ACF, 0x9808, 0x9162, 0x56F3, 0x53A8, 0x0000, +0x9017, 0x5439, 0x5782, 0x5E25, 0x63A8, 0x6C34, 0x708A, 0x7761, 0x7C8B, 0x7FE0, 0x8870, 0x9042, 0x9154, 0x9310, 0x9318, 0x968F, +0x745E, 0x9AC4, 0x5D07, 0x5D69, 0x6570, 0x67A2, 0x8DA8, 0x96DB, 0x636E, 0x6749, 0x6919, 0x83C5, 0x9817, 0x96C0, 0x88FE, 0x6F84, +0x647A, 0x5BF8, 0x4E16, 0x702C, 0x755D, 0x662F, 0x51C4, 0x5236, 0x52E2, 0x59D3, 0x5F81, 0x6027, 0x6210, 0x653F, 0x6574, 0x661F, +0x6674, 0x68F2, 0x6816, 0x6B63, 0x6E05, 0x7272, 0x751F, 0x76DB, 0x7CBE, 0x8056, 0x58F0, 0x88FD, 0x897F, 0x8AA0, 0x8A93, 0x8ACB, +0x901D, 0x9192, 0x9752, 0x9759, 0x6589, 0x7A0E, 0x8106, 0x96BB, 0x5E2D, 0x60DC, 0x621A, 0x65A5, 0x6614, 0x6790, 0x77F3, 0x7A4D, +0x7C4D, 0x7E3E, 0x810A, 0x8CAC, 0x8D64, 0x8DE1, 0x8E5F, 0x78A9, 0x5207, 0x62D9, 0x63A5, 0x6442, 0x6298, 0x8A2D, 0x7A83, 0x7BC0, +0x8AAC, 0x96EA, 0x7D76, 0x820C, 0x8749, 0x4ED9, 0x5148, 0x5343, 0x5360, 0x5BA3, 0x5C02, 0x5C16, 0x5DDD, 0x6226, 0x6247, 0x64B0, +0x6813, 0x6834, 0x6CC9, 0x6D45, 0x6D17, 0x67D3, 0x6F5C, 0x714E, 0x717D, 0x65CB, 0x7A7F, 0x7BAD, 0x7DDA, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x7E4A, 0x7FA8, 0x817A, 0x821B, 0x8239, 0x85A6, 0x8A6E, 0x8CCE, 0x8DF5, 0x9078, 0x9077, 0x92AD, 0x9291, 0x9583, 0x9BAE, 0x524D, +0x5584, 0x6F38, 0x7136, 0x5168, 0x7985, 0x7E55, 0x81B3, 0x7CCE, 0x564C, 0x5851, 0x5CA8, 0x63AA, 0x66FE, 0x66FD, 0x695A, 0x72D9, +0x758F, 0x758E, 0x790E, 0x7956, 0x79DF, 0x7C97, 0x7D20, 0x7D44, 0x8607, 0x8A34, 0x963B, 0x9061, 0x9F20, 0x50E7, 0x5275, 0x53CC, +0x53E2, 0x5009, 0x55AA, 0x58EE, 0x594F, 0x723D, 0x5B8B, 0x5C64, 0x531D, 0x60E3, 0x60F3, 0x635C, 0x6383, 0x633F, 0x63BB, 0x0000, +0x64CD, 0x65E9, 0x66F9, 0x5DE3, 0x69CD, 0x69FD, 0x6F15, 0x71E5, 0x4E89, 0x75E9, 0x76F8, 0x7A93, 0x7CDF, 0x7DCF, 0x7D9C, 0x8061, +0x8349, 0x8358, 0x846C, 0x84BC, 0x85FB, 0x88C5, 0x8D70, 0x9001, 0x906D, 0x9397, 0x971C, 0x9A12, 0x50CF, 0x5897, 0x618E, 0x81D3, +0x8535, 0x8D08, 0x9020, 0x4FC3, 0x5074, 0x5247, 0x5373, 0x606F, 0x6349, 0x675F, 0x6E2C, 0x8DB3, 0x901F, 0x4FD7, 0x5C5E, 0x8CCA, +0x65CF, 0x7D9A, 0x5352, 0x8896, 0x5176, 0x63C3, 0x5B58, 0x5B6B, 0x5C0A, 0x640D, 0x6751, 0x905C, 0x4ED6, 0x591A, 0x592A, 0x6C70, +0x8A51, 0x553E, 0x5815, 0x59A5, 0x60F0, 0x6253, 0x67C1, 0x8235, 0x6955, 0x9640, 0x99C4, 0x9A28, 0x4F53, 0x5806, 0x5BFE, 0x8010, +0x5CB1, 0x5E2F, 0x5F85, 0x6020, 0x614B, 0x6234, 0x66FF, 0x6CF0, 0x6EDE, 0x80CE, 0x817F, 0x82D4, 0x888B, 0x8CB8, 0x9000, 0x902E, +0x968A, 0x9EDB, 0x9BDB, 0x4EE3, 0x53F0, 0x5927, 0x7B2C, 0x918D, 0x984C, 0x9DF9, 0x6EDD, 0x7027, 0x5353, 0x5544, 0x5B85, 0x6258, +0x629E, 0x62D3, 0x6CA2, 0x6FEF, 0x7422, 0x8A17, 0x9438, 0x6FC1, 0x8AFE, 0x8338, 0x51E7, 0x86F8, 0x53EA, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x53E9, 0x4F46, 0x9054, 0x8FB0, 0x596A, 0x8131, 0x5DFD, 0x7AEA, 0x8FBF, 0x68DA, 0x8C37, 0x72F8, 0x9C48, 0x6A3D, 0x8AB0, 0x4E39, +0x5358, 0x5606, 0x5766, 0x62C5, 0x63A2, 0x65E6, 0x6B4E, 0x6DE1, 0x6E5B, 0x70AD, 0x77ED, 0x7AEF, 0x7BAA, 0x7DBB, 0x803D, 0x80C6, +0x86CB, 0x8A95, 0x935B, 0x56E3, 0x58C7, 0x5F3E, 0x65AD, 0x6696, 0x6A80, 0x6BB5, 0x7537, 0x8AC7, 0x5024, 0x77E5, 0x5730, 0x5F1B, +0x6065, 0x667A, 0x6C60, 0x75F4, 0x7A1A, 0x7F6E, 0x81F4, 0x8718, 0x9045, 0x99B3, 0x7BC9, 0x755C, 0x7AF9, 0x7B51, 0x84C4, 0x0000, +0x9010, 0x79E9, 0x7A92, 0x8336, 0x5AE1, 0x7740, 0x4E2D, 0x4EF2, 0x5B99, 0x5FE0, 0x62BD, 0x663C, 0x67F1, 0x6CE8, 0x866B, 0x8877, +0x8A3B, 0x914E, 0x92F3, 0x99D0, 0x6A17, 0x7026, 0x732A, 0x82E7, 0x8457, 0x8CAF, 0x4E01, 0x5146, 0x51CB, 0x558B, 0x5BF5, 0x5E16, +0x5E33, 0x5E81, 0x5F14, 0x5F35, 0x5F6B, 0x5FB4, 0x61F2, 0x6311, 0x66A2, 0x671D, 0x6F6E, 0x7252, 0x753A, 0x773A, 0x8074, 0x8139, +0x8178, 0x8776, 0x8ABF, 0x8ADC, 0x8D85, 0x8DF3, 0x929A, 0x9577, 0x9802, 0x9CE5, 0x52C5, 0x6357, 0x76F4, 0x6715, 0x6C88, 0x73CD, +0x8CC3, 0x93AE, 0x9673, 0x6D25, 0x589C, 0x690E, 0x69CC, 0x8FFD, 0x939A, 0x75DB, 0x901A, 0x585A, 0x6802, 0x63B4, 0x69FB, 0x4F43, +0x6F2C, 0x67D8, 0x8FBB, 0x8526, 0x7DB4, 0x9354, 0x693F, 0x6F70, 0x576A, 0x58F7, 0x5B2C, 0x7D2C, 0x722A, 0x540A, 0x91E3, 0x9DB4, +0x4EAD, 0x4F4E, 0x505C, 0x5075, 0x5243, 0x8C9E, 0x5448, 0x5824, 0x5B9A, 0x5E1D, 0x5E95, 0x5EAD, 0x5EF7, 0x5F1F, 0x608C, 0x62B5, +0x633A, 0x63D0, 0x68AF, 0x6C40, 0x7887, 0x798E, 0x7A0B, 0x7DE0, 0x8247, 0x8A02, 0x8AE6, 0x8E44, 0x9013, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x90B8, 0x912D, 0x91D8, 0x9F0E, 0x6CE5, 0x6458, 0x64E2, 0x6575, 0x6EF4, 0x7684, 0x7B1B, 0x9069, 0x93D1, 0x6EBA, 0x54F2, 0x5FB9, +0x64A4, 0x8F4D, 0x8FED, 0x9244, 0x5178, 0x586B, 0x5929, 0x5C55, 0x5E97, 0x6DFB, 0x7E8F, 0x751C, 0x8CBC, 0x8EE2, 0x985B, 0x70B9, +0x4F1D, 0x6BBF, 0x6FB1, 0x7530, 0x96FB, 0x514E, 0x5410, 0x5835, 0x5857, 0x59AC, 0x5C60, 0x5F92, 0x6597, 0x675C, 0x6E21, 0x767B, +0x83DF, 0x8CED, 0x9014, 0x90FD, 0x934D, 0x7825, 0x783A, 0x52AA, 0x5EA6, 0x571F, 0x5974, 0x6012, 0x5012, 0x515A, 0x51AC, 0x0000, +0x51CD, 0x5200, 0x5510, 0x5854, 0x5858, 0x5957, 0x5B95, 0x5CF6, 0x5D8B, 0x60BC, 0x6295, 0x642D, 0x6771, 0x6843, 0x68BC, 0x68DF, +0x76D7, 0x6DD8, 0x6E6F, 0x6D9B, 0x706F, 0x71C8, 0x5F53, 0x75D8, 0x7977, 0x7B49, 0x7B54, 0x7B52, 0x7CD6, 0x7D71, 0x5230, 0x8463, +0x8569, 0x85E4, 0x8A0E, 0x8B04, 0x8C46, 0x8E0F, 0x9003, 0x900F, 0x9419, 0x9676, 0x982D, 0x9A30, 0x95D8, 0x50CD, 0x52D5, 0x540C, +0x5802, 0x5C0E, 0x61A7, 0x649E, 0x6D1E, 0x77B3, 0x7AE5, 0x80F4, 0x8404, 0x9053, 0x9285, 0x5CE0, 0x9D07, 0x533F, 0x5F97, 0x5FB3, +0x6D9C, 0x7279, 0x7763, 0x79BF, 0x7BE4, 0x6BD2, 0x72EC, 0x8AAD, 0x6803, 0x6A61, 0x51F8, 0x7A81, 0x6934, 0x5C4A, 0x9CF6, 0x82EB, +0x5BC5, 0x9149, 0x701E, 0x5678, 0x5C6F, 0x60C7, 0x6566, 0x6C8C, 0x8C5A, 0x9041, 0x9813, 0x5451, 0x66C7, 0x920D, 0x5948, 0x90A3, +0x5185, 0x4E4D, 0x51EA, 0x8599, 0x8B0E, 0x7058, 0x637A, 0x934B, 0x6962, 0x99B4, 0x7E04, 0x7577, 0x5357, 0x6960, 0x8EDF, 0x96E3, +0x6C5D, 0x4E8C, 0x5C3C, 0x5F10, 0x8FE9, 0x5302, 0x8CD1, 0x8089, 0x8679, 0x5EFF, 0x65E5, 0x4E73, 0x5165, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x5982, 0x5C3F, 0x97EE, 0x4EFB, 0x598A, 0x5FCD, 0x8A8D, 0x6FE1, 0x79B0, 0x7962, 0x5BE7, 0x8471, 0x732B, 0x71B1, 0x5E74, 0x5FF5, +0x637B, 0x649A, 0x71C3, 0x7C98, 0x4E43, 0x5EFC, 0x4E4B, 0x57DC, 0x56A2, 0x60A9, 0x6FC3, 0x7D0D, 0x80FD, 0x8133, 0x81BF, 0x8FB2, +0x8997, 0x86A4, 0x5DF4, 0x628A, 0x64AD, 0x8987, 0x6777, 0x6CE2, 0x6D3E, 0x7436, 0x7834, 0x5A46, 0x7F75, 0x82AD, 0x99AC, 0x4FF3, +0x5EC3, 0x62DD, 0x6392, 0x6557, 0x676F, 0x76C3, 0x724C, 0x80CC, 0x80BA, 0x8F29, 0x914D, 0x500D, 0x57F9, 0x5A92, 0x6885, 0x0000, +0x6973, 0x7164, 0x72FD, 0x8CB7, 0x58F2, 0x8CE0, 0x966A, 0x9019, 0x877F, 0x79E4, 0x77E7, 0x8429, 0x4F2F, 0x5265, 0x535A, 0x62CD, +0x67CF, 0x6CCA, 0x767D, 0x7B94, 0x7C95, 0x8236, 0x8584, 0x8FEB, 0x66DD, 0x6F20, 0x7206, 0x7E1B, 0x83AB, 0x99C1, 0x9EA6, 0x51FD, +0x7BB1, 0x7872, 0x7BB8, 0x8087, 0x7B48, 0x6AE8, 0x5E61, 0x808C, 0x7551, 0x7560, 0x516B, 0x9262, 0x6E8C, 0x767A, 0x9197, 0x9AEA, +0x4F10, 0x7F70, 0x629C, 0x7B4F, 0x95A5, 0x9CE9, 0x567A, 0x5859, 0x86E4, 0x96BC, 0x4F34, 0x5224, 0x534A, 0x53CD, 0x53DB, 0x5E06, +0x642C, 0x6591, 0x677F, 0x6C3E, 0x6C4E, 0x7248, 0x72AF, 0x73ED, 0x7554, 0x7E41, 0x822C, 0x85E9, 0x8CA9, 0x7BC4, 0x91C6, 0x7169, +0x9812, 0x98EF, 0x633D, 0x6669, 0x756A, 0x76E4, 0x78D0, 0x8543, 0x86EE, 0x532A, 0x5351, 0x5426, 0x5983, 0x5E87, 0x5F7C, 0x60B2, +0x6249, 0x6279, 0x62AB, 0x6590, 0x6BD4, 0x6CCC, 0x75B2, 0x76AE, 0x7891, 0x79D8, 0x7DCB, 0x7F77, 0x80A5, 0x88AB, 0x8AB9, 0x8CBB, +0x907F, 0x975E, 0x98DB, 0x6A0B, 0x7C38, 0x5099, 0x5C3E, 0x5FAE, 0x6787, 0x6BD8, 0x7435, 0x7709, 0x7F8E, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x9F3B, 0x67CA, 0x7A17, 0x5339, 0x758B, 0x9AED, 0x5F66, 0x819D, 0x83F1, 0x8098, 0x5F3C, 0x5FC5, 0x7562, 0x7B46, 0x903C, 0x6867, +0x59EB, 0x5A9B, 0x7D10, 0x767E, 0x8B2C, 0x4FF5, 0x5F6A, 0x6A19, 0x6C37, 0x6F02, 0x74E2, 0x7968, 0x8868, 0x8A55, 0x8C79, 0x5EDF, +0x63CF, 0x75C5, 0x79D2, 0x82D7, 0x9328, 0x92F2, 0x849C, 0x86ED, 0x9C2D, 0x54C1, 0x5F6C, 0x658C, 0x6D5C, 0x7015, 0x8CA7, 0x8CD3, +0x983B, 0x654F, 0x74F6, 0x4E0D, 0x4ED8, 0x57E0, 0x592B, 0x5A66, 0x5BCC, 0x51A8, 0x5E03, 0x5E9C, 0x6016, 0x6276, 0x6577, 0x0000, +0x65A7, 0x666E, 0x6D6E, 0x7236, 0x7B26, 0x8150, 0x819A, 0x8299, 0x8B5C, 0x8CA0, 0x8CE6, 0x8D74, 0x961C, 0x9644, 0x4FAE, 0x64AB, +0x6B66, 0x821E, 0x8461, 0x856A, 0x90E8, 0x5C01, 0x6953, 0x98A8, 0x847A, 0x8557, 0x4F0F, 0x526F, 0x5FA9, 0x5E45, 0x670D, 0x798F, +0x8179, 0x8907, 0x8986, 0x6DF5, 0x5F17, 0x6255, 0x6CB8, 0x4ECF, 0x7269, 0x9B92, 0x5206, 0x543B, 0x5674, 0x58B3, 0x61A4, 0x626E, +0x711A, 0x596E, 0x7C89, 0x7CDE, 0x7D1B, 0x96F0, 0x6587, 0x805E, 0x4E19, 0x4F75, 0x5175, 0x5840, 0x5E63, 0x5E73, 0x5F0A, 0x67C4, +0x4E26, 0x853D, 0x9589, 0x965B, 0x7C73, 0x9801, 0x50FB, 0x58C1, 0x7656, 0x78A7, 0x5225, 0x77A5, 0x8511, 0x7B86, 0x504F, 0x5909, +0x7247, 0x7BC7, 0x7DE8, 0x8FBA, 0x8FD4, 0x904D, 0x4FBF, 0x52C9, 0x5A29, 0x5F01, 0x97AD, 0x4FDD, 0x8217, 0x92EA, 0x5703, 0x6355, +0x6B69, 0x752B, 0x88DC, 0x8F14, 0x7A42, 0x52DF, 0x5893, 0x6155, 0x620A, 0x66AE, 0x6BCD, 0x7C3F, 0x83E9, 0x5023, 0x4FF8, 0x5305, +0x5446, 0x5831, 0x5949, 0x5B9D, 0x5CF0, 0x5CEF, 0x5D29, 0x5E96, 0x62B1, 0x6367, 0x653E, 0x65B9, 0x670B, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x6CD5, 0x6CE1, 0x70F9, 0x7832, 0x7E2B, 0x80DE, 0x82B3, 0x840C, 0x84EC, 0x8702, 0x8912, 0x8A2A, 0x8C4A, 0x90A6, 0x92D2, 0x98FD, +0x9CF3, 0x9D6C, 0x4E4F, 0x4EA1, 0x508D, 0x5256, 0x574A, 0x59A8, 0x5E3D, 0x5FD8, 0x5FD9, 0x623F, 0x66B4, 0x671B, 0x67D0, 0x68D2, +0x5192, 0x7D21, 0x80AA, 0x81A8, 0x8B00, 0x8C8C, 0x8CBF, 0x927E, 0x9632, 0x5420, 0x982C, 0x5317, 0x50D5, 0x535C, 0x58A8, 0x64B2, +0x6734, 0x7267, 0x7766, 0x7A46, 0x91E6, 0x52C3, 0x6CA1, 0x6B86, 0x5800, 0x5E4C, 0x5954, 0x672C, 0x7FFB, 0x51E1, 0x76C6, 0x0000, +0x6469, 0x78E8, 0x9B54, 0x9EBB, 0x57CB, 0x59B9, 0x6627, 0x679A, 0x6BCE, 0x54E9, 0x69D9, 0x5E55, 0x819C, 0x6795, 0x9BAA, 0x67FE, +0x9C52, 0x685D, 0x4EA6, 0x4FE3, 0x53C8, 0x62B9, 0x672B, 0x6CAB, 0x8FC4, 0x4FAD, 0x7E6D, 0x9EBF, 0x4E07, 0x6162, 0x6E80, 0x6F2B, +0x8513, 0x5473, 0x672A, 0x9B45, 0x5DF3, 0x7B95, 0x5CAC, 0x5BC6, 0x871C, 0x6E4A, 0x84D1, 0x7A14, 0x8108, 0x5999, 0x7C8D, 0x6C11, +0x7720, 0x52D9, 0x5922, 0x7121, 0x725F, 0x77DB, 0x9727, 0x9D61, 0x690B, 0x5A7F, 0x5A18, 0x51A5, 0x540D, 0x547D, 0x660E, 0x76DF, +0x8FF7, 0x9298, 0x9CF4, 0x59EA, 0x725D, 0x6EC5, 0x514D, 0x68C9, 0x7DBF, 0x7DEC, 0x9762, 0x9EBA, 0x6478, 0x6A21, 0x8302, 0x5984, +0x5B5F, 0x6BDB, 0x731B, 0x76F2, 0x7DB2, 0x8017, 0x8499, 0x5132, 0x6728, 0x9ED9, 0x76EE, 0x6762, 0x52FF, 0x9905, 0x5C24, 0x623B, +0x7C7E, 0x8CB0, 0x554F, 0x60B6, 0x7D0B, 0x9580, 0x5301, 0x4E5F, 0x51B6, 0x591C, 0x723A, 0x8036, 0x91CE, 0x5F25, 0x77E2, 0x5384, +0x5F79, 0x7D04, 0x85AC, 0x8A33, 0x8E8D, 0x9756, 0x67F3, 0x85AE, 0x9453, 0x6109, 0x6108, 0x6CB9, 0x7652, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x8AED, 0x8F38, 0x552F, 0x4F51, 0x512A, 0x52C7, 0x53CB, 0x5BA5, 0x5E7D, 0x60A0, 0x6182, 0x63D6, 0x6709, 0x67DA, 0x6E67, 0x6D8C, +0x7336, 0x7337, 0x7531, 0x7950, 0x88D5, 0x8A98, 0x904A, 0x9091, 0x90F5, 0x96C4, 0x878D, 0x5915, 0x4E88, 0x4F59, 0x4E0E, 0x8A89, +0x8F3F, 0x9810, 0x50AD, 0x5E7C, 0x5996, 0x5BB9, 0x5EB8, 0x63DA, 0x63FA, 0x64C1, 0x66DC, 0x694A, 0x69D8, 0x6D0B, 0x6EB6, 0x7194, +0x7528, 0x7AAF, 0x7F8A, 0x8000, 0x8449, 0x84C9, 0x8981, 0x8B21, 0x8E0A, 0x9065, 0x967D, 0x990A, 0x617E, 0x6291, 0x6B32, 0x0000, +0x6C83, 0x6D74, 0x7FCC, 0x7FFC, 0x6DC0, 0x7F85, 0x87BA, 0x88F8, 0x6765, 0x83B1, 0x983C, 0x96F7, 0x6D1B, 0x7D61, 0x843D, 0x916A, +0x4E71, 0x5375, 0x5D50, 0x6B04, 0x6FEB, 0x85CD, 0x862D, 0x89A7, 0x5229, 0x540F, 0x5C65, 0x674E, 0x68A8, 0x7406, 0x7483, 0x75E2, +0x88CF, 0x88E1, 0x91CC, 0x96E2, 0x9678, 0x5F8B, 0x7387, 0x7ACB, 0x844E, 0x63A0, 0x7565, 0x5289, 0x6D41, 0x6E9C, 0x7409, 0x7559, +0x786B, 0x7C92, 0x9686, 0x7ADC, 0x9F8D, 0x4FB6, 0x616E, 0x65C5, 0x865C, 0x4E86, 0x4EAE, 0x50DA, 0x4E21, 0x51CC, 0x5BEE, 0x6599, +0x6881, 0x6DBC, 0x731F, 0x7642, 0x77AD, 0x7A1C, 0x7CE7, 0x826F, 0x8AD2, 0x907C, 0x91CF, 0x9675, 0x9818, 0x529B, 0x7DD1, 0x502B, +0x5398, 0x6797, 0x6DCB, 0x71D0, 0x7433, 0x81E8, 0x8F2A, 0x96A3, 0x9C57, 0x9E9F, 0x7460, 0x5841, 0x6D99, 0x7D2F, 0x985E, 0x4EE4, +0x4F36, 0x4F8B, 0x51B7, 0x52B1, 0x5DBA, 0x601C, 0x73B2, 0x793C, 0x82D3, 0x9234, 0x96B7, 0x96F6, 0x970A, 0x9E97, 0x9F62, 0x66A6, +0x6B74, 0x5217, 0x52A3, 0x70C8, 0x88C2, 0x5EC9, 0x604B, 0x6190, 0x6F23, 0x7149, 0x7C3E, 0x7DF4, 0x806F, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x84EE, 0x9023, 0x932C, 0x5442, 0x9B6F, 0x6AD3, 0x7089, 0x8CC2, 0x8DEF, 0x9732, 0x52B4, 0x5A41, 0x5ECA, 0x5F04, 0x6717, 0x697C, +0x6994, 0x6D6A, 0x6F0F, 0x7262, 0x72FC, 0x7BED, 0x8001, 0x807E, 0x874B, 0x90CE, 0x516D, 0x9E93, 0x7984, 0x808B, 0x9332, 0x8AD6, +0x502D, 0x548C, 0x8A71, 0x6B6A, 0x8CC4, 0x8107, 0x60D1, 0x67A0, 0x9DF2, 0x4E99, 0x4E98, 0x9C10, 0x8A6B, 0x85C1, 0x8568, 0x6900, +0x6E7E, 0x7897, 0x8155, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5F0C, +0x4E10, 0x4E15, 0x4E2A, 0x4E31, 0x4E36, 0x4E3C, 0x4E3F, 0x4E42, 0x4E56, 0x4E58, 0x4E82, 0x4E85, 0x8C6B, 0x4E8A, 0x8212, 0x5F0D, +0x4E8E, 0x4E9E, 0x4E9F, 0x4EA0, 0x4EA2, 0x4EB0, 0x4EB3, 0x4EB6, 0x4ECE, 0x4ECD, 0x4EC4, 0x4EC6, 0x4EC2, 0x4ED7, 0x4EDE, 0x4EED, +0x4EDF, 0x4EF7, 0x4F09, 0x4F5A, 0x4F30, 0x4F5B, 0x4F5D, 0x4F57, 0x4F47, 0x4F76, 0x4F88, 0x4F8F, 0x4F98, 0x4F7B, 0x4F69, 0x4F70, +0x4F91, 0x4F6F, 0x4F86, 0x4F96, 0x5118, 0x4FD4, 0x4FDF, 0x4FCE, 0x4FD8, 0x4FDB, 0x4FD1, 0x4FDA, 0x4FD0, 0x4FE4, 0x4FE5, 0x501A, +0x5028, 0x5014, 0x502A, 0x5025, 0x5005, 0x4F1C, 0x4FF6, 0x5021, 0x5029, 0x502C, 0x4FFE, 0x4FEF, 0x5011, 0x5006, 0x5043, 0x5047, +0x6703, 0x5055, 0x5050, 0x5048, 0x505A, 0x5056, 0x506C, 0x5078, 0x5080, 0x509A, 0x5085, 0x50B4, 0x50B2, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x50C9, 0x50CA, 0x50B3, 0x50C2, 0x50D6, 0x50DE, 0x50E5, 0x50ED, 0x50E3, 0x50EE, 0x50F9, 0x50F5, 0x5109, 0x5101, 0x5102, 0x5116, +0x5115, 0x5114, 0x511A, 0x5121, 0x513A, 0x5137, 0x513C, 0x513B, 0x513F, 0x5140, 0x5152, 0x514C, 0x5154, 0x5162, 0x7AF8, 0x5169, +0x516A, 0x516E, 0x5180, 0x5182, 0x56D8, 0x518C, 0x5189, 0x518F, 0x5191, 0x5193, 0x5195, 0x5196, 0x51A4, 0x51A6, 0x51A2, 0x51A9, +0x51AA, 0x51AB, 0x51B3, 0x51B1, 0x51B2, 0x51B0, 0x51B5, 0x51BD, 0x51C5, 0x51C9, 0x51DB, 0x51E0, 0x8655, 0x51E9, 0x51ED, 0x0000, +0x51F0, 0x51F5, 0x51FE, 0x5204, 0x520B, 0x5214, 0x520E, 0x5227, 0x522A, 0x522E, 0x5233, 0x5239, 0x524F, 0x5244, 0x524B, 0x524C, +0x525E, 0x5254, 0x526A, 0x5274, 0x5269, 0x5273, 0x527F, 0x527D, 0x528D, 0x5294, 0x5292, 0x5271, 0x5288, 0x5291, 0x8FA8, 0x8FA7, +0x52AC, 0x52AD, 0x52BC, 0x52B5, 0x52C1, 0x52CD, 0x52D7, 0x52DE, 0x52E3, 0x52E6, 0x98ED, 0x52E0, 0x52F3, 0x52F5, 0x52F8, 0x52F9, +0x5306, 0x5308, 0x7538, 0x530D, 0x5310, 0x530F, 0x5315, 0x531A, 0x5323, 0x532F, 0x5331, 0x5333, 0x5338, 0x5340, 0x5346, 0x5345, +0x4E17, 0x5349, 0x534D, 0x51D6, 0x535E, 0x5369, 0x536E, 0x5918, 0x537B, 0x5377, 0x5382, 0x5396, 0x53A0, 0x53A6, 0x53A5, 0x53AE, +0x53B0, 0x53B6, 0x53C3, 0x7C12, 0x96D9, 0x53DF, 0x66FC, 0x71EE, 0x53EE, 0x53E8, 0x53ED, 0x53FA, 0x5401, 0x543D, 0x5440, 0x542C, +0x542D, 0x543C, 0x542E, 0x5436, 0x5429, 0x541D, 0x544E, 0x548F, 0x5475, 0x548E, 0x545F, 0x5471, 0x5477, 0x5470, 0x5492, 0x547B, +0x5480, 0x5476, 0x5484, 0x5490, 0x5486, 0x54C7, 0x54A2, 0x54B8, 0x54A5, 0x54AC, 0x54C4, 0x54C8, 0x54A8, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x54AB, 0x54C2, 0x54A4, 0x54BE, 0x54BC, 0x54D8, 0x54E5, 0x54E6, 0x550F, 0x5514, 0x54FD, 0x54EE, 0x54ED, 0x54FA, 0x54E2, 0x5539, +0x5540, 0x5563, 0x554C, 0x552E, 0x555C, 0x5545, 0x5556, 0x5557, 0x5538, 0x5533, 0x555D, 0x5599, 0x5580, 0x54AF, 0x558A, 0x559F, +0x557B, 0x557E, 0x5598, 0x559E, 0x55AE, 0x557C, 0x5583, 0x55A9, 0x5587, 0x55A8, 0x55DA, 0x55C5, 0x55DF, 0x55C4, 0x55DC, 0x55E4, +0x55D4, 0x5614, 0x55F7, 0x5616, 0x55FE, 0x55FD, 0x561B, 0x55F9, 0x564E, 0x5650, 0x71DF, 0x5634, 0x5636, 0x5632, 0x5638, 0x0000, +0x566B, 0x5664, 0x562F, 0x566C, 0x566A, 0x5686, 0x5680, 0x568A, 0x56A0, 0x5694, 0x568F, 0x56A5, 0x56AE, 0x56B6, 0x56B4, 0x56C2, +0x56BC, 0x56C1, 0x56C3, 0x56C0, 0x56C8, 0x56CE, 0x56D1, 0x56D3, 0x56D7, 0x56EE, 0x56F9, 0x5700, 0x56FF, 0x5704, 0x5709, 0x5708, +0x570B, 0x570D, 0x5713, 0x5718, 0x5716, 0x55C7, 0x571C, 0x5726, 0x5737, 0x5738, 0x574E, 0x573B, 0x5740, 0x574F, 0x5769, 0x57C0, +0x5788, 0x5761, 0x577F, 0x5789, 0x5793, 0x57A0, 0x57B3, 0x57A4, 0x57AA, 0x57B0, 0x57C3, 0x57C6, 0x57D4, 0x57D2, 0x57D3, 0x580A, +0x57D6, 0x57E3, 0x580B, 0x5819, 0x581D, 0x5872, 0x5821, 0x5862, 0x584B, 0x5870, 0x6BC0, 0x5852, 0x583D, 0x5879, 0x5885, 0x58B9, +0x589F, 0x58AB, 0x58BA, 0x58DE, 0x58BB, 0x58B8, 0x58AE, 0x58C5, 0x58D3, 0x58D1, 0x58D7, 0x58D9, 0x58D8, 0x58E5, 0x58DC, 0x58E4, +0x58DF, 0x58EF, 0x58FA, 0x58F9, 0x58FB, 0x58FC, 0x58FD, 0x5902, 0x590A, 0x5910, 0x591B, 0x68A6, 0x5925, 0x592C, 0x592D, 0x5932, +0x5938, 0x593E, 0x7AD2, 0x5955, 0x5950, 0x594E, 0x595A, 0x5958, 0x5962, 0x5960, 0x5967, 0x596C, 0x5969, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x5978, 0x5981, 0x599D, 0x4F5E, 0x4FAB, 0x59A3, 0x59B2, 0x59C6, 0x59E8, 0x59DC, 0x598D, 0x59D9, 0x59DA, 0x5A25, 0x5A1F, 0x5A11, +0x5A1C, 0x5A09, 0x5A1A, 0x5A40, 0x5A6C, 0x5A49, 0x5A35, 0x5A36, 0x5A62, 0x5A6A, 0x5A9A, 0x5ABC, 0x5ABE, 0x5ACB, 0x5AC2, 0x5ABD, +0x5AE3, 0x5AD7, 0x5AE6, 0x5AE9, 0x5AD6, 0x5AFA, 0x5AFB, 0x5B0C, 0x5B0B, 0x5B16, 0x5B32, 0x5AD0, 0x5B2A, 0x5B36, 0x5B3E, 0x5B43, +0x5B45, 0x5B40, 0x5B51, 0x5B55, 0x5B5A, 0x5B5B, 0x5B65, 0x5B69, 0x5B70, 0x5B73, 0x5B75, 0x5B78, 0x6588, 0x5B7A, 0x5B80, 0x0000, +0x5B83, 0x5BA6, 0x5BB8, 0x5BC3, 0x5BC7, 0x5BC9, 0x5BD4, 0x5BD0, 0x5BE4, 0x5BE6, 0x5BE2, 0x5BDE, 0x5BE5, 0x5BEB, 0x5BF0, 0x5BF6, +0x5BF3, 0x5C05, 0x5C07, 0x5C08, 0x5C0D, 0x5C13, 0x5C20, 0x5C22, 0x5C28, 0x5C38, 0x5C39, 0x5C41, 0x5C46, 0x5C4E, 0x5C53, 0x5C50, +0x5C4F, 0x5B71, 0x5C6C, 0x5C6E, 0x4E62, 0x5C76, 0x5C79, 0x5C8C, 0x5C91, 0x5C94, 0x599B, 0x5CAB, 0x5CBB, 0x5CB6, 0x5CBC, 0x5CB7, +0x5CC5, 0x5CBE, 0x5CC7, 0x5CD9, 0x5CE9, 0x5CFD, 0x5CFA, 0x5CED, 0x5D8C, 0x5CEA, 0x5D0B, 0x5D15, 0x5D17, 0x5D5C, 0x5D1F, 0x5D1B, +0x5D11, 0x5D14, 0x5D22, 0x5D1A, 0x5D19, 0x5D18, 0x5D4C, 0x5D52, 0x5D4E, 0x5D4B, 0x5D6C, 0x5D73, 0x5D76, 0x5D87, 0x5D84, 0x5D82, +0x5DA2, 0x5D9D, 0x5DAC, 0x5DAE, 0x5DBD, 0x5D90, 0x5DB7, 0x5DBC, 0x5DC9, 0x5DCD, 0x5DD3, 0x5DD2, 0x5DD6, 0x5DDB, 0x5DEB, 0x5DF2, +0x5DF5, 0x5E0B, 0x5E1A, 0x5E19, 0x5E11, 0x5E1B, 0x5E36, 0x5E37, 0x5E44, 0x5E43, 0x5E40, 0x5E4E, 0x5E57, 0x5E54, 0x5E5F, 0x5E62, +0x5E64, 0x5E47, 0x5E75, 0x5E76, 0x5E7A, 0x9EBC, 0x5E7F, 0x5EA0, 0x5EC1, 0x5EC2, 0x5EC8, 0x5ED0, 0x5ECF, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x5ED6, 0x5EE3, 0x5EDD, 0x5EDA, 0x5EDB, 0x5EE2, 0x5EE1, 0x5EE8, 0x5EE9, 0x5EEC, 0x5EF1, 0x5EF3, 0x5EF0, 0x5EF4, 0x5EF8, 0x5EFE, +0x5F03, 0x5F09, 0x5F5D, 0x5F5C, 0x5F0B, 0x5F11, 0x5F16, 0x5F29, 0x5F2D, 0x5F38, 0x5F41, 0x5F48, 0x5F4C, 0x5F4E, 0x5F2F, 0x5F51, +0x5F56, 0x5F57, 0x5F59, 0x5F61, 0x5F6D, 0x5F73, 0x5F77, 0x5F83, 0x5F82, 0x5F7F, 0x5F8A, 0x5F88, 0x5F91, 0x5F87, 0x5F9E, 0x5F99, +0x5F98, 0x5FA0, 0x5FA8, 0x5FAD, 0x5FBC, 0x5FD6, 0x5FFB, 0x5FE4, 0x5FF8, 0x5FF1, 0x5FDD, 0x60B3, 0x5FFF, 0x6021, 0x6060, 0x0000, +0x6019, 0x6010, 0x6029, 0x600E, 0x6031, 0x601B, 0x6015, 0x602B, 0x6026, 0x600F, 0x603A, 0x605A, 0x6041, 0x606A, 0x6077, 0x605F, +0x604A, 0x6046, 0x604D, 0x6063, 0x6043, 0x6064, 0x6042, 0x606C, 0x606B, 0x6059, 0x6081, 0x608D, 0x60E7, 0x6083, 0x609A, 0x6084, +0x609B, 0x6096, 0x6097, 0x6092, 0x60A7, 0x608B, 0x60E1, 0x60B8, 0x60E0, 0x60D3, 0x60B4, 0x5FF0, 0x60BD, 0x60C6, 0x60B5, 0x60D8, +0x614D, 0x6115, 0x6106, 0x60F6, 0x60F7, 0x6100, 0x60F4, 0x60FA, 0x6103, 0x6121, 0x60FB, 0x60F1, 0x610D, 0x610E, 0x6147, 0x613E, +0x6128, 0x6127, 0x614A, 0x613F, 0x613C, 0x612C, 0x6134, 0x613D, 0x6142, 0x6144, 0x6173, 0x6177, 0x6158, 0x6159, 0x615A, 0x616B, +0x6174, 0x616F, 0x6165, 0x6171, 0x615F, 0x615D, 0x6153, 0x6175, 0x6199, 0x6196, 0x6187, 0x61AC, 0x6194, 0x619A, 0x618A, 0x6191, +0x61AB, 0x61AE, 0x61CC, 0x61CA, 0x61C9, 0x61F7, 0x61C8, 0x61C3, 0x61C6, 0x61BA, 0x61CB, 0x7F79, 0x61CD, 0x61E6, 0x61E3, 0x61F6, +0x61FA, 0x61F4, 0x61FF, 0x61FD, 0x61FC, 0x61FE, 0x6200, 0x6208, 0x6209, 0x620D, 0x620C, 0x6214, 0x621B, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x621E, 0x6221, 0x622A, 0x622E, 0x6230, 0x6232, 0x6233, 0x6241, 0x624E, 0x625E, 0x6263, 0x625B, 0x6260, 0x6268, 0x627C, 0x6282, +0x6289, 0x627E, 0x6292, 0x6293, 0x6296, 0x62D4, 0x6283, 0x6294, 0x62D7, 0x62D1, 0x62BB, 0x62CF, 0x62FF, 0x62C6, 0x64D4, 0x62C8, +0x62DC, 0x62CC, 0x62CA, 0x62C2, 0x62C7, 0x629B, 0x62C9, 0x630C, 0x62EE, 0x62F1, 0x6327, 0x6302, 0x6308, 0x62EF, 0x62F5, 0x6350, +0x633E, 0x634D, 0x641C, 0x634F, 0x6396, 0x638E, 0x6380, 0x63AB, 0x6376, 0x63A3, 0x638F, 0x6389, 0x639F, 0x63B5, 0x636B, 0x0000, +0x6369, 0x63BE, 0x63E9, 0x63C0, 0x63C6, 0x63E3, 0x63C9, 0x63D2, 0x63F6, 0x63C4, 0x6416, 0x6434, 0x6406, 0x6413, 0x6426, 0x6436, +0x651D, 0x6417, 0x6428, 0x640F, 0x6467, 0x646F, 0x6476, 0x644E, 0x652A, 0x6495, 0x6493, 0x64A5, 0x64A9, 0x6488, 0x64BC, 0x64DA, +0x64D2, 0x64C5, 0x64C7, 0x64BB, 0x64D8, 0x64C2, 0x64F1, 0x64E7, 0x8209, 0x64E0, 0x64E1, 0x62AC, 0x64E3, 0x64EF, 0x652C, 0x64F6, +0x64F4, 0x64F2, 0x64FA, 0x6500, 0x64FD, 0x6518, 0x651C, 0x6505, 0x6524, 0x6523, 0x652B, 0x6534, 0x6535, 0x6537, 0x6536, 0x6538, +0x754B, 0x6548, 0x6556, 0x6555, 0x654D, 0x6558, 0x655E, 0x655D, 0x6572, 0x6578, 0x6582, 0x6583, 0x8B8A, 0x659B, 0x659F, 0x65AB, +0x65B7, 0x65C3, 0x65C6, 0x65C1, 0x65C4, 0x65CC, 0x65D2, 0x65DB, 0x65D9, 0x65E0, 0x65E1, 0x65F1, 0x6772, 0x660A, 0x6603, 0x65FB, +0x6773, 0x6635, 0x6636, 0x6634, 0x661C, 0x664F, 0x6644, 0x6649, 0x6641, 0x665E, 0x665D, 0x6664, 0x6667, 0x6668, 0x665F, 0x6662, +0x6670, 0x6683, 0x6688, 0x668E, 0x6689, 0x6684, 0x6698, 0x669D, 0x66C1, 0x66B9, 0x66C9, 0x66BE, 0x66BC, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x66C4, 0x66B8, 0x66D6, 0x66DA, 0x66E0, 0x663F, 0x66E6, 0x66E9, 0x66F0, 0x66F5, 0x66F7, 0x670F, 0x6716, 0x671E, 0x6726, 0x6727, +0x9738, 0x672E, 0x673F, 0x6736, 0x6741, 0x6738, 0x6737, 0x6746, 0x675E, 0x6760, 0x6759, 0x6763, 0x6764, 0x6789, 0x6770, 0x67A9, +0x677C, 0x676A, 0x678C, 0x678B, 0x67A6, 0x67A1, 0x6785, 0x67B7, 0x67EF, 0x67B4, 0x67EC, 0x67B3, 0x67E9, 0x67B8, 0x67E4, 0x67DE, +0x67DD, 0x67E2, 0x67EE, 0x67B9, 0x67CE, 0x67C6, 0x67E7, 0x6A9C, 0x681E, 0x6846, 0x6829, 0x6840, 0x684D, 0x6832, 0x684E, 0x0000, +0x68B3, 0x682B, 0x6859, 0x6863, 0x6877, 0x687F, 0x689F, 0x688F, 0x68AD, 0x6894, 0x689D, 0x689B, 0x6883, 0x6AAE, 0x68B9, 0x6874, +0x68B5, 0x68A0, 0x68BA, 0x690F, 0x688D, 0x687E, 0x6901, 0x68CA, 0x6908, 0x68D8, 0x6922, 0x6926, 0x68E1, 0x690C, 0x68CD, 0x68D4, +0x68E7, 0x68D5, 0x6936, 0x6912, 0x6904, 0x68D7, 0x68E3, 0x6925, 0x68F9, 0x68E0, 0x68EF, 0x6928, 0x692A, 0x691A, 0x6923, 0x6921, +0x68C6, 0x6979, 0x6977, 0x695C, 0x6978, 0x696B, 0x6954, 0x697E, 0x696E, 0x6939, 0x6974, 0x693D, 0x6959, 0x6930, 0x6961, 0x695E, +0x695D, 0x6981, 0x696A, 0x69B2, 0x69AE, 0x69D0, 0x69BF, 0x69C1, 0x69D3, 0x69BE, 0x69CE, 0x5BE8, 0x69CA, 0x69DD, 0x69BB, 0x69C3, +0x69A7, 0x6A2E, 0x6991, 0x69A0, 0x699C, 0x6995, 0x69B4, 0x69DE, 0x69E8, 0x6A02, 0x6A1B, 0x69FF, 0x6B0A, 0x69F9, 0x69F2, 0x69E7, +0x6A05, 0x69B1, 0x6A1E, 0x69ED, 0x6A14, 0x69EB, 0x6A0A, 0x6A12, 0x6AC1, 0x6A23, 0x6A13, 0x6A44, 0x6A0C, 0x6A72, 0x6A36, 0x6A78, +0x6A47, 0x6A62, 0x6A59, 0x6A66, 0x6A48, 0x6A38, 0x6A22, 0x6A90, 0x6A8D, 0x6AA0, 0x6A84, 0x6AA2, 0x6AA3, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x6A97, 0x8617, 0x6ABB, 0x6AC3, 0x6AC2, 0x6AB8, 0x6AB3, 0x6AAC, 0x6ADE, 0x6AD1, 0x6ADF, 0x6AAA, 0x6ADA, 0x6AEA, 0x6AFB, 0x6B05, +0x8616, 0x6AFA, 0x6B12, 0x6B16, 0x9B31, 0x6B1F, 0x6B38, 0x6B37, 0x76DC, 0x6B39, 0x98EE, 0x6B47, 0x6B43, 0x6B49, 0x6B50, 0x6B59, +0x6B54, 0x6B5B, 0x6B5F, 0x6B61, 0x6B78, 0x6B79, 0x6B7F, 0x6B80, 0x6B84, 0x6B83, 0x6B8D, 0x6B98, 0x6B95, 0x6B9E, 0x6BA4, 0x6BAA, +0x6BAB, 0x6BAF, 0x6BB2, 0x6BB1, 0x6BB3, 0x6BB7, 0x6BBC, 0x6BC6, 0x6BCB, 0x6BD3, 0x6BDF, 0x6BEC, 0x6BEB, 0x6BF3, 0x6BEF, 0x0000, +0x9EBE, 0x6C08, 0x6C13, 0x6C14, 0x6C1B, 0x6C24, 0x6C23, 0x6C5E, 0x6C55, 0x6C62, 0x6C6A, 0x6C82, 0x6C8D, 0x6C9A, 0x6C81, 0x6C9B, +0x6C7E, 0x6C68, 0x6C73, 0x6C92, 0x6C90, 0x6CC4, 0x6CF1, 0x6CD3, 0x6CBD, 0x6CD7, 0x6CC5, 0x6CDD, 0x6CAE, 0x6CB1, 0x6CBE, 0x6CBA, +0x6CDB, 0x6CEF, 0x6CD9, 0x6CEA, 0x6D1F, 0x884D, 0x6D36, 0x6D2B, 0x6D3D, 0x6D38, 0x6D19, 0x6D35, 0x6D33, 0x6D12, 0x6D0C, 0x6D63, +0x6D93, 0x6D64, 0x6D5A, 0x6D79, 0x6D59, 0x6D8E, 0x6D95, 0x6FE4, 0x6D85, 0x6DF9, 0x6E15, 0x6E0A, 0x6DB5, 0x6DC7, 0x6DE6, 0x6DB8, +0x6DC6, 0x6DEC, 0x6DDE, 0x6DCC, 0x6DE8, 0x6DD2, 0x6DC5, 0x6DFA, 0x6DD9, 0x6DE4, 0x6DD5, 0x6DEA, 0x6DEE, 0x6E2D, 0x6E6E, 0x6E2E, +0x6E19, 0x6E72, 0x6E5F, 0x6E3E, 0x6E23, 0x6E6B, 0x6E2B, 0x6E76, 0x6E4D, 0x6E1F, 0x6E43, 0x6E3A, 0x6E4E, 0x6E24, 0x6EFF, 0x6E1D, +0x6E38, 0x6E82, 0x6EAA, 0x6E98, 0x6EC9, 0x6EB7, 0x6ED3, 0x6EBD, 0x6EAF, 0x6EC4, 0x6EB2, 0x6ED4, 0x6ED5, 0x6E8F, 0x6EA5, 0x6EC2, +0x6E9F, 0x6F41, 0x6F11, 0x704C, 0x6EEC, 0x6EF8, 0x6EFE, 0x6F3F, 0x6EF2, 0x6F31, 0x6EEF, 0x6F32, 0x6ECC, 0x0000, 0x0000, 0x0000, +0xFFFF +}; + +const uint16 sjise0tou[] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x6F3E, 0x6F13, 0x6EF7, 0x6F86, 0x6F7A, 0x6F78, 0x6F81, 0x6F80, 0x6F6F, 0x6F5B, 0x6FF3, 0x6F6D, 0x6F82, 0x6F7C, 0x6F58, 0x6F8E, +0x6F91, 0x6FC2, 0x6F66, 0x6FB3, 0x6FA3, 0x6FA1, 0x6FA4, 0x6FB9, 0x6FC6, 0x6FAA, 0x6FDF, 0x6FD5, 0x6FEC, 0x6FD4, 0x6FD8, 0x6FF1, +0x6FEE, 0x6FDB, 0x7009, 0x700B, 0x6FFA, 0x7011, 0x7001, 0x700F, 0x6FFE, 0x701B, 0x701A, 0x6F74, 0x701D, 0x7018, 0x701F, 0x7030, +0x703E, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70AF, 0x70F1, 0x70AC, 0x70B8, 0x70B3, 0x70AE, 0x70DF, 0x70CB, 0x70DD, 0x0000, +0x70D9, 0x7109, 0x70FD, 0x711C, 0x7119, 0x7165, 0x7155, 0x7188, 0x7166, 0x7162, 0x714C, 0x7156, 0x716C, 0x718F, 0x71FB, 0x7184, +0x7195, 0x71A8, 0x71AC, 0x71D7, 0x71B9, 0x71BE, 0x71D2, 0x71C9, 0x71D4, 0x71CE, 0x71E0, 0x71EC, 0x71E7, 0x71F5, 0x71FC, 0x71F9, +0x71FF, 0x720D, 0x7210, 0x721B, 0x7228, 0x722D, 0x722C, 0x7230, 0x7232, 0x723B, 0x723C, 0x723F, 0x7240, 0x7246, 0x724B, 0x7258, +0x7274, 0x727E, 0x7282, 0x7281, 0x7287, 0x7292, 0x7296, 0x72A2, 0x72A7, 0x72B9, 0x72B2, 0x72C3, 0x72C6, 0x72C4, 0x72CE, 0x72D2, +0x72E2, 0x72E0, 0x72E1, 0x72F9, 0x72F7, 0x500F, 0x7317, 0x730A, 0x731C, 0x7316, 0x731D, 0x7334, 0x732F, 0x7329, 0x7325, 0x733E, +0x734E, 0x734F, 0x9ED8, 0x7357, 0x736A, 0x7368, 0x7370, 0x7378, 0x7375, 0x737B, 0x737A, 0x73C8, 0x73B3, 0x73CE, 0x73BB, 0x73C0, +0x73E5, 0x73EE, 0x73DE, 0x74A2, 0x7405, 0x746F, 0x7425, 0x73F8, 0x7432, 0x743A, 0x7455, 0x743F, 0x745F, 0x7459, 0x7441, 0x745C, +0x7469, 0x7470, 0x7463, 0x746A, 0x7476, 0x747E, 0x748B, 0x749E, 0x74A7, 0x74CA, 0x74CF, 0x74D4, 0x73F1, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x74E0, 0x74E3, 0x74E7, 0x74E9, 0x74EE, 0x74F2, 0x74F0, 0x74F1, 0x74F8, 0x74F7, 0x7504, 0x7503, 0x7505, 0x750C, 0x750E, 0x750D, +0x7515, 0x7513, 0x751E, 0x7526, 0x752C, 0x753C, 0x7544, 0x754D, 0x754A, 0x7549, 0x755B, 0x7546, 0x755A, 0x7569, 0x7564, 0x7567, +0x756B, 0x756D, 0x7578, 0x7576, 0x7586, 0x7587, 0x7574, 0x758A, 0x7589, 0x7582, 0x7594, 0x759A, 0x759D, 0x75A5, 0x75A3, 0x75C2, +0x75B3, 0x75C3, 0x75B5, 0x75BD, 0x75B8, 0x75BC, 0x75B1, 0x75CD, 0x75CA, 0x75D2, 0x75D9, 0x75E3, 0x75DE, 0x75FE, 0x75FF, 0x0000, +0x75FC, 0x7601, 0x75F0, 0x75FA, 0x75F2, 0x75F3, 0x760B, 0x760D, 0x7609, 0x761F, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634, +0x7630, 0x763B, 0x7647, 0x7648, 0x7646, 0x765C, 0x7658, 0x7661, 0x7662, 0x7668, 0x7669, 0x766A, 0x7667, 0x766C, 0x7670, 0x7672, +0x7676, 0x7678, 0x767C, 0x7680, 0x7683, 0x7688, 0x768B, 0x768E, 0x7696, 0x7693, 0x7699, 0x769A, 0x76B0, 0x76B4, 0x76B8, 0x76B9, +0x76BA, 0x76C2, 0x76CD, 0x76D6, 0x76D2, 0x76DE, 0x76E1, 0x76E5, 0x76E7, 0x76EA, 0x862F, 0x76FB, 0x7708, 0x7707, 0x7704, 0x7729, +0x7724, 0x771E, 0x7725, 0x7726, 0x771B, 0x7737, 0x7738, 0x7747, 0x775A, 0x7768, 0x776B, 0x775B, 0x7765, 0x777F, 0x777E, 0x7779, +0x778E, 0x778B, 0x7791, 0x77A0, 0x779E, 0x77B0, 0x77B6, 0x77B9, 0x77BF, 0x77BC, 0x77BD, 0x77BB, 0x77C7, 0x77CD, 0x77D7, 0x77DA, +0x77DC, 0x77E3, 0x77EE, 0x77FC, 0x780C, 0x7812, 0x7926, 0x7820, 0x792A, 0x7845, 0x788E, 0x7874, 0x7886, 0x787C, 0x789A, 0x788C, +0x78A3, 0x78B5, 0x78AA, 0x78AF, 0x78D1, 0x78C6, 0x78CB, 0x78D4, 0x78BE, 0x78BC, 0x78C5, 0x78CA, 0x78EC, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x78E7, 0x78DA, 0x78FD, 0x78F4, 0x7907, 0x7912, 0x7911, 0x7919, 0x792C, 0x792B, 0x7940, 0x7960, 0x7957, 0x795F, 0x795A, 0x7955, +0x7953, 0x797A, 0x797F, 0x798A, 0x799D, 0x79A7, 0x9F4B, 0x79AA, 0x79AE, 0x79B3, 0x79B9, 0x79BA, 0x79C9, 0x79D5, 0x79E7, 0x79EC, +0x79E1, 0x79E3, 0x7A08, 0x7A0D, 0x7A18, 0x7A19, 0x7A20, 0x7A1F, 0x7980, 0x7A31, 0x7A3B, 0x7A3E, 0x7A37, 0x7A43, 0x7A57, 0x7A49, +0x7A61, 0x7A62, 0x7A69, 0x9F9D, 0x7A70, 0x7A79, 0x7A7D, 0x7A88, 0x7A97, 0x7A95, 0x7A98, 0x7A96, 0x7AA9, 0x7AC8, 0x7AB0, 0x0000, +0x7AB6, 0x7AC5, 0x7AC4, 0x7ABF, 0x9083, 0x7AC7, 0x7ACA, 0x7ACD, 0x7ACF, 0x7AD5, 0x7AD3, 0x7AD9, 0x7ADA, 0x7ADD, 0x7AE1, 0x7AE2, +0x7AE6, 0x7AED, 0x7AF0, 0x7B02, 0x7B0F, 0x7B0A, 0x7B06, 0x7B33, 0x7B18, 0x7B19, 0x7B1E, 0x7B35, 0x7B28, 0x7B36, 0x7B50, 0x7B7A, +0x7B04, 0x7B4D, 0x7B0B, 0x7B4C, 0x7B45, 0x7B75, 0x7B65, 0x7B74, 0x7B67, 0x7B70, 0x7B71, 0x7B6C, 0x7B6E, 0x7B9D, 0x7B98, 0x7B9F, +0x7B8D, 0x7B9C, 0x7B9A, 0x7B8B, 0x7B92, 0x7B8F, 0x7B5D, 0x7B99, 0x7BCB, 0x7BC1, 0x7BCC, 0x7BCF, 0x7BB4, 0x7BC6, 0x7BDD, 0x7BE9, +0x7C11, 0x7C14, 0x7BE6, 0x7BE5, 0x7C60, 0x7C00, 0x7C07, 0x7C13, 0x7BF3, 0x7BF7, 0x7C17, 0x7C0D, 0x7BF6, 0x7C23, 0x7C27, 0x7C2A, +0x7C1F, 0x7C37, 0x7C2B, 0x7C3D, 0x7C4C, 0x7C43, 0x7C54, 0x7C4F, 0x7C40, 0x7C50, 0x7C58, 0x7C5F, 0x7C64, 0x7C56, 0x7C65, 0x7C6C, +0x7C75, 0x7C83, 0x7C90, 0x7CA4, 0x7CAD, 0x7CA2, 0x7CAB, 0x7CA1, 0x7CA8, 0x7CB3, 0x7CB2, 0x7CB1, 0x7CAE, 0x7CB9, 0x7CBD, 0x7CC0, +0x7CC5, 0x7CC2, 0x7CD8, 0x7CD2, 0x7CDC, 0x7CE2, 0x9B3B, 0x7CEF, 0x7CF2, 0x7CF4, 0x7CF6, 0x7CFA, 0x7D06, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x7D02, 0x7D1C, 0x7D15, 0x7D0A, 0x7D45, 0x7D4B, 0x7D2E, 0x7D32, 0x7D3F, 0x7D35, 0x7D46, 0x7D73, 0x7D56, 0x7D4E, 0x7D72, 0x7D68, +0x7D6E, 0x7D4F, 0x7D63, 0x7D93, 0x7D89, 0x7D5B, 0x7D8F, 0x7D7D, 0x7D9B, 0x7DBA, 0x7DAE, 0x7DA3, 0x7DB5, 0x7DC7, 0x7DBD, 0x7DAB, +0x7E3D, 0x7DA2, 0x7DAF, 0x7DDC, 0x7DB8, 0x7D9F, 0x7DB0, 0x7DD8, 0x7DDD, 0x7DE4, 0x7DDE, 0x7DFB, 0x7DF2, 0x7DE1, 0x7E05, 0x7E0A, +0x7E23, 0x7E21, 0x7E12, 0x7E31, 0x7E1F, 0x7E09, 0x7E0B, 0x7E22, 0x7E46, 0x7E66, 0x7E3B, 0x7E35, 0x7E39, 0x7E43, 0x7E37, 0x0000, +0x7E32, 0x7E3A, 0x7E67, 0x7E5D, 0x7E56, 0x7E5E, 0x7E59, 0x7E5A, 0x7E79, 0x7E6A, 0x7E69, 0x7E7C, 0x7E7B, 0x7E83, 0x7DD5, 0x7E7D, +0x8FAE, 0x7E7F, 0x7E88, 0x7E89, 0x7E8C, 0x7E92, 0x7E90, 0x7E93, 0x7E94, 0x7E96, 0x7E8E, 0x7E9B, 0x7E9C, 0x7F38, 0x7F3A, 0x7F45, +0x7F4C, 0x7F4D, 0x7F4E, 0x7F50, 0x7F51, 0x7F55, 0x7F54, 0x7F58, 0x7F5F, 0x7F60, 0x7F68, 0x7F69, 0x7F67, 0x7F78, 0x7F82, 0x7F86, +0x7F83, 0x7F88, 0x7F87, 0x7F8C, 0x7F94, 0x7F9E, 0x7F9D, 0x7F9A, 0x7FA3, 0x7FAF, 0x7FB2, 0x7FB9, 0x7FAE, 0x7FB6, 0x7FB8, 0x8B71, +0x7FC5, 0x7FC6, 0x7FCA, 0x7FD5, 0x7FD4, 0x7FE1, 0x7FE6, 0x7FE9, 0x7FF3, 0x7FF9, 0x98DC, 0x8006, 0x8004, 0x800B, 0x8012, 0x8018, +0x8019, 0x801C, 0x8021, 0x8028, 0x803F, 0x803B, 0x804A, 0x8046, 0x8052, 0x8058, 0x805A, 0x805F, 0x8062, 0x8068, 0x8073, 0x8072, +0x8070, 0x8076, 0x8079, 0x807D, 0x807F, 0x8084, 0x8086, 0x8085, 0x809B, 0x8093, 0x809A, 0x80AD, 0x5190, 0x80AC, 0x80DB, 0x80E5, +0x80D9, 0x80DD, 0x80C4, 0x80DA, 0x80D6, 0x8109, 0x80EF, 0x80F1, 0x811B, 0x8129, 0x8123, 0x812F, 0x814B, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x968B, 0x8146, 0x813E, 0x8153, 0x8151, 0x80FC, 0x8171, 0x816E, 0x8165, 0x8166, 0x8174, 0x8183, 0x8188, 0x818A, 0x8180, 0x8182, +0x81A0, 0x8195, 0x81A4, 0x81A3, 0x815F, 0x8193, 0x81A9, 0x81B0, 0x81B5, 0x81BE, 0x81B8, 0x81BD, 0x81C0, 0x81C2, 0x81BA, 0x81C9, +0x81CD, 0x81D1, 0x81D9, 0x81D8, 0x81C8, 0x81DA, 0x81DF, 0x81E0, 0x81E7, 0x81FA, 0x81FB, 0x81FE, 0x8201, 0x8202, 0x8205, 0x8207, +0x820A, 0x820D, 0x8210, 0x8216, 0x8229, 0x822B, 0x8238, 0x8233, 0x8240, 0x8259, 0x8258, 0x825D, 0x825A, 0x825F, 0x8264, 0x0000, +0x8262, 0x8268, 0x826A, 0x826B, 0x822E, 0x8271, 0x8277, 0x8278, 0x827E, 0x828D, 0x8292, 0x82AB, 0x829F, 0x82BB, 0x82AC, 0x82E1, +0x82E3, 0x82DF, 0x82D2, 0x82F4, 0x82F3, 0x82FA, 0x8393, 0x8303, 0x82FB, 0x82F9, 0x82DE, 0x8306, 0x82DC, 0x8309, 0x82D9, 0x8335, +0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339, 0x8350, 0x8345, 0x832F, 0x832B, 0x8317, 0x8318, 0x8385, 0x839A, 0x83AA, 0x839F, +0x83A2, 0x8396, 0x8323, 0x838E, 0x8387, 0x838A, 0x837C, 0x83B5, 0x8373, 0x8375, 0x83A0, 0x8389, 0x83A8, 0x83F4, 0x8413, 0x83EB, +0x83CE, 0x83FD, 0x8403, 0x83D8, 0x840B, 0x83C1, 0x83F7, 0x8407, 0x83E0, 0x83F2, 0x840D, 0x8422, 0x8420, 0x83BD, 0x8438, 0x8506, +0x83FB, 0x846D, 0x842A, 0x843C, 0x855A, 0x8484, 0x8477, 0x846B, 0x84AD, 0x846E, 0x8482, 0x8469, 0x8446, 0x842C, 0x846F, 0x8479, +0x8435, 0x84CA, 0x8462, 0x84B9, 0x84BF, 0x849F, 0x84D9, 0x84CD, 0x84BB, 0x84DA, 0x84D0, 0x84C1, 0x84C6, 0x84D6, 0x84A1, 0x8521, +0x84FF, 0x84F4, 0x8517, 0x8518, 0x852C, 0x851F, 0x8515, 0x8514, 0x84FC, 0x8540, 0x8563, 0x8558, 0x8548, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x8541, 0x8602, 0x854B, 0x8555, 0x8580, 0x85A4, 0x8588, 0x8591, 0x858A, 0x85A8, 0x856D, 0x8594, 0x859B, 0x85EA, 0x8587, 0x859C, +0x8577, 0x857E, 0x8590, 0x85C9, 0x85BA, 0x85CF, 0x85B9, 0x85D0, 0x85D5, 0x85DD, 0x85E5, 0x85DC, 0x85F9, 0x860A, 0x8613, 0x860B, +0x85FE, 0x85FA, 0x8606, 0x8622, 0x861A, 0x8630, 0x863F, 0x864D, 0x4E55, 0x8654, 0x865F, 0x8667, 0x8671, 0x8693, 0x86A3, 0x86A9, +0x86AA, 0x868B, 0x868C, 0x86B6, 0x86AF, 0x86C4, 0x86C6, 0x86B0, 0x86C9, 0x8823, 0x86AB, 0x86D4, 0x86DE, 0x86E9, 0x86EC, 0x0000, +0x86DF, 0x86DB, 0x86EF, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703, 0x86FB, 0x8711, 0x8709, 0x870D, 0x86F9, 0x870A, 0x8734, 0x873F, +0x8737, 0x873B, 0x8725, 0x8729, 0x871A, 0x8760, 0x875F, 0x8778, 0x874C, 0x874E, 0x8774, 0x8757, 0x8768, 0x876E, 0x8759, 0x8753, +0x8763, 0x876A, 0x8805, 0x87A2, 0x879F, 0x8782, 0x87AF, 0x87CB, 0x87BD, 0x87C0, 0x87D0, 0x96D6, 0x87AB, 0x87C4, 0x87B3, 0x87C7, +0x87C6, 0x87BB, 0x87EF, 0x87F2, 0x87E0, 0x880F, 0x880D, 0x87FE, 0x87F6, 0x87F7, 0x880E, 0x87D2, 0x8811, 0x8816, 0x8815, 0x8822, +0x8821, 0x8831, 0x8836, 0x8839, 0x8827, 0x883B, 0x8844, 0x8842, 0x8852, 0x8859, 0x885E, 0x8862, 0x886B, 0x8881, 0x887E, 0x889E, +0x8875, 0x887D, 0x88B5, 0x8872, 0x8882, 0x8897, 0x8892, 0x88AE, 0x8899, 0x88A2, 0x888D, 0x88A4, 0x88B0, 0x88BF, 0x88B1, 0x88C3, +0x88C4, 0x88D4, 0x88D8, 0x88D9, 0x88DD, 0x88F9, 0x8902, 0x88FC, 0x88F4, 0x88E8, 0x88F2, 0x8904, 0x890C, 0x890A, 0x8913, 0x8943, +0x891E, 0x8925, 0x892A, 0x892B, 0x8941, 0x8944, 0x893B, 0x8936, 0x8938, 0x894C, 0x891D, 0x8960, 0x895E, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x8966, 0x8964, 0x896D, 0x896A, 0x896F, 0x8974, 0x8977, 0x897E, 0x8983, 0x8988, 0x898A, 0x8993, 0x8998, 0x89A1, 0x89A9, 0x89A6, +0x89AC, 0x89AF, 0x89B2, 0x89BA, 0x89BD, 0x89BF, 0x89C0, 0x89DA, 0x89DC, 0x89DD, 0x89E7, 0x89F4, 0x89F8, 0x8A03, 0x8A16, 0x8A10, +0x8A0C, 0x8A1B, 0x8A1D, 0x8A25, 0x8A36, 0x8A41, 0x8A5B, 0x8A52, 0x8A46, 0x8A48, 0x8A7C, 0x8A6D, 0x8A6C, 0x8A62, 0x8A85, 0x8A82, +0x8A84, 0x8AA8, 0x8AA1, 0x8A91, 0x8AA5, 0x8AA6, 0x8A9A, 0x8AA3, 0x8AC4, 0x8ACD, 0x8AC2, 0x8ADA, 0x8AEB, 0x8AF3, 0x8AE7, 0x0000, +0x8AE4, 0x8AF1, 0x8B14, 0x8AE0, 0x8AE2, 0x8AF7, 0x8ADE, 0x8ADB, 0x8B0C, 0x8B07, 0x8B1A, 0x8AE1, 0x8B16, 0x8B10, 0x8B17, 0x8B20, +0x8B33, 0x97AB, 0x8B26, 0x8B2B, 0x8B3E, 0x8B28, 0x8B41, 0x8B4C, 0x8B4F, 0x8B4E, 0x8B49, 0x8B56, 0x8B5B, 0x8B5A, 0x8B6B, 0x8B5F, +0x8B6C, 0x8B6F, 0x8B74, 0x8B7D, 0x8B80, 0x8B8C, 0x8B8E, 0x8B92, 0x8B93, 0x8B96, 0x8B99, 0x8B9A, 0x8C3A, 0x8C41, 0x8C3F, 0x8C48, +0x8C4C, 0x8C4E, 0x8C50, 0x8C55, 0x8C62, 0x8C6C, 0x8C78, 0x8C7A, 0x8C82, 0x8C89, 0x8C85, 0x8C8A, 0x8C8D, 0x8C8E, 0x8C94, 0x8C7C, +0x8C98, 0x621D, 0x8CAD, 0x8CAA, 0x8CBD, 0x8CB2, 0x8CB3, 0x8CAE, 0x8CB6, 0x8CC8, 0x8CC1, 0x8CE4, 0x8CE3, 0x8CDA, 0x8CFD, 0x8CFA, +0x8CFB, 0x8D04, 0x8D05, 0x8D0A, 0x8D07, 0x8D0F, 0x8D0D, 0x8D10, 0x9F4E, 0x8D13, 0x8CCD, 0x8D14, 0x8D16, 0x8D67, 0x8D6D, 0x8D71, +0x8D73, 0x8D81, 0x8D99, 0x8DC2, 0x8DBE, 0x8DBA, 0x8DCF, 0x8DDA, 0x8DD6, 0x8DCC, 0x8DDB, 0x8DCB, 0x8DEA, 0x8DEB, 0x8DDF, 0x8DE3, +0x8DFC, 0x8E08, 0x8E09, 0x8DFF, 0x8E1D, 0x8E1E, 0x8E10, 0x8E1F, 0x8E42, 0x8E35, 0x8E30, 0x8E34, 0x8E4A, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x8E47, 0x8E49, 0x8E4C, 0x8E50, 0x8E48, 0x8E59, 0x8E64, 0x8E60, 0x8E2A, 0x8E63, 0x8E55, 0x8E76, 0x8E72, 0x8E7C, 0x8E81, 0x8E87, +0x8E85, 0x8E84, 0x8E8B, 0x8E8A, 0x8E93, 0x8E91, 0x8E94, 0x8E99, 0x8EAA, 0x8EA1, 0x8EAC, 0x8EB0, 0x8EC6, 0x8EB1, 0x8EBE, 0x8EC5, +0x8EC8, 0x8ECB, 0x8EDB, 0x8EE3, 0x8EFC, 0x8EFB, 0x8EEB, 0x8EFE, 0x8F0A, 0x8F05, 0x8F15, 0x8F12, 0x8F19, 0x8F13, 0x8F1C, 0x8F1F, +0x8F1B, 0x8F0C, 0x8F26, 0x8F33, 0x8F3B, 0x8F39, 0x8F45, 0x8F42, 0x8F3E, 0x8F4C, 0x8F49, 0x8F46, 0x8F4E, 0x8F57, 0x8F5C, 0x0000, +0x8F62, 0x8F63, 0x8F64, 0x8F9C, 0x8F9F, 0x8FA3, 0x8FAD, 0x8FAF, 0x8FB7, 0x8FDA, 0x8FE5, 0x8FE2, 0x8FEA, 0x8FEF, 0x9087, 0x8FF4, +0x9005, 0x8FF9, 0x8FFA, 0x9011, 0x9015, 0x9021, 0x900D, 0x901E, 0x9016, 0x900B, 0x9027, 0x9036, 0x9035, 0x9039, 0x8FF8, 0x904F, +0x9050, 0x9051, 0x9052, 0x900E, 0x9049, 0x903E, 0x9056, 0x9058, 0x905E, 0x9068, 0x906F, 0x9076, 0x96A8, 0x9072, 0x9082, 0x907D, +0x9081, 0x9080, 0x908A, 0x9089, 0x908F, 0x90A8, 0x90AF, 0x90B1, 0x90B5, 0x90E2, 0x90E4, 0x6248, 0x90DB, 0x9102, 0x9112, 0x9119, +0x9132, 0x9130, 0x914A, 0x9156, 0x9158, 0x9163, 0x9165, 0x9169, 0x9173, 0x9172, 0x918B, 0x9189, 0x9182, 0x91A2, 0x91AB, 0x91AF, +0x91AA, 0x91B5, 0x91B4, 0x91BA, 0x91C0, 0x91C1, 0x91C9, 0x91CB, 0x91D0, 0x91D6, 0x91DF, 0x91E1, 0x91DB, 0x91FC, 0x91F5, 0x91F6, +0x921E, 0x91FF, 0x9214, 0x922C, 0x9215, 0x9211, 0x925E, 0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923F, 0x924B, 0x9250, +0x929C, 0x9296, 0x9293, 0x929B, 0x925A, 0x92CF, 0x92B9, 0x92B7, 0x92E9, 0x930F, 0x92FA, 0x9344, 0x932E, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x9319, 0x9322, 0x931A, 0x9323, 0x933A, 0x9335, 0x933B, 0x935C, 0x9360, 0x937C, 0x936E, 0x9356, 0x93B0, 0x93AC, 0x93AD, 0x9394, +0x93B9, 0x93D6, 0x93D7, 0x93E8, 0x93E5, 0x93D8, 0x93C3, 0x93DD, 0x93D0, 0x93C8, 0x93E4, 0x941A, 0x9414, 0x9413, 0x9403, 0x9407, +0x9410, 0x9436, 0x942B, 0x9435, 0x9421, 0x943A, 0x9441, 0x9452, 0x9444, 0x945B, 0x9460, 0x9462, 0x945E, 0x946A, 0x9229, 0x9470, +0x9475, 0x9477, 0x947D, 0x945A, 0x947C, 0x947E, 0x9481, 0x947F, 0x9582, 0x9587, 0x958A, 0x9594, 0x9596, 0x9598, 0x9599, 0x0000, +0x95A0, 0x95A8, 0x95A7, 0x95AD, 0x95BC, 0x95BB, 0x95B9, 0x95BE, 0x95CA, 0x6FF6, 0x95C3, 0x95CD, 0x95CC, 0x95D5, 0x95D4, 0x95D6, +0x95DC, 0x95E1, 0x95E5, 0x95E2, 0x9621, 0x9628, 0x962E, 0x962F, 0x9642, 0x964C, 0x964F, 0x964B, 0x9677, 0x965C, 0x965E, 0x965D, +0x965F, 0x9666, 0x9672, 0x966C, 0x968D, 0x9698, 0x9695, 0x9697, 0x96AA, 0x96A7, 0x96B1, 0x96B2, 0x96B0, 0x96B4, 0x96B6, 0x96B8, +0x96B9, 0x96CE, 0x96CB, 0x96C9, 0x96CD, 0x894D, 0x96DC, 0x970D, 0x96D5, 0x96F9, 0x9704, 0x9706, 0x9708, 0x9713, 0x970E, 0x9711, +0x970F, 0x9716, 0x9719, 0x9724, 0x972A, 0x9730, 0x9739, 0x973D, 0x973E, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749, 0x975C, 0x9760, +0x9764, 0x9766, 0x9768, 0x52D2, 0x976B, 0x9771, 0x9779, 0x9785, 0x977C, 0x9781, 0x977A, 0x9786, 0x978B, 0x978F, 0x9790, 0x979C, +0x97A8, 0x97A6, 0x97A3, 0x97B3, 0x97B4, 0x97C3, 0x97C6, 0x97C8, 0x97CB, 0x97DC, 0x97ED, 0x9F4F, 0x97F2, 0x7ADF, 0x97F6, 0x97F5, +0x980F, 0x980C, 0x9838, 0x9824, 0x9821, 0x9837, 0x983D, 0x9846, 0x984F, 0x984B, 0x986B, 0x986F, 0x9870, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x9871, 0x9874, 0x9873, 0x98AA, 0x98AF, 0x98B1, 0x98B6, 0x98C4, 0x98C3, 0x98C6, 0x98E9, 0x98EB, 0x9903, 0x9909, 0x9912, 0x9914, +0x9918, 0x9921, 0x991D, 0x991E, 0x9924, 0x9920, 0x992C, 0x992E, 0x993D, 0x993E, 0x9942, 0x9949, 0x9945, 0x9950, 0x994B, 0x9951, +0x9952, 0x994C, 0x9955, 0x9997, 0x9998, 0x99A5, 0x99AD, 0x99AE, 0x99BC, 0x99DF, 0x99DB, 0x99DD, 0x99D8, 0x99D1, 0x99ED, 0x99EE, +0x99F1, 0x99F2, 0x99FB, 0x99F8, 0x9A01, 0x9A0F, 0x9A05, 0x99E2, 0x9A19, 0x9A2B, 0x9A37, 0x9A45, 0x9A42, 0x9A40, 0x9A43, 0x0000, +0x9A3E, 0x9A55, 0x9A4D, 0x9A5B, 0x9A57, 0x9A5F, 0x9A62, 0x9A65, 0x9A64, 0x9A69, 0x9A6B, 0x9A6A, 0x9AAD, 0x9AB0, 0x9ABC, 0x9AC0, +0x9ACF, 0x9AD1, 0x9AD3, 0x9AD4, 0x9ADE, 0x9ADF, 0x9AE2, 0x9AE3, 0x9AE6, 0x9AEF, 0x9AEB, 0x9AEE, 0x9AF4, 0x9AF1, 0x9AF7, 0x9AFB, +0x9B06, 0x9B18, 0x9B1A, 0x9B1F, 0x9B22, 0x9B23, 0x9B25, 0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2E, 0x9B2F, 0x9B32, 0x9B44, 0x9B43, +0x9B4F, 0x9B4D, 0x9B4E, 0x9B51, 0x9B58, 0x9B74, 0x9B93, 0x9B83, 0x9B91, 0x9B96, 0x9B97, 0x9B9F, 0x9BA0, 0x9BA8, 0x9BB4, 0x9BC0, +0x9BCA, 0x9BB9, 0x9BC6, 0x9BCF, 0x9BD1, 0x9BD2, 0x9BE3, 0x9BE2, 0x9BE4, 0x9BD4, 0x9BE1, 0x9C3A, 0x9BF2, 0x9BF1, 0x9BF0, 0x9C15, +0x9C14, 0x9C09, 0x9C13, 0x9C0C, 0x9C06, 0x9C08, 0x9C12, 0x9C0A, 0x9C04, 0x9C2E, 0x9C1B, 0x9C25, 0x9C24, 0x9C21, 0x9C30, 0x9C47, +0x9C32, 0x9C46, 0x9C3E, 0x9C5A, 0x9C60, 0x9C67, 0x9C76, 0x9C78, 0x9CE7, 0x9CEC, 0x9CF0, 0x9D09, 0x9D08, 0x9CEB, 0x9D03, 0x9D06, +0x9D2A, 0x9D26, 0x9DAF, 0x9D23, 0x9D1F, 0x9D44, 0x9D15, 0x9D12, 0x9D41, 0x9D3F, 0x9D3E, 0x9D46, 0x9D48, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x9D5D, 0x9D5E, 0x9D64, 0x9D51, 0x9D50, 0x9D59, 0x9D72, 0x9D89, 0x9D87, 0x9DAB, 0x9D6F, 0x9D7A, 0x9D9A, 0x9DA4, 0x9DA9, 0x9DB2, +0x9DC4, 0x9DC1, 0x9DBB, 0x9DB8, 0x9DBA, 0x9DC6, 0x9DCF, 0x9DC2, 0x9DD9, 0x9DD3, 0x9DF8, 0x9DE6, 0x9DED, 0x9DEF, 0x9DFD, 0x9E1A, +0x9E1B, 0x9E1E, 0x9E75, 0x9E79, 0x9E7D, 0x9E81, 0x9E88, 0x9E8B, 0x9E8C, 0x9E92, 0x9E95, 0x9E91, 0x9E9D, 0x9EA5, 0x9EA9, 0x9EB8, +0x9EAA, 0x9EAD, 0x9761, 0x9ECC, 0x9ECE, 0x9ECF, 0x9ED0, 0x9ED4, 0x9EDC, 0x9EDE, 0x9EDD, 0x9EE0, 0x9EE5, 0x9EE8, 0x9EEF, 0x0000, +0x9EF4, 0x9EF6, 0x9EF7, 0x9EF9, 0x9EFB, 0x9EFC, 0x9EFD, 0x9F07, 0x9F08, 0x76B7, 0x9F15, 0x9F21, 0x9F2C, 0x9F3E, 0x9F4A, 0x9F52, +0x9F54, 0x9F63, 0x9F5F, 0x9F60, 0x9F61, 0x9F66, 0x9F67, 0x9F6C, 0x9F6A, 0x9F77, 0x9F72, 0x9F76, 0x9F95, 0x9F9C, 0x9FA0, 0x582F, +0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0xFFFF +}; + +// Sneaky struct for interating thru multiple tables (SJIS) +struct table_segment { + const uint16 *table; + uint16 offset; +}; + +const table_segment sjistables[] = { {sjis00tou, 0x0000}, + {sjis81tou, 0x8100}, + {sjise0tou, 0xE000}, + {NULL, 0x0000} }; + + +// From EncodingComversions.cpp + +// Pierre's Uber Macro +#define u_lendian_to_utf8(str, uni_str)\ +{\ + if ((B_LENDIAN_TO_HOST_INT16(uni_str[0])&0xff80) == 0)\ + *str++ = B_LENDIAN_TO_HOST_INT16(*uni_str++);\ + else if ((B_LENDIAN_TO_HOST_INT16(uni_str[0])&0xf800) == 0) {\ + str[0] = 0xc0|(B_LENDIAN_TO_HOST_INT16(uni_str[0])>>6);\ + str[1] = 0x80|(B_LENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\ + str += 2;\ + } else if ((B_LENDIAN_TO_HOST_INT16(uni_str[0])&0xfc00) != 0xd800) {\ + str[0] = 0xe0|(B_LENDIAN_TO_HOST_INT16(uni_str[0])>>12);\ + str[1] = 0x80|((B_LENDIAN_TO_HOST_INT16(uni_str[0])>>6)&0x3f);\ + str[2] = 0x80|(B_LENDIAN_TO_HOST_INT16(*uni_str++)&0x3f);\ + str += 3;\ + } else {\ + int val;\ + val = ((B_LENDIAN_TO_HOST_INT16(uni_str[0])-0xd7c0)<<10) | (B_LENDIAN_TO_HOST_INT16(uni_str[1])&0x3ff);\ + str[0] = 0xf0 | (val>>18);\ + str[1] = 0x80 | ((val>>12)&0x3f);\ + str[2] = 0x80 | ((val>>6)&0x3f);\ + str[3] = 0x80 | (val&0x3f);\ + uni_str += 2; str += 4;\ + }\ +} + +// Pierre's Uber Macro +#define u_hostendian_to_utf8(str, uni_str)\ +{\ + if ((uni_str[0]&0xff80) == 0)\ + *str++ = *uni_str++;\ + else if ((uni_str[0]&0xf800) == 0) {\ + str[0] = 0xc0|(uni_str[0]>>6);\ + str[1] = 0x80|(*uni_str++&0x3f);\ + str += 2;\ + } else if ((uni_str[0]&0xfc00) != 0xd800) {\ + str[0] = 0xe0|(uni_str[0]>>12);\ + str[1] = 0x80|((uni_str[0]>>6)&0x3f);\ + str[2] = 0x80|(*uni_str++&0x3f);\ + str += 3;\ + } else {\ + int val;\ + val = ((uni_str[0]-0xd7c0)<<10) | (uni_str[1]&0x3ff);\ + str[0] = 0xf0 | (val>>18);\ + str[1] = 0x80 | ((val>>12)&0x3f);\ + str[2] = 0x80 | ((val>>6)&0x3f);\ + str[3] = 0x80 | (val&0x3f);\ + uni_str += 2; str += 4;\ + }\ +} + +// Another Uber Macro +#define utf8_to_u_hostendian(str, uni_str, err_flag) \ +{\ + err_flag = 0;\ + if ((str[0]&0x80) == 0)\ + *uni_str++ = *str++;\ + else if ((str[1] & 0xC0) != 0x80) {\ + *uni_str++ = 0xfffd;\ + str+=1;\ + } else if ((str[0]&0x20) == 0) {\ + *uni_str++ = ((str[0]&31)<<6) | (str[1]&63);\ + str+=2;\ + } else if ((str[2] & 0xC0) != 0x80) {\ + *uni_str++ = 0xfffd;\ + str+=2;\ + } else if ((str[0]&0x10) == 0) {\ + *uni_str++ = ((str[0]&15)<<12) | ((str[1]&63)<<6) | (str[2]&63);\ + str+=3;\ + } else if ((str[3] & 0xC0) != 0x80) {\ + *uni_str++ = 0xfffd;\ + str+=3;\ + } else {\ + err_flag = 1;\ + }\ +} + +// Count the number of bytes of a UTF-8 character +#define utf8_char_len(c) ((((int32)0xE5000000 >> ((c >> 3) & 0x1E)) & 3) + 1) + +// converts LENDIAN unicode to utf8 +static status_t +_lendian_unicode_to_utf8( + const char *src, + int32 *srcLen, + char *dst, + int32 *dstLen) +{ + int32 srcLimit = *srcLen; + int32 dstLimit = *dstLen; + int32 srcCount = 0; + int32 dstCount = 0; + + for (srcCount = 0; srcCount < srcLimit; srcCount += 2) { + uint16 *UNICODE = (uint16 *)&src[srcCount]; + uchar utf8[4]; + uchar *UTF8 = utf8; + int32 utf8Len; + int32 j; + + u_lendian_to_utf8(UTF8, UNICODE); + + utf8Len = UTF8 - utf8; + if ((dstCount + utf8Len) > dstLimit) + break; + + for (j = 0; j < utf8Len; j++) + dst[dstCount + j] = utf8[j]; + dstCount += utf8Len; + } + + *srcLen = srcCount; + *dstLen = dstCount; + + return ((dstCount > 0) ? B_NO_ERROR : B_ERROR); +} + +// utf8 to LENDIAN unicode +static status_t +_utf8_to_lendian_unicode( + const char *src, + int32 *srcLen, + char *dst, + int32 *dstLen) +{ + int32 srcLimit = *srcLen; + int32 dstLimit = *dstLen - 1; + int32 srcCount = 0; + int32 dstCount = 0; + + while ((srcCount < srcLimit) && (dstCount < dstLimit)) { + uint16 unicode; + uint16 *UNICODE = &unicode; + uchar *UTF8 = (uchar *)src + srcCount; + int err_flag; + + if ((srcCount + utf8_char_len(src[srcCount])) > srcLimit) + break; + + utf8_to_u_hostendian(UTF8, UNICODE, err_flag); + if(err_flag == 1) + return EINVAL; + + unicode = B_HOST_TO_LENDIAN_INT16(unicode); + dst[dstCount++] = unicode & 0xFF; + dst[dstCount++] = unicode >> 8; + + srcCount += UTF8 - ((uchar *)(src + srcCount)); + } + + *srcLen = srcCount; + *dstLen = dstCount; + + return ((dstCount > 0) ? B_NO_ERROR : B_ERROR); +} + +// table translation to utf8 +static status_t +_one_to_utf8( + const uint16 *table, + const char *src, + int32 *srcLen, + char *dst, + int32 *dstLen) +{ + int32 srcLimit = *srcLen; + int32 dstLimit = *dstLen; + int32 srcCount = 0; + int32 dstCount = 0; + + for (srcCount = 0; srcCount < srcLimit; srcCount++) { + // table should be in host endian + uint16 unicode = table[(uchar)src[srcCount]]; + uint16 *UNICODE = &unicode; + uchar utf8[4]; + uchar *UTF8 = utf8; + + *(uint32 *)utf8 = 0; + u_hostendian_to_utf8(UTF8, UNICODE); + + int32 utf8Len = UTF8 - utf8; + if ((dstCount + utf8Len) > dstLimit) + break; + + for (int32 j = 0; j < utf8Len; j++) + dst[dstCount + j] = utf8[j]; + dstCount += utf8Len; + } + + *srcLen = srcCount; + *dstLen = dstCount; + + return ((dstCount > 0) ? B_NO_ERROR : B_ERROR); +} + +// converts utf8 to BENDIAN sjis +status_t +_utf8_to_sjis_bendian( + const char *src, + int32 *srcLen, + char *dst, + int32 *dstLen) +{ + int32 srcLimit = *srcLen; + int32 dstLimit = *dstLen; + int32 srcCount = 0; + int32 dstCount = 0; + + while ((srcCount < srcLimit) && (dstCount < dstLimit)) { + if ((srcCount + utf8_char_len(src[srcCount])) > srcLimit) + break; + + uint16 unicode; + uint16 *UNICODE = &unicode; + int err_flag; + bool multibyte = false; + const uint16 *table = NULL; + uchar *UTF8 = (uchar *)src + srcCount; + + utf8_to_u_hostendian(UTF8, UNICODE, err_flag); + if(err_flag == 1) + return EINVAL; + + // XXX horrible horrible way to convert + for (int32 t = 0; (table = sjistables[t].table) != NULL; t++) { + for (int32 i = 0; table[i] != 0xFFFF; i++) { + if (unicode == table[i]) { + uint16 offset = sjistables[t].offset; + + if (offset == 0x0000) + dst[dstCount] = i; + else { + if ((dstCount + 1) < dstLimit) { + uint16 sjis = offset + i; + // bendian + *(uint16 *)&dst[dstCount] = B_HOST_TO_BENDIAN_INT16(sjis); + multibyte = true; + } else + goto exit; + } + goto donesearch; + } + } + } + // we must not have found it + dst[dstCount] = 0x1a; + +donesearch: + srcCount += UTF8 - ((uchar *)(src + srcCount)); + dstCount += (multibyte) ? 2 : 1; + } + +exit: + *srcLen = srcCount; + *dstLen = dstCount; + + return ((dstCount > 0) ? B_NO_ERROR : B_ERROR); +} + +inline bool is_unicode_japanese(uint16 c) +{ + if (((c >= 0x3000) && (c <= 0x30ff)) || + ((c >= 0x3200) && (c <= 0x3400)) || + ((c >= 0x4e00) && (c <= 0x9fff)) || + ((c >= 0xf900) && (c <= 0xfaff)) || + ((c >= 0xfe30) && (c <= 0xfe6f)) || + ((c >= 0xff00) && (c <= 0xffef))) + return true; + + return false; +} + +inline bool is_initial_sjis_byte(uchar c) +{ + return (((c >= 0x81) && (c <= 0x9F)) || + ((c >= 0xE0) && (c <= 0xEF)) ); +} + +// takes a unicode name of unilen uchar's and converts to a utf8 name of at +// most utf8len uint8's +status_t unicode_to_utf8(const uchar *uni, uint32 unilen, uint8 *utf8, + uint32 utf8len) +{ + status_t result; + uint32 origlen = unilen; + + DPRINTF(0, ("unicode_to_utf8\n")); + + result = _lendian_unicode_to_utf8((char *)uni, + (int32 *)&unilen, (char *)utf8, (int32 *)&utf8len); + + if (unilen < origlen) { + dprintf("Name is too long (%lx < %lx)\n", unilen, origlen); + return B_ERROR; + } + + return result; +} + +// from dir.c +extern "C" { + extern const char acceptable[]; + extern const char illegal[]; +} +const char underbar[] = "+,;=[]" + "\x83\x85\x88\x89\x8A\x8B\x8C\x8D" + "\x93\x95\x96\x97\x98" + "\xA0\xA1\xA2\xA3"; +const char capitalize_from[] = "\x81\x82\x84\x86\x87\x91\x94\xA4"; +const char capitalize_to[] = "\x9A\x90\x8E\x8F\x80\x92\x99\xA5"; + +/* XXX: this doesn't work quite right, but it's okay for now since having + * lfn's where they aren't needed doesn't hurt */ +bool requires_long_name(const char *utf8, const uchar *unicode) +{ + int i, j; + + DPRINTF(0, ("requires_long_name\n")); + + for (i=0;unicode[i] || unicode[i+1];i+=2) { + uint16 ar = (unicode[i+1] << 8) | unicode[i]; + if (is_unicode_japanese(ar)) { + DPRINTF(1, ("All Japanese names require long file names for now\n")); + return true; + } + } + + for (i=0;i<8;i++) { + if (utf8[i] == 0) return false; + if (utf8[i] == '.') break; + /* XXX: should also check for upper-ascii stuff (requires matching + * unicode with msdostou table, but doesn't hurt for now */ + if (!strchr(acceptable, utf8[i])) return true; + } + + if (utf8[i] == 0) return false; + if ((i == 8) && (utf8[i] != '.')) return true; /* name too long */ + i++; + if (utf8[i] == 0) return true; /* filenames with trailing periods */ + + for (j=0;j<3;j++,i++) { + if (utf8[i] == 0) return false; + /* XXX: same here */ + if (!strchr(acceptable, utf8[i])) return true; + } + + return (utf8[i] == 0) ? false : true; +} + +status_t utf8_to_unicode(const char *utf8, uchar *uni, uint32 unilen) +{ + status_t result; + uint32 utf8len, origlen; + + DPRINTF(0, ("utf8_to_unicode\n")); + + utf8len = origlen = strlen(utf8) + 1; + + result = _utf8_to_lendian_unicode(utf8, + (int32 *)&utf8len, (char *)uni, (int32 *)&unilen); + + if (origlen < utf8len) { + dprintf("Name is too long (%lx < %lx)\n", unilen, origlen); + return B_ERROR; + } + + return (result == B_OK) ? unilen : result; +} + +static +status_t munge_short_name_english(uchar nshort[11], uint64 value) +{ + char buffer[8]; + int len, i; + + DPRINTF(0, ("munge_short_name_english\n")); + + // short names must have only numbers following + // the tilde and cannot begin with 0 + sprintf(buffer, "~%Ld", value); + len = strlen(buffer); + i = 7 - len; + + ASSERT((i > 0) && (i < 8)); + + while ((nshort[i] == ' ') && (i > 0)) i--; + i++; + + memcpy(nshort + i, buffer, len); + + return B_OK; +} + +static +status_t munge_short_name_sjis(uchar nshort[11], uint64 value) +{ + char buffer[8]; + int len, i, last; + + DPRINTF(0, ("munge_short_name_sjis\n")); + + // short names must have only numbers following + // the tilde and cannot begin with 0 + sprintf(buffer, "~%Ld", value); + len = strlen(buffer); + + last = 0; + for (i=0;i<=8-len;i++) { + last = i; + if (nshort[i] == ' ') break; + if (is_initial_sjis_byte(nshort[i])) i++; + } + + memcpy(nshort + last, buffer, len); + memset(nshort + last + len, ' ', 8 - (last + len)); + + return B_OK; +} + +status_t munge_short_name2(uchar nshort[11], int encoding) +{ + uint64 value; + + DPRINTF(0, ("munge_short_name2\n")); + + value = system_time() % 100000LL; + if (!value) value++; + + /* XXX: what if it's zero? */ + if (encoding == MS_DOS_CONVERSION) + return munge_short_name_english(nshort, value); + else if (encoding == SJIS_CONVERSION) + return munge_short_name_sjis(nshort, value); + + return EINVAL; +} + +status_t munge_short_name1(uchar nshort[11], int iteration, int encoding) +{ + DPRINTF(0, ("munge_short_name1\n")); + + if (encoding == MS_DOS_CONVERSION) + return munge_short_name_english(nshort, iteration); + else if (encoding == SJIS_CONVERSION) + return munge_short_name_sjis(nshort, iteration); + + return EINVAL; +} + +/* + rules: + 1. if there are multiple '.'s in the name, the last one signals the + extension. else scandisk complains. + 2. a lower case letter gets translated to its upper case equivalent in the + short name. this includes weird upper-ascii letters. + 3. devices (CON, PRN, etc) are not valid short names. (implemented in + dir.c) + 4. ignore leading '.'s; a name of all '.'s is illegal +*/ + +static status_t +generate_short_name_msdos(const uchar *utf8, const uint16 *uni, + uint32 unilen, uchar nshort[11]) +{ + int i; + char *cp; + const uint16 *puni; + + TOUCH(utf8); + + DPRINTF(0, ("generate_short_name_msdos\n")); + + puni = uni; + for (i=0; (i < 8) && (puni < uni + unilen / 2); puni++) { + uint16 match; + uint32 c; + + match = B_LENDIAN_TO_HOST_INT16(*puni); + + if (match == 0) return B_OK; + if (match == '.') { + /* skip leading dots */ + if (i == 0) continue; else break; + } + + for (c = 0; c < 0x100; c++) + if (msdostou[c] == match) + break; + + if (c < 0x100) { + if (strchr(illegal, c)) return EINVAL; + + if ((c >= 'a') && (c <= 'z')) + nshort[i++] = c - 'a' + 'A'; + else if (strchr(underbar, c)) + nshort[i++] = '_'; + else if ((cp = strchr(capitalize_from, c)) != NULL) + nshort[i++] = capitalize_to[(int)(cp - capitalize_from)]; + else if (strchr(acceptable, c) || strchr(capitalize_to, c)) + nshort[i++] = c; + } + } + + /* find the final dot */ + for (puni = uni + unilen / 2 - 1; puni >= uni; puni--) + if (B_LENDIAN_TO_HOST_INT16(*puni) == '.') + break; + + if (puni < uni) return B_OK; + + puni ++; + + for (i=8; (i < 11) && (puni < uni + unilen / 2); puni++) { + uint16 match; + uint32 c; + + match = B_LENDIAN_TO_HOST_INT16(*puni); + if (match == 0) return B_OK; + + for (c=0; c<0x100; c++) + if (msdostou[c] == match) + break; + + if (c < 0x100) { + if (strchr(illegal, c)) return EINVAL; + + if ((c >= 'a') && (c <= 'z')) + nshort[i++] = c - 'a' + 'A'; + else if (strchr(underbar, c)) + nshort[i++] = '_'; + else if ((cp = strchr(capitalize_from, c)) != NULL) + nshort[i++] = capitalize_to[(int)(cp - capitalize_from)]; + else if (strchr(acceptable, c) || strchr(capitalize_to, c)) + nshort[i++] = c; + } + } + + return B_OK; +} + +static status_t +generate_short_name_sjis(const uchar *utf8, const uint16 *uni, + uint32 unilen, uchar nshort[11]) +{ + uchar *sjis, *s, *t; + int32 utf8len, sjislen; + status_t result; + int32 state = 0; + + TOUCH(uni); + + DPRINTF(0, ("generate_short_name_sjis\n")); + + sjis = (uchar *)malloc(unilen); + if (sjis == NULL) return ENOMEM; + + utf8len = strlen((const char *)utf8) + 1; + sjislen = unilen; + + result = _utf8_to_sjis_bendian((char *)utf8, + (int32 *)&utf8len, (char *)sjis, (int32 *)&sjislen); + + if (result < 0) goto bi; + + if (utf8len < (int32)strlen((const char *)utf8) + 1) { + result = EINVAL; + goto bi; + } + + result = B_OK; + + s = sjis; + while (*s == '.') s++; + for (state=0;state<8;) { + if (is_initial_sjis_byte(*s)) { + if (state < 7) { + nshort[state++] = *(s++); + } else + break; + } else { + if (*s == '.') break; + if (*s == 0) goto bi; + if ((*s >= 'a') && (*s <= 'z')) *s = *s - 'a' + 'A'; + } + nshort[state++] = *(s++); + } + + t = sjis + strlen((const char *)sjis) - 1; + while ((t >= s) && (*t != '.')) + t--; + + if (t >= s) { + s = t+1; + for (state=8;state<11;) { + if (is_initial_sjis_byte(*s)) { + if (state < 10) { + nshort[state++] = *(s++); + } else + break; + } else { + if (*s == 0) break; + if ((*s >= 'a') && (*s <= 'z')) *s = *s - 'a' + 'A'; + } + nshort[state++] = *(s++); + } + } + +bi: + if (result < 0) { + dprintf("generate_short_name_sjis error: %lx (%s)\n", result, strerror(result)); + } + + free(sjis); + + return result; +} + +status_t generate_short_name(const uchar *name, const uchar *uni, + uint32 unilen, uchar nshort[11], int *encoding) +{ + uint32 i; + status_t result; + + DPRINTF(0, ("generate_short_name\n")); + + ASSERT(encoding != NULL); + memset(nshort, ' ', 11); + + /* assume english by default */ + *encoding = MS_DOS_CONVERSION; + + /* check for japanese */ + for (i=0;i 8) return true; + if ((nshort[leading - 1] == '_') && (*utf8name != '_')) return true; + } + + if (*utf8name != 0) { + utf8name++; + + for ( ; *utf8name != 0; utf8name++) { + if (!BEGINS_UTF8CHAR(*utf8name)) continue; + if (*utf8name == '.') return true; + trailing++; + if (trailing > 3) return true; + if ((nshort[leading + trailing - 1] == '_') && (*utf8name != '_')) return true; + } + } + + for (i = 0, len = 0; i < 8; i++) + if (nshort[i] != ' ') len++; + if (len != leading) return true; + + for (i = 8, len = 0; i < 11; i++) + if (nshort[i] != ' ') len++; + if (len != trailing) return true; + + return false; +} + +#ifdef USER + +int main(int argc, char **argv) +{ + int i; + + for (i=1;i 0) { + char nshort[11]; + int enc; + result = generate_short_name(unicode, result, nshort, &enc); + printf("short [%s] ", nshort); + munge_short_name1(nshort, 1234, MS_DOS_CONVERSION); + printf("munged [%s] ", nshort); + printf("long name: %x\n", requires_long_name(argv[i])); + } + printf("\n"); + } + + return 0; +} + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/encodings.h b/src/tests/add-ons/kernel/file_systems/dos/r5/encodings.h new file mode 100644 index 0000000000..782c4922ac --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/encodings.h @@ -0,0 +1,31 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_ENCODINGS_H_ +#define _DOSFS_ENCODINGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +status_t unicode_to_utf8(const uchar *uni, uint32 unilen, uint8 *utf8, + uint32 utf8len); + +bool requires_munged_short_name(const uchar *utf8name, + const uchar nshort[11], int encoding); + +bool requires_long_name(const char *utf8, const uchar *unicode); +status_t utf8_to_unicode(const char *utf8, uchar *uni, uint32 unilen); +status_t munge_short_name2(uchar nshort[11], int encoding); +status_t munge_short_name1(uchar nshort[11], int iteration, int encoding); +status_t generate_short_name(const uchar *name, const uchar *uni, + uint32 unilen, uchar nshort[11], int *encoding); + +status_t msdos_to_utf8(uchar *msdos, uchar *utf8, uint32 utf8len, bool toLower); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/fat.c b/src/tests/add-ons/kernel/file_systems/dos/r5/fat.c new file mode 100644 index 0000000000..90aaaf5068 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/fat.c @@ -0,0 +1,644 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "dosfs.h" +#include "fat.h" +#include "util.h" + +#include "file.h" +#include "vcache.h" + +#define END_FAT_ENTRY 0x0fffffff +#define BAD_FAT_ENTRY 0x0ffffff1 + +#define DPRINTF(a,b) if (debug_fat > (a)) dprintf b + +static status_t mirror_fats(nspace *vol, uint32 sector, uint8 *buffer) +{ + uint32 i; + + if (!vol->fat_mirrored) + return B_OK; + + sector -= vol->active_fat * vol->sectors_per_fat; + + for (i=0;ifat_count;i++) { + if (i == vol->active_fat) + continue; + cached_write(vol->fd, sector + i*vol->sectors_per_fat, buffer, 1, vol->bytes_per_sector); + } + + return B_OK; +} + +static int32 _count_free_clusters_fat32(nspace *vol) +{ + int32 count = 0; + uint8 *block; + uint32 fat_sector; + uint32 i; + uint32 cur_sector; + + cur_sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat; + + for(fat_sector = 0; fat_sector < vol->sectors_per_fat; fat_sector++) { + block = (uint8 *)get_block(vol->fd, cur_sector, vol->bytes_per_sector); + if(block == NULL) { + return EIO; + } + + for(i=0; i < vol->bytes_per_sector; i += sizeof(uint32)) { + uint32 val = read32(block, i); + if(val == 0) count++; + } + + release_block(vol->fd, cur_sector); + cur_sector++; + } + + return count; +} + +// count free: no parameters. returns int32 +// get_entry: cluster #. returns int32 entry/status +// set_entry: cluster #, value. returns int32 status +// allocate: # clusters in N, returns int32 status/starting cluster + +enum { _IOCTL_COUNT_FREE_, _IOCTL_GET_ENTRY_, _IOCTL_SET_ENTRY_, _IOCTL_ALLOCATE_N_ENTRIES_ }; + +static int32 _fat_ioctl_(nspace *vol, uint32 action, uint32 cluster, int32 N) +{ + int32 result = 0; + uint32 n = 0, first = 0, last = 0; + uint32 i; + uint32 sector; + uint32 off, val = 0; /* quiet warning */ + uint8 *block1, *block2 = NULL; /* quiet warning */ + + // mark end of chain for allocations + uint32 V = (action == _IOCTL_SET_ENTRY_) ? N : 0x0fffffff; + + ASSERT((action >= _IOCTL_COUNT_FREE_) && (action <= _IOCTL_ALLOCATE_N_ENTRIES_)); + + if (check_nspace_magic(vol, "_fat_ioctl_")) return EINVAL; + + DPRINTF(3, ("_fat_ioctl_: action %lx, cluster %lx, N %lx\n", action, cluster, N)); + + + if (action == _IOCTL_COUNT_FREE_) { + if(vol->fat_bits == 32) + // use a optimized version of the cluster counting algorithms + return _count_free_clusters_fat32(vol); + else + cluster = 2; + } + + if (action == _IOCTL_ALLOCATE_N_ENTRIES_) + cluster = vol->last_allocated; + + if (action != _IOCTL_COUNT_FREE_) { + if (!IS_DATA_CLUSTER(cluster)) { + DPRINTF(0, ("_fat_ioctl_ called with invalid cluster (%lx)\n", cluster)); + return EINVAL; + } + } + + off = cluster * vol->fat_bits / 8; + sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat + + off / vol->bytes_per_sector; + off %= vol->bytes_per_sector; + + if ((block1 = (uint8 *)get_block(vol->fd, sector, vol->bytes_per_sector)) == NULL) { + DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %lx)\n", sector)); + return EIO; + } + + for (i=0;itotal_clusters;i++) { + ASSERT(IS_DATA_CLUSTER(cluster)); + ASSERT(off == ((cluster * vol->fat_bits / 8) % vol->bytes_per_sector)); + + if (vol->fat_bits == 12) { + if (off == vol->bytes_per_sector - 1) { + if ((block2 = (uint8 *)get_block(vol->fd, ++sector, vol->bytes_per_sector)) == NULL) { + DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %lx)\n", sector)); + result = EIO; + sector--; + goto bi; + } + } + if (action != _IOCTL_SET_ENTRY_) { + if (off == vol->bytes_per_sector - 1) { + val = block1[off] + 0x100*block2[0]; + } else + val = block1[off] + 0x100*block1[off+1]; + if (cluster & 1) { + val >>= 4; + } else { + val &= 0xfff; + } + if (val > 0xff0) val |= 0x0ffff000; + } + if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) || + (action == _IOCTL_SET_ENTRY_)) { + uint32 andmask, ormask; + if (cluster & 1) { + ormask = (V & 0xfff) << 4; + andmask = 0xf; + } else { + ormask = V & 0xfff; + andmask = 0xf000; + } + block1[off] &= (andmask & 0xff); + block1[off] |= (ormask & 0xff); + if (off == vol->bytes_per_sector - 1) { + mark_blocks_dirty(vol->fd, sector - 1, 1); + mirror_fats(vol, sector - 1, block1); + block2[0] &= (andmask >> 8); + block2[0] |= (ormask >> 8); + } else { + block1[off+1] &= (andmask >> 8); + block1[off+1] |= (ormask >> 8); + } + } + + if (off == vol->bytes_per_sector - 1) { + off = (cluster & 1) ? 1 : 0; + release_block(vol->fd, sector - 1); + block1 = block2; + } else { + off += (cluster & 1) ? 2 : 1; + } + } else if (vol->fat_bits == 16) { + if (action != _IOCTL_SET_ENTRY_) { + val = read16(block1, off); +// val = block1[off] + 0x100*block1[off+1]; + if (val > 0xfff0) val |= 0x0fff0000; + } + if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) || + (action == _IOCTL_SET_ENTRY_)) { + *(uint16 *)&block1[off] = B_HOST_TO_LENDIAN_INT16(V); +// block1[off] = V & 0xff; +// block1[off+1] = (V >> 8) & 0xff; + } + off += 2; + } else if (vol->fat_bits == 32) { + if (action != _IOCTL_SET_ENTRY_) { + val = read32(block1, off) & 0x0fffffff; +// val = block1[off] + 0x100*block1[off+1] + +// 0x10000*block1[off+2] + 0x1000000*(block1[off+3]&0x0f); +// if (val > 0x0ffffff0) val |= 0x00000000; + } + if (((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) || + (action == _IOCTL_SET_ENTRY_)) { + ASSERT((V & 0xf0000000) == 0); + *(uint32 *)&block1[off] = B_HOST_TO_LENDIAN_INT32(V); +// block1[off] = V & 0xff; +// block1[off+1] = (V >> 8) & 0xff; +// block1[off+2] = (V >> 16) & 0xff; +// block1[off+3] = (V >> 24) & 0x0f; +// ASSERT(V == (block1[off] + 0x100*block1[off+1] + 0x10000*block1[off+2] + 0x1000000*block1[off+3])); + } + off += 4; + } else + ASSERT(0); + + if (action == _IOCTL_COUNT_FREE_) { + if (val == 0) + result++; + } else if (action == _IOCTL_GET_ENTRY_) { + result = val; + goto bi; + } else if (action == _IOCTL_SET_ENTRY_) { + mark_blocks_dirty(vol->fd, sector, 1); + mirror_fats(vol, sector, block1); + goto bi; + } else if ((action == _IOCTL_ALLOCATE_N_ENTRIES_) && (val == 0)) { + vol->free_clusters--; + mark_blocks_dirty(vol->fd, sector, 1); + mirror_fats(vol, sector, block1); + if (n == 0) { + ASSERT(first == 0); + first = last = cluster; + } else { + ASSERT(IS_DATA_CLUSTER(first)); + ASSERT(IS_DATA_CLUSTER(last)); + // set last cluster to point to us + + if ((result = _fat_ioctl_(vol,_IOCTL_SET_ENTRY_,last,cluster)) < 0) { + ASSERT(0); + goto bi; + } + + last = cluster; + } + + if (++n == N) + goto bi; + } + + // iterate cluster and sector if needed + if (++cluster == vol->total_clusters + 2) { + release_block(vol->fd, sector); + + cluster = 2; + off = 2 * vol->fat_bits / 8; + sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat; + + block1 = (uint8 *)get_block(vol->fd, sector, vol->bytes_per_sector); + } + + if (off >= vol->bytes_per_sector) { + release_block(vol->fd, sector); + off -= vol->bytes_per_sector; sector++; + ASSERT(sector < vol->reserved_sectors + (vol->active_fat + 1) * vol->sectors_per_fat); + block1 = (uint8 *)get_block(vol->fd, sector, vol->bytes_per_sector); + } + + if (block1 == NULL) { + DPRINTF(0, ("_fat_ioctl_: error reading fat (sector %lx)\n", sector)); + result = EIO; + goto bi; + } + } + +bi: + if (block1) release_block(vol->fd, sector); + + if (action == _IOCTL_ALLOCATE_N_ENTRIES_) { + if (result < 0) { + DPRINTF(0, ("pooh. there is a problem. clearing chain (%lx)\n", first)); + if (first != 0) clear_fat_chain(vol, first); + } else if (n != N) { + DPRINTF(0, ("not enough free entries (%lx/%lx found)\n", n, N)); + if (first != 0) clear_fat_chain(vol, first); + result = ENOSPC; + } else if (result == 0) { + vol->last_allocated = cluster; + result = first; + ASSERT(IS_DATA_CLUSTER(first)); + } + } + + if (result < B_OK) + DPRINTF(0, ("_fat_ioctl_ error: action = %lx cluster = %lx N = %lx (%s)\n", action, cluster, N, strerror(result))); + + return result; +} + +int32 count_free_clusters(nspace *vol) +{ + return _fat_ioctl_(vol, _IOCTL_COUNT_FREE_, 0, 0); +} + +static int32 get_fat_entry(nspace *vol, uint32 cluster) +{ + int32 value = _fat_ioctl_(vol, _IOCTL_GET_ENTRY_, cluster, 0); + + if (value < 0) + return value; + + if ((value == 0) || IS_DATA_CLUSTER(value)) + return value; + + if (value > 0x0ffffff7) + return END_FAT_ENTRY; + + if (value > 0x0ffffff0) + return BAD_FAT_ENTRY; + + DPRINTF(0, ("invalid fat entry: %lx\n", value)); + return BAD_FAT_ENTRY; +} + +static status_t set_fat_entry(nspace *vol, uint32 cluster, int32 value) +{ + return _fat_ioctl_(vol, _IOCTL_SET_ENTRY_, cluster, value); +} + +// traverse n fat entries +int32 get_nth_fat_entry(nspace *vol, int32 cluster, uint32 n) +{ + if (check_nspace_magic(vol, "get_nth_fat_entry")) return EINVAL; + + while (n--) { + cluster = get_fat_entry(vol, cluster); + + if (!IS_DATA_CLUSTER(cluster)) + break; + } + + ASSERT(cluster != 0); + + return cluster; +} + +// count number of clusters in fat chain starting at given cluster +// should only be used for calculating directory sizes because it doesn't +// return proper error codes +uint32 count_clusters(nspace *vol, int32 cluster) +{ + int32 count = 0; + + DPRINTF(2, ("count_clusters %lx\n", cluster)); + + if (check_nspace_magic(vol, "count_clusters")) return 0; + + // not intended for use on root directory + if (!IS_DATA_CLUSTER(cluster)) { + DPRINTF(0, ("count_clusters called on invalid cluster (%lx)\n", cluster)); + return 0; + } + + while (IS_DATA_CLUSTER(cluster)) { + count++; + + // break out of circular fat chains in a sketchy manner + if (count == vol->total_clusters) + return 0; + + cluster = get_fat_entry(vol, cluster); + } + + DPRINTF(2, ("count_clusters %lx = %lx\n", cluster, count)); + + if (cluster == END_FAT_ENTRY) + return count; + + dprintf("cluster = %lx\n", cluster); + ASSERT(0); + + return 0; +} + +status_t clear_fat_chain(nspace *vol, uint32 cluster) +{ + int32 c; + status_t result; + + if (!IS_DATA_CLUSTER(cluster)) { + DPRINTF(0, ("clear_fat_chain called on invalid cluster (%lx)\n", cluster)); + return EINVAL; + } + + ASSERT(count_clusters(vol, cluster) != 0); + + DPRINTF(2, ("clearing fat chain: %lx", cluster)); + while (IS_DATA_CLUSTER(cluster)) { + if ((c = get_fat_entry(vol, cluster)) < 0) { + DPRINTF(0, ("clear_fat_chain: error clearing fat entry for cluster %lx (%s)\n", cluster, strerror(c))); + return c; + } + if ((result = set_fat_entry(vol, cluster, 0)) != B_OK) { + DPRINTF(0, ("clear_fat_chain: error clearing fat entry for cluster %lx (%s)\n", cluster, strerror(result))); + return result; + } + vol->free_clusters++; + cluster = c; + DPRINTF(2, (", %lx", cluster)); + } + DPRINTF(2, ("\n")); + + if (cluster != END_FAT_ENTRY) + dprintf("clear_fat_chain: fat chain terminated improperly with %lx\n", cluster); + + return 0; +} + +status_t allocate_n_fat_entries(nspace *vol, int32 n, int32 *start) +{ + int32 c; + + ASSERT(n > 0); + + DPRINTF(2, ("allocating %lx fat entries\n", n)); + + c = _fat_ioctl_(vol, _IOCTL_ALLOCATE_N_ENTRIES_, 0, n); + if (c < 0) + return c; + + ASSERT(IS_DATA_CLUSTER(c)); + ASSERT(count_clusters(vol, c) == n); + + DPRINTF(2, ("allocated %lx fat entries at %lx\n", n, c)); + + *start = c; + return 0; +} + +status_t set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters) +{ + status_t result; + int32 i, c, n; + + DPRINTF(1, ("set_fat_chain_length: %Lx to %lx clusters (%lx)\n", node->vnid, clusters, node->cluster)); + + if (IS_FIXED_ROOT(node->cluster) || (!IS_DATA_CLUSTER(node->cluster) && (node->cluster != 0))) { + DPRINTF(0, ("set_fat_chain_length called on invalid cluster (%lx)\n", node->cluster)); + return EINVAL; + } + + if (clusters == 0) { + DPRINTF(1, ("truncating node to zero bytes\n")); + if (node->cluster == 0) + return B_OK; + c = node->cluster; + if ((result = clear_fat_chain(vol, c)) != B_OK) + return result; + node->cluster = 0; + node->end_cluster = 0; + + // XXX: don't have to do this this way -- can clean up nicely + do { + result = vcache_set_entry(vol, node->vnid, + GENERATE_DIR_INDEX_VNID(node->dir_vnid, node->sindex)); + // repeat until memory is freed up + if (result != B_OK) + snooze(5000LL); + } while (result != B_OK); + + /* write to disk so that get_next_dirent doesn't barf */ + write_vnode_entry(vol, node); + + return result; + } + + if (node->cluster == 0) { + DPRINTF(1, ("node has no clusters. adding %lx clusters\n", clusters)); + + if ((result = allocate_n_fat_entries(vol, clusters, &n)) != B_OK) + return result; + node->cluster = n; + node->end_cluster = get_nth_fat_entry(vol, n, clusters - 1); + + // XXX: don't have to do this this way -- can clean up nicely + do { + result = vcache_set_entry(vol, node->vnid, + GENERATE_DIR_CLUSTER_VNID(node->dir_vnid, node->cluster)); + // repeat until memory is freed up + if (result != B_OK) + snooze(5000LL); + } while (result != B_OK); + + /* write to disk so that get_next_dirent doesn't barf */ + write_vnode_entry(vol, node); + + return result; + } + + i = (node->st_size + vol->bytes_per_sector * vol->sectors_per_cluster - 1) / + vol->bytes_per_sector / vol->sectors_per_cluster; + if (i == clusters) return B_OK; + + if (clusters > i) { + // add new fat entries + DPRINTF(1, ("adding %lx new fat entries\n", clusters - i)); + if ((result = allocate_n_fat_entries(vol, clusters - i, &n)) != B_OK) + return result; + + ASSERT(IS_DATA_CLUSTER(n)); + + result = set_fat_entry(vol, node->end_cluster, n); + if (result < B_OK) { + clear_fat_chain(vol, n); + return result; + } + + node->end_cluster = get_nth_fat_entry(vol, n, clusters - i - 1); + + return result; + } + + // traverse fat chain + c = node->cluster; + n = get_fat_entry(vol,c); + for (i=1;iend_cluster = c; + return clear_fat_chain(vol, n); +} + +void dump_fat_chain(nspace *vol, uint32 cluster) +{ + dprintf("fat chain: %lx", cluster); + while (IS_DATA_CLUSTER(cluster)) { + cluster = get_fat_entry(vol, cluster); + dprintf(" %lx", cluster); + } + dprintf("\n"); +} + +status_t fragment(nspace *vol, uint32 *pattern) +{ + uint32 sector, offset, previous_entry, i, val; + uchar *buffer; + bool dirty = FALSE; + + srand(time(NULL)|1); + + if (vol->fat_bits == 16) + previous_entry = 0xffff; + else if (vol->fat_bits == 32) + previous_entry = 0x0fffffff; + else { + dprintf("fragment: only for FAT16 and FAT32\n"); + return ENOSYS; + } + + sector = vol->reserved_sectors + vol->active_fat * vol->sectors_per_fat + + ((vol->total_clusters + 2 - 1) * (vol->fat_bits / 8)) / + vol->bytes_per_sector; + offset = ((vol->total_clusters + 2 - 1) * (vol->fat_bits / 8)) % + vol->bytes_per_sector; + + buffer = (uchar *)get_block(vol->fd, sector, vol->bytes_per_sector); + if (!buffer) { + dprintf("fragment: error getting fat block %lx\n", sector); + return EINVAL; + } + + val = pattern ? *pattern : rand(); + + for (i=vol->total_clusters+1;i>=2;i--) { + if (val & (1 << (i & 31))) { + if (vol->fat_bits == 16) { + if (read16(buffer, offset) == 0) { + buffer[offset+0] = (previous_entry ) & 0xff; + buffer[offset+1] = (previous_entry >> 8) & 0xff; + previous_entry = i; + dirty = TRUE; + vol->free_clusters--; + } + } else { + if (read32(buffer, offset) == 0) { + buffer[offset+0] = (previous_entry ) & 0xff; + buffer[offset+1] = (previous_entry >> 8) & 0xff; + buffer[offset+2] = (previous_entry >> 16) & 0xff; + buffer[offset+3] = (previous_entry >> 24) & 0xff; + previous_entry = i; + dirty = TRUE; + vol->free_clusters--; + } + } + } + + if (!offset) { + if (dirty) { + mark_blocks_dirty(vol->fd, sector, 1); + mirror_fats(vol, sector, buffer); + } + release_block(vol->fd, sector); + + dirty = FALSE; + sector--; + + buffer = (uchar *)get_block(vol->fd, sector, + vol->bytes_per_sector); + if (!buffer) { + dprintf("fragment: error getting fat block %lx\n", sector); + return EINVAL; + } + } + + offset = (offset - vol->fat_bits / 8 + vol->bytes_per_sector) % + vol->bytes_per_sector; + + if (!pattern && ((i & 31) == 31)) + val = rand(); + } + + if (dirty) { + mark_blocks_dirty(vol->fd, sector, 1); + mirror_fats(vol, sector, buffer); + } + release_block(vol->fd, sector); + + vol->last_allocated = (rand() % vol->total_clusters) + 2; + + return B_OK; +} diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/fat.h b/src/tests/add-ons/kernel/file_systems/dos/r5/fat.h new file mode 100644 index 0000000000..bf61275b17 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/fat.h @@ -0,0 +1,31 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_FAT_H_ +#define _DOSFS_FAT_H_ + +#define vIS_DATA_CLUSTER(vol,cluster) (((cluster) >= 2) && ((cluster) < vol->total_clusters + 2)) +#define IS_DATA_CLUSTER(cluster) vIS_DATA_CLUSTER(vol,cluster) + +// cluster 1 represents root directory for fat12 and fat16 +#define IS_FIXED_ROOT(cluster) ((cluster) == 1) + +int32 count_free_clusters(nspace *vol); +int32 get_nth_fat_entry(nspace *vol, int32 cluster, uint32 n); +uint32 count_clusters(nspace *vol, int32 cluster); + +/* remember to update vnode iteration after calling this function */ +status_t clear_fat_chain(nspace *vol, uint32 cluster); + +/* remember to set end of chain field when merging into a vnode */ +status_t allocate_n_fat_entries(nspace *vol, int32 n, int32 *start); + +/* remember to update vnode iteration after calling this function */ +status_t set_fat_chain_length(nspace *vol, vnode *node, uint32 clusters); + +void dump_fat_chain(nspace *vol, uint32 cluster); + +status_t fragment(nspace *vol, uint32 *pattern); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/file.c b/src/tests/add-ons/kernel/file_systems/dos/r5/file.c new file mode 100644 index 0000000000..b3bda2526f --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/file.c @@ -0,0 +1,1406 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "iter.h" +#include "dosfs.h" +#include "dlist.h" +#include "fat.h" +#include "dir.h" +#include "file.h" +#include "attr.h" +#include "vcache.h" +#include "util.h" + +#define DPRINTF(a,b) if (debug_file > (a)) dprintf b + +#define MAX_FILE_SIZE 0xffffffffLL + +#define FILECOOKIE_MAGIC 'looT' + +typedef struct filecookie +{ + uint32 magic; + uint32 mode; // open mode + + /* simple cluster cache */ + struct { + uint32 iteration; + uint32 index; /* index in the fat chain */ + uint32 cluster; + } ccache; +} filecookie; + +static CHECK_MAGIC(filecookie,struct filecookie, FILECOOKIE_MAGIC) + +status_t write_vnode_entry(nspace *vol, vnode *node) +{ + uint32 i; + struct diri diri; + uint8 *buffer; + + // don't update entries of deleted files + if (is_vnode_removed(vol->id, node->vnid) > 0) return 0; + + // XXX: should check if directory position is still valid even + // though we do the is_vnode_removed check above + + if ((node->cluster != 0) && !IS_DATA_CLUSTER(node->cluster)) { + dprintf("write_vnode_entry called on invalid cluster (%lx)\n", node->cluster); + return EINVAL; + } + + buffer = diri_init(vol, VNODE_PARENT_DIR_CLUSTER(node), node->eindex, &diri); + if (buffer == NULL) + return ENOENT; + + buffer[0x0b] = node->mode; // file attributes + + memset(buffer+0xc, 0, 0x16-0xc); + i = time_t2dos(node->st_time); + buffer[0x16] = i & 0xff; + buffer[0x17] = (i >> 8) & 0xff; + buffer[0x18] = (i >> 16) & 0xff; + buffer[0x19] = (i >> 24) & 0xff; + buffer[0x1a] = node->cluster & 0xff; // starting cluster + buffer[0x1b] = (node->cluster >> 8) & 0xff; + if (vol->fat_bits == 32) { + buffer[0x14] = (node->cluster >> 16) & 0xff; + buffer[0x15] = (node->cluster >> 24) & 0xff; + } + if (node->mode & FAT_SUBDIR) { + buffer[0x1c] = buffer[0x1d] = buffer[0x1e] = buffer[0x1f] = 0; + } else { + buffer[0x1c] = node->st_size & 0xff; // file size + buffer[0x1d] = (node->st_size >> 8) & 0xff; + buffer[0x1e] = (node->st_size >> 16) & 0xff; + buffer[0x1f] = (node->st_size >> 24) & 0xff; + } + + diri_mark_dirty(&diri); + diri_free(&diri); + + notify_listener(B_STAT_CHANGED, vol->id, 0, 0, node->vnid, NULL); + + return B_OK; +} + +// called when fs is done with vnode +// after close, etc. free vnode resources here +int dosfs_write_vnode(void *_vol, void *_node, char reenter) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + + TOUCH(reenter); + + if (check_nspace_magic(vol, "dosfs_write_vnode") || + check_vnode_magic(node, "dosfs_write_vnode")) { + return EINVAL; + } + + DPRINTF(0, ("dosfs_write_vnode (vnode_id %Lx)\n", ((vnode *)_node)->vnid)); + + if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) { + LOCK_VOL(vol); + _dosfs_sync(vol); + UNLOCK_VOL(vol); + } + + if (node != NULL) { +#if TRACK_FILENAME + if (node->filename) free(node->filename); +#endif + if (node->vnid != vol->root_vnode.vnid) { + node->magic = ~VNODE_MAGIC; // munge magic number to be safe + free(node); + } + } + + return 0; +} + +int dosfs_rstat(void *_vol, void *_node, struct stat *st) +{ + nspace *vol = (nspace*)_vol; + vnode *node = (vnode*)_node; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_rstat") || + check_vnode_magic(node, "dosfs_rstat")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(1, ("dosfs_rstat (vnode id %Lx)\n", node->vnid)); + + st->st_dev = vol->id; + st->st_ino = node->vnid; + st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; + if (node->mode & FAT_SUBDIR) { + st->st_mode &= ~S_IFREG; + st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + } + if ((vol->flags & B_FS_IS_READONLY) || (node->mode & FAT_READ_ONLY)) + st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + + st->st_nlink = 1; + st->st_uid = 0; + st->st_gid = 0; + st->st_size = node->st_size; + st->st_blksize = 0x10000; /* this value was chosen arbitrarily */ + st->st_atime = st->st_mtime = st->st_ctime = st->st_crtime = node->st_time; + + UNLOCK_VOL(vol); + + return B_NO_ERROR; +} + +int dosfs_wstat(void *_vol, void *_node, struct stat *st, long mask) +{ + int err = B_OK; + nspace *vol = (nspace*)_vol; + vnode *node = (vnode*)_node; + bool dirty = false; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_wstat") || + check_vnode_magic(node, "dosfs_wstat")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_wstat (vnode id %Lx)\n", node->vnid)); + + if (vol->flags & B_FS_IS_READONLY) { + dprintf("can't wstat on read-only volume\n"); + UNLOCK_VOL(vol); + return EROFS; + } + + if (node->disk_image == 2) { + dprintf("can't wstat disk image\n"); + UNLOCK_VOL(vol); + return EPERM; + } + + if (mask & WSTAT_MODE) { + DPRINTF(0, ("setting file mode to %o\n", st->st_mode)); + if (st->st_mode & S_IWUSR) + node->mode &= ~FAT_READ_ONLY; + else + node->mode |= FAT_READ_ONLY; + dirty = true; + } + + if (mask & WSTAT_SIZE) { + DPRINTF(0, ("setting file size to %Lx\n", st->st_size)); + if (node->mode & FAT_SUBDIR) { + dprintf("dosfs_wstat: can't set file size of directory!\n"); + err = EISDIR; + } else if (st->st_size > MAX_FILE_SIZE) { + dprintf("dosfs_wstat: desired file size exceeds fat limit\n"); + err = E2BIG; + } else { + uint32 clusters = (st->st_size + vol->bytes_per_sector*vol->sectors_per_cluster - 1) / vol->bytes_per_sector / vol->sectors_per_cluster; + DPRINTF(0, ("setting fat chain length to %lx clusters\n", clusters)); + if ((err = set_fat_chain_length(vol, node, clusters)) == B_OK) { + node->st_size = st->st_size; + node->iteration++; + dirty = true; + } + } + } + + if (mask & WSTAT_MTIME) { + DPRINTF(0, ("setting modification time\n")); + node->st_time = st->st_mtime; + dirty = true; + } + + if (dirty) { + write_vnode_entry(vol, node); + notify_listener(B_STAT_CHANGED, vol->id, 0, 0, node->vnid, NULL); + + if (vol->fs_flags & FS_FLAGS_OP_SYNC) { + // sync the filesystem + _dosfs_sync(vol); + node->dirty = false; + } + } + + if (err != B_OK) DPRINTF(0, ("dosfs_wstat (%s)\n", strerror(err))); + + UNLOCK_VOL(vol); + + return err; +} + +int dosfs_open(void *_vol, void *_node, int omode, void **_cookie) +{ + int result = EINVAL; + nspace *vol = (nspace *)_vol; + vnode* node = (vnode*)_node; + filecookie *cookie; + + *_cookie = NULL; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_open") || + check_vnode_magic(node, "dosfs_open")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_open: vnode id %Lx, omode %x\n", node->vnid, omode)); + + if (omode & O_CREAT) { + dprintf("dosfs_open called with O_CREAT. call dosfs_create instead!\n"); + result = EINVAL; + goto error; + } + + if ((vol->flags & B_FS_IS_READONLY) || + (node->mode & FAT_READ_ONLY) || + (node->disk_image != 0) || + // allow opening directories for ioctl() calls + // and to let BVolume to work + (node->mode & FAT_SUBDIR)) { + omode = (omode & ~O_RWMASK) | O_RDONLY; + } + + if ((omode & O_TRUNC) && ((omode & O_RWMASK) == O_RDONLY)) { + DPRINTF(0, ("can't open file for reading with O_TRUNC\n")); + result = EPERM; + goto error; + } + + if (omode & O_TRUNC) { + DPRINTF(0, ("dosfs_open called with O_TRUNC set\n")); + if ((result = set_fat_chain_length(vol, node, 0)) != B_OK) { + dprintf("dosfs_open: error truncating file\n"); + goto error; + } + node->mode = 0; + node->st_size = 0; + node->iteration++; + } + + if ((cookie = calloc(sizeof(filecookie), 1)) == NULL) { + result = ENOMEM; + goto error; + } + + cookie->magic = FILECOOKIE_MAGIC; + cookie->mode = omode; + cookie->ccache.iteration = node->iteration; + cookie->ccache.index = 0; + cookie->ccache.cluster = node->cluster; + *_cookie = cookie; + result = B_OK; + +error: + if (result != B_OK) DPRINTF(0, ("dosfs_open (%s)\n", strerror(result))); + + UNLOCK_VOL(vol); + return result; +} + +int dosfs_read(void *_vol, void *_node, void *_cookie, off_t pos, + void *buf, size_t *len) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + filecookie *cookie = (filecookie *)_cookie; + uint8 *buffer; + struct csi iter; + int result = B_OK; + size_t bytes_read = 0; + uint32 cluster1; + off_t diff; + + LOCK_VOL(vol); + + if (check_nspace_magic((nspace *)vol, "dosfs_read") || + check_vnode_magic(node, "dosfs_read") || + check_filecookie_magic(cookie, "dosfs_read")) { + *len = 0; + UNLOCK_VOL(vol); + return EINVAL; + } + + if (node->mode & FAT_SUBDIR) { + DPRINTF(0, ("dosfs_read called on subdirectory %Lx\n", node->vnid)); + *len = 0; + UNLOCK_VOL(vol); + return EISDIR; + } + + DPRINTF(0, ("dosfs_read called %lx bytes at %Lx (vnode id %Lx)\n", *len, pos, node->vnid)); + + if (pos < 0) pos = 0; + + if ((node->st_size == 0) || (*len == 0) || (pos >= node->st_size)) { + bytes_read = 0; + goto bi; + } + + // truncate bytes to read to file size + if (pos + *len >= node->st_size) + *len = node->st_size - pos; + + if ((cookie->ccache.iteration == node->iteration) && + (pos >= cookie->ccache.index * vol->bytes_per_sector * vol->sectors_per_cluster)) { + /* the cached fat value is both valid and helpful */ + ASSERT(IS_DATA_CLUSTER(cookie->ccache.cluster)); + ASSERT(cookie->ccache.cluster == get_nth_fat_entry(vol, node->cluster, cookie->ccache.index)); + cluster1 = cookie->ccache.cluster; + diff = pos - cookie->ccache.index * vol->bytes_per_sector * vol->sectors_per_cluster; + } else { + /* the fat chain changed, so we have to start from the beginning */ + cluster1 = node->cluster; + diff = pos; + } + diff /= vol->bytes_per_sector; /* convert to sectors */ + + if ((result = init_csi(vol, cluster1, 0, &iter)) != B_OK) { + dprintf("dosfs_read: invalid starting cluster (%lx)\n", cluster1); + goto bi; + } + + if (diff && ((result = iter_csi(&iter, diff)) != B_OK)) { + dprintf("dosfs_read: end of file reached (init)\n"); + result = EIO; + goto bi; + } + + ASSERT(iter.cluster == get_nth_fat_entry(vol, node->cluster, pos / vol->bytes_per_sector / vol->sectors_per_cluster)); + + if ((pos % vol->bytes_per_sector) != 0) { + // read in partial first sector if necessary + size_t amt; + buffer = csi_get_block(&iter); + if (buffer == NULL) { + dprintf("dosfs_read: error reading cluster %lx, sector %lx\n", iter.cluster, iter.sector); + result = EIO; + goto bi; + } + amt = vol->bytes_per_sector - (pos % vol->bytes_per_sector); + if (amt > *len) amt = *len; + memcpy(buf, buffer + (pos % vol->bytes_per_sector), amt); + csi_release_block(&iter); + bytes_read += amt; + + if (bytes_read < *len) + if ((result = iter_csi(&iter, 1)) != B_OK) { + dprintf("dosfs_read: end of file reached\n"); + result = EIO; + goto bi; + } + } + + // read middle sectors + while (bytes_read + vol->bytes_per_sector <= *len) { + result = csi_read_blocks(&iter, (uint8*)buf + bytes_read, *len - bytes_read); + if (result < B_OK) { + dprintf("dosfs_read: end of file reached\n"); + goto bi; + } + bytes_read += result; + + if (bytes_read < *len) + if ((result = iter_csi(&iter, 1)) != B_OK) { + dprintf("dosfs_read: end of file reached\n"); + result = EIO; + goto bi; + } + } + + // read part of remaining sector if needed + if (bytes_read < *len) { + size_t amt; + + buffer = csi_get_block(&iter); + if (buffer == NULL) { + dprintf("dosfs_read: error reading cluster %lx, sector %lx\n", iter.cluster, iter.sector); + result = EIO; + goto bi; + } + amt = *len - bytes_read; + memcpy((uint8*)buf + bytes_read, buffer, amt); + csi_release_block(&iter); + bytes_read += amt; + } + + if (*len) { + cookie->ccache.iteration = node->iteration; + cookie->ccache.index = (pos + *len - 1) / vol->bytes_per_sector / vol->sectors_per_cluster; + cookie->ccache.cluster = iter.cluster; + + ASSERT(cookie->ccache.cluster == get_nth_fat_entry(vol, node->cluster, cookie->ccache.index)); + } + + result = B_OK; +bi: + *len = bytes_read; + + if (result != B_OK) { + DPRINTF(0, ("dosfs_read (%s)\n", strerror(result))); + } else { + DPRINTF(0, ("dosfs_read: read %lx bytes\n", *len)); + } + UNLOCK_VOL(vol); + + return result; +} + +int dosfs_write(void *_vol, void *_node, void *_cookie, off_t pos, + const void *buf, size_t *len) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + filecookie *cookie = (filecookie *)_cookie; + uint8 *buffer; + struct csi iter; + int result = B_OK; + size_t bytes_written = 0; + uint32 cluster1; + off_t diff; + + LOCK_VOL(vol); + + if (check_nspace_magic((nspace *)vol, "dosfs_write") || + check_vnode_magic(node, "dosfs_write") || + check_filecookie_magic(cookie, "dosfs_write")) { + *len = 0; + UNLOCK_VOL(vol); + return EINVAL; + } + + if (node->mode & FAT_SUBDIR) { + DPRINTF(0, ("dosfs_write called on subdirectory %Lx\n", node->vnid)); + *len = 0; + UNLOCK_VOL(vol); + return EISDIR; + + } + + DPRINTF(0, ("dosfs_write called %lx bytes at %Lx from buffer at %lx (vnode id %Lx)\n", *len, pos, (uint32)buf, node->vnid)); + + if ((cookie->mode & O_RWMASK) == O_RDONLY) { + dprintf("dosfs_write: called on file opened as read-only\n"); + result = EPERM; + goto bi; + } + + if (pos < 0) pos = 0; + + if (cookie->mode & O_APPEND) { + pos = node->st_size; + } + + if (pos >= MAX_FILE_SIZE) { + dprintf("dosfs_write: write position exceeds fat limits\n"); + result = E2BIG; + goto bi; + } + + if (pos + *len >= MAX_FILE_SIZE) { + *len = (size_t)(MAX_FILE_SIZE - pos); + } + + if (node->st_size && + (cookie->ccache.iteration == node->iteration) && + (pos >= cookie->ccache.index * vol->bytes_per_sector * vol->sectors_per_cluster)) { + ASSERT(IS_DATA_CLUSTER(cookie->ccache.cluster)); + ASSERT(cookie->ccache.cluster == get_nth_fat_entry(vol, node->cluster, cookie->ccache.index)); + cluster1 = cookie->ccache.cluster; + diff = pos - cookie->ccache.index * vol->bytes_per_sector * vol->sectors_per_cluster; + } else { + cluster1 = 0xffffffff; + diff = 0; + } + + // extend file size if needed + if (pos + *len > node->st_size) { + uint32 clusters = (pos + *len + vol->bytes_per_sector*vol->sectors_per_cluster - 1) / vol->bytes_per_sector / vol->sectors_per_cluster; + if (node->st_size <= (clusters - 1) * vol->sectors_per_cluster * vol->bytes_per_sector) { + if ((result = set_fat_chain_length(vol, node, clusters)) != B_OK) { + goto bi; + } + node->iteration++; + } + node->st_size = pos + *len; + /* needs to be written to disk asap so that later vnid calculations + * by get_next_dirent are correct + */ + write_vnode_entry(vol, node); + + DPRINTF(0, ("setting file size to %Lx (%lx clusters)\n", node->st_size, clusters)); + node->dirty = true; + } + + if (cluster1 == 0xffffffff) { + cluster1 = node->cluster; + diff = pos; + } + diff /= vol->bytes_per_sector; /* convert to sectors */ + + if ((result = init_csi(vol, cluster1, 0, &iter)) != B_OK) { + dprintf("dosfs_write: invalid starting cluster (%lx)\n", cluster1); + goto bi; + } + + if (diff && ((result = iter_csi(&iter, diff)) != B_OK)) { + dprintf("dosfs_write: end of file reached (init)\n"); + result = EIO; + goto bi; + } + + ASSERT(iter.cluster == get_nth_fat_entry(vol, node->cluster, pos / vol->bytes_per_sector / vol->sectors_per_cluster)); + + if(*len > 0) + node->dirty = true; + + // write partial first sector if necessary + if ((pos % vol->bytes_per_sector) != 0) { + size_t amt; + buffer = csi_get_block(&iter); + if (buffer == NULL) { + dprintf("dosfs_write: error writing cluster %lx, sector %lx\n", iter.cluster, iter.sector); + result = EIO; + goto bi; + } + amt = vol->bytes_per_sector - (pos % vol->bytes_per_sector); + if (amt > *len) amt = *len; + memcpy(buffer + (pos % vol->bytes_per_sector), buf, amt); + csi_mark_block_dirty(&iter); + csi_release_block(&iter); + bytes_written += amt; + + if (bytes_written < *len) + if ((result = iter_csi(&iter, 1)) != B_OK) { + dprintf("dosfs_write: end of file reached\n"); + result = EIO; + goto bi; + } + } + + // write middle sectors + while (bytes_written + vol->bytes_per_sector <= *len) { + result = csi_write_blocks(&iter, (uint8*)buf + bytes_written, *len - bytes_written); + if (result < B_OK) { + dprintf("dosfs_write: end of file reached\n"); + goto bi; + } + bytes_written += result; + + if (bytes_written < *len) + if ((result = iter_csi(&iter, 1)) != B_OK) { + dprintf("dosfs_write: end of file reached\n"); + result = EIO; + goto bi; + } + } + + // write part of remaining sector if needed + if (bytes_written < *len) { + size_t amt; + + buffer = csi_get_block(&iter); + if (buffer == NULL) { + dprintf("error writing cluster %lx, sector %lx\n", iter.cluster, iter.sector); + result = EIO; + goto bi; + } + amt = *len - bytes_written; + memcpy(buffer, (uint8*)buf + bytes_written, amt); + csi_mark_block_dirty(&iter); + csi_release_block(&iter); + bytes_written += amt; + } + + if (*len) { + cookie->ccache.iteration = node->iteration; + cookie->ccache.index = (pos + *len - 1) / vol->bytes_per_sector / vol->sectors_per_cluster; + cookie->ccache.cluster = iter.cluster; + + ASSERT(cookie->ccache.cluster == get_nth_fat_entry(vol, node->cluster, cookie->ccache.index)); + } + + result = B_OK; + +bi: + *len = bytes_written; + + if (result != B_OK) { + DPRINTF(0, ("dosfs_write (%s)\n", strerror(result))); + } else { + DPRINTF(0, ("dosfs_write: wrote %lx bytes\n", *len)); + } + UNLOCK_VOL(vol); + + return result; +} + +int dosfs_close(void *_vol, void *_node, void *_cookie) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_close") || + check_vnode_magic(node, "dosfs_close") || + check_filecookie_magic(_cookie, "dosfs_close")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_close (vnode id %Lx)\n", ((vnode *)_node)->vnid)); + + if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) { + _dosfs_sync(vol); + node->dirty = false; + } + + UNLOCK_VOL(vol); + + return 0; +} + +int dosfs_free_cookie(void *_vol, void *_node, void *_cookie) +{ + nspace *vol = _vol; + vnode *node = _node; + filecookie *cookie = _cookie; + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_free_cookie") || + check_vnode_magic(node, "dosfs_free_cookie") || + check_filecookie_magic(cookie, "dosfs_free_cookie")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_free_cookie (vnode id %Lx)\n", node->vnid)); + + cookie->magic = ~FILECOOKIE_MAGIC; + free(cookie); + + UNLOCK_VOL(vol); + + return 0; +} + +int dosfs_create(void *_vol, void *_dir, const char *name, int omode, + int perms, vnode_id *vnid, void **_cookie) +{ + nspace *vol = (nspace *)_vol; + vnode *dir = (vnode *)_dir, *file; + filecookie *cookie; + status_t result = EINVAL; + bool dups_exist; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_create") || + check_vnode_magic(dir, "dosfs_create")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + ASSERT(name != NULL); + if (name == NULL) { + dprintf("dosfs_create called with null name\n"); + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_create called: %Lx/%s perms=%o omode=%o\n", dir->vnid, name, perms, omode)); + + if (vol->flags & B_FS_IS_READONLY) { + dprintf("dosfs_create called on read-only volume\n"); + UNLOCK_VOL(vol); + return EROFS; + } + + if (is_vnode_removed(vol->id, dir->vnid) > 0) { + dprintf("dosfs_create() called in removed directory. disallowed.\n"); + UNLOCK_VOL(vol); + return EPERM; + } + + if ((omode & O_RWMASK) == O_RDONLY) { + dprintf("invalid permissions used in creating file\n"); + UNLOCK_VOL(vol); + return EPERM; + } + + // create file cookie; do it here to make cleaning up easier + if ((cookie = calloc(sizeof(filecookie), 1)) == NULL) { + result = ENOMEM; + goto bi; + } + + result = findfile_case_duplicates(vol, dir, name, vnid, &file, &dups_exist); + if (result == B_OK) { + if (omode & O_EXCL) { + dprintf("exclusive dosfs_create called on existing file %s\n", name); + put_vnode(vol->id, file->vnid); + result = EEXIST; + goto bi; + } + + if (file->mode & FAT_SUBDIR) { + dprintf("can't dosfs_create over an existing subdirectory\n"); + put_vnode(vol->id, file->vnid); + result = EPERM; + goto bi; + } + + if (file->disk_image) { + dprintf("can't dosfs_create over a disk image\n"); + put_vnode(vol->id, file->vnid); + result = EPERM; + goto bi; + } + + if (omode & O_TRUNC) { + set_fat_chain_length(vol, file, 0); + file->st_size = 0; + file->iteration++; + } + } else if (result == ENOENT && dups_exist) { + // the file doesn't exist in the exact case, but another does in the + // non-exact case. We wont create the new file. + result = EEXIST; + goto bi; + } else if (result == ENOENT && !dups_exist) { + // the file doesn't already exist in any case + vnode dummy; /* used only to create directory entry */ + + dummy.magic = VNODE_MAGIC; + dummy.dir_vnid = dir->vnid; + dummy.cluster = 0; + dummy.end_cluster = 0; + dummy.mode = 0; + dummy.st_size = 0; + time(&(dummy.st_time)); + + if ((result = create_dir_entry(vol, dir, &dummy, name, &(dummy.sindex), &(dummy.eindex))) != B_OK) { + dprintf("dosfs_create: error creating directory entry for %s (%s)\n", name, strerror(result)); + dummy.magic = ~VNODE_MAGIC; + goto bi; + } + dummy.vnid = GENERATE_DIR_INDEX_VNID(dummy.dir_vnid, dummy.sindex); + // XXX: dangerous construct + if (find_vnid_in_vcache(vol, dummy.vnid) == B_OK) { + dummy.vnid = generate_unique_vnid(vol); + if ((result = add_to_vcache(vol, dummy.vnid, GENERATE_DIR_INDEX_VNID(dummy.dir_vnid, dummy.sindex))) < 0) { + // XXX: should remove entry on failure + if (vol->fs_flags & FS_FLAGS_OP_SYNC) + _dosfs_sync(vol); + goto bi; + } + } + *vnid = dummy.vnid; + dummy.magic = ~VNODE_MAGIC; + + result = get_vnode(vol->id, *vnid, (void **)&file); + if (result < B_OK) { + if (vol->fs_flags & FS_FLAGS_OP_SYNC) + _dosfs_sync(vol); + goto bi; + } + } else { + goto bi; + } + + cookie->magic = FILECOOKIE_MAGIC; + cookie->mode = omode; + cookie->ccache.iteration = file->iteration; + cookie->ccache.index = 0; + cookie->ccache.cluster = file->cluster; + *_cookie = cookie; + + notify_listener(B_ENTRY_CREATED, vol->id, dir->vnid, 0, *vnid, name); + + result = 0; + + if (vol->fs_flags & FS_FLAGS_OP_SYNC) + _dosfs_sync(vol); + +bi: if (result != B_OK) free(cookie); + + UNLOCK_VOL(vol); + + if (result != B_OK) DPRINTF(0, ("dosfs_create (%s)\n", strerror(result))); + + return result; +} + +int dosfs_mkdir(void *_vol, void *_dir, const char *name, int perms) +{ + nspace *vol = (nspace *)_vol; + vnode *dir = (vnode *)_dir, dummy; + status_t result = EINVAL; + struct csi csi; + uchar *buffer; + uint32 i; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_mkdir") || + check_vnode_magic(dir, "dosfs_mkdir")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + if (is_vnode_removed(vol->id, dir->vnid) > 0) { + dprintf("dosfs_mkdir() called in removed directory. disallowed.\n"); + UNLOCK_VOL(vol); + return EPERM; + } + + DPRINTF(0, ("dosfs_mkdir called: %Lx/%s (perm %o)\n", dir->vnid, name, perms)); + + if ((dir->mode & FAT_SUBDIR) == 0) { + dprintf("dosfs_mkdir: vnode id %Lx is not a directory\n", dir->vnid); + UNLOCK_VOL(vol); + return EINVAL; + } + + // S_IFDIR is never set in perms, so we patch it + perms &= ~S_IFMT; perms |= S_IFDIR; + + if (vol->flags & B_FS_IS_READONLY) { + dprintf("mkdir called on read-only volume\n"); + UNLOCK_VOL(vol); + return EROFS; + } + + /* only used to create directory entry */ + dummy.magic = VNODE_MAGIC; + dummy.dir_vnid = dir->vnid; + if ((result = allocate_n_fat_entries(vol, 1, (int32 *)&(dummy.cluster))) < 0) { + dprintf("dosfs_mkdir: error allocating space for %s (%s))\n", name, strerror(result)); + goto bi; + } + dummy.end_cluster = dummy.cluster; + dummy.mode = FAT_SUBDIR; + if (!(perms & (S_IWUSR | S_IWGRP | S_IWGRP))) { + dummy.mode |= FAT_READ_ONLY; + } + dummy.st_size = vol->bytes_per_sector*vol->sectors_per_cluster; + time(&(dummy.st_time)); + + dummy.vnid = GENERATE_DIR_CLUSTER_VNID(dummy.dir_vnid, dummy.cluster); + // XXX: dangerous construct + if (find_vnid_in_vcache(vol, dummy.vnid) == B_OK) { + dummy.vnid = generate_unique_vnid(vol); + if ((result = add_to_vcache(vol, dummy.vnid, GENERATE_DIR_CLUSTER_VNID(dummy.dir_vnid, dummy.cluster))) < 0) + goto bi2; + } + + if ((result = dlist_add(vol, dummy.vnid)) < 0) { + dprintf("dosfs_mkdir: error adding directory %s to dlist (%s)\n", name, strerror(result)); + goto bi3; + } + + buffer = malloc(vol->bytes_per_sector); + if (!buffer) { + result = ENOMEM; + goto bi4; + } + + if ((result = create_dir_entry(vol, dir, &dummy, name, &(dummy.sindex), &(dummy.eindex))) != B_OK) { + dprintf("dosfs_mkdir: error creating directory entry for %s (%s))\n", name, strerror(result)); + goto bi5; + } + + // create '.' and '..' entries and then end of directories + memset(buffer, 0, vol->bytes_per_sector); + memset(buffer, ' ', 11); + memset(buffer+0x20, ' ', 11); + buffer[0] = buffer[0x20] = buffer[0x21] = '.'; + buffer[0x0b] = buffer[0x2b] = 0x30; + i = time_t2dos(dummy.st_time); + buffer[0x16] = i & 0xff; + buffer[0x17] = (i >> 8) & 0xff; + buffer[0x18] = (i >> 16) & 0xff; + buffer[0x19] = (i >> 24) & 0xff; + i = time_t2dos(dir->st_time); + buffer[0x36] = i & 0xff; + buffer[0x37] = (i >> 8) & 0xff; + buffer[0x38] = (i >> 16) & 0xff; + buffer[0x39] = (i >> 24) & 0xff; + buffer[0x1a] = dummy.cluster & 0xff; + buffer[0x1b] = (dummy.cluster >> 8) & 0xff; + if (vol->fat_bits == 32) { + buffer[0x14] = (dummy.cluster >> 16) & 0xff; + buffer[0x15] = (dummy.cluster >> 24) & 0xff; + } + // root directory is always denoted by cluster 0, even for fat32 (!) + if (dir->vnid != vol->root_vnode.vnid) { + buffer[0x3a] = dir->cluster & 0xff; + buffer[0x3b] = (dir->cluster >> 8) & 0xff; + if (vol->fat_bits == 32) { + buffer[0x34] = (dir->cluster >> 16) & 0xff; + buffer[0x35] = (dir->cluster >> 24) & 0xff; + } + } + + init_csi(vol, dummy.cluster, 0, &csi); + csi_write_block(&csi, buffer); + + // clear out rest of cluster to keep scandisk happy + memset(buffer, 0, vol->bytes_per_sector); + for (i=1;isectors_per_cluster;i++) { + if (iter_csi(&csi, 1) != B_OK) { + dprintf("dosfs_mkdir: error writing directory cluster\n"); + break; + } + csi_write_block(&csi, buffer); + } + + free(buffer); + + notify_listener(B_ENTRY_CREATED, vol->id, dir->vnid, 0, dummy.vnid, name); + + result = B_OK; + + if (vol->fs_flags & FS_FLAGS_OP_SYNC) + _dosfs_sync(vol); + + UNLOCK_VOL(vol); + return result; + +bi5:free(buffer); +bi4:dlist_remove(vol, dummy.vnid); +bi3:if (IS_ARTIFICIAL_VNID(dummy.vnid)) remove_from_vcache(vol, dummy.vnid); +bi2:clear_fat_chain(vol, dummy.cluster); + if (vol->fs_flags & FS_FLAGS_OP_SYNC) + _dosfs_sync(vol); +bi: dummy.magic = ~VNODE_MAGIC; + + UNLOCK_VOL(vol); + if (result != B_OK) DPRINTF(0, ("dosfs_mkdir (%s)\n", strerror(result))); + return result; +} + +int dosfs_rename(void *_vol, void *_odir, const char *oldname, + void *_ndir, const char *newname) +{ + status_t result = EINVAL; + nspace *vol = (nspace *)_vol; + vnode *odir = (vnode *)_odir, *ndir = (vnode *)_ndir, *file, *file2; + uint32 ns, ne; + bool dups_exist; + bool dirty = false; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "dosfs_rename") || + check_vnode_magic(odir, "dosfs_rename") || + check_vnode_magic(ndir, "dosfs_rename")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_rename called: %Lx/%s->%Lx/%s\n", odir->vnid, oldname, ndir->vnid, newname)); + + if(!is_filename_legal(newname)) { + dprintf("dosfs_rename called with invalid name '%s'\n", newname); + result = EINVAL; + goto bi; + } + + if (vol->flags & B_FS_IS_READONLY) { + dprintf("rename called on read-only volume\n"); + result = EROFS; + goto bi; + } + + if (!oldname || !(*oldname) || !newname || !(*newname)) { + result = EINVAL; + goto bi; + } + + if ((odir->vnid == ndir->vnid) && !strcmp(oldname, newname)) { + result = EPERM; + goto bi; + } + + // locate the file + if ((result = findfile_case(vol,odir,oldname,NULL,&file)) != B_OK) { + DPRINTF(0, ("dosfs_rename: can't find file %s in directory %Lx\n", oldname, odir->vnid)); + goto bi; + } + + if (file->disk_image) { + dprintf("rename called on disk image or disk image directory\n"); + result = EPERM; + goto bi1; + } + + if (check_vnode_magic(file, "dosfs_rename")) { + result = EINVAL; + goto bi1; + } + + // don't move a directory into one of its children + if (file->mode & FAT_SUBDIR) { + vnode_id vnid = ndir->vnid; + while (1) { + vnode *dir; + vnode_id parent; + + if (vnid == file->vnid) { + result = EINVAL; + goto bi1; + } + + if (vnid == vol->root_vnode.vnid) + break; + + result = get_vnode(vol->id, vnid, (void **)&dir); + if (result < B_OK) + goto bi1; + parent = dir->dir_vnid; + put_vnode(vol->id, vnid); + vnid = parent; + } + } + + // see if file already exists and erase it if it does + result = findfile_case_duplicates(vol, ndir, newname, NULL, &file2, &dups_exist); + if (result == B_OK) { + if (file2->mode & FAT_SUBDIR) { + dprintf("destination already occupied by a directory\n"); + result = EPERM; + goto bi2; + } + + if (file2->disk_image) { + DPRINTF(0, ("dosfs_rename: can't replace disk image or disk image directory\n")); + result = EPERM; + goto bi2; + } + ns = file2->sindex; ne = file2->eindex; + + // let others know the old file is gone + notify_listener(B_ENTRY_REMOVED, vol->id, ndir->vnid, 0, file2->vnid, NULL); + + // Make sure this vnode 1) is in the vcache and 2) no longer has a + // location associated with it. See discussion in dosfs_unlink() + vcache_set_entry(vol, file2->vnid, generate_unique_vnid(vol)); + + // mark vnode for removal (dosfs_remove_vnode will clear the fat chain) + // note we don't have to lock the file because the fat chain doesn't + // get wiped from the disk until dosfs_remove_vnode() is called; we'll + // have a phantom chain in effect until the last file is closed. + remove_vnode(vol->id, file2->vnid); // must be done in this order + put_vnode(vol->id, file2->vnid); + + dirty = true; + + // erase old directory entry + if ((result = erase_dir_entry(vol, file)) != B_OK) { + dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname, strerror(result)); + goto bi1; + } + } else if (result == ENOENT && (!dups_exist || (odir->vnid == ndir->vnid && !strcasecmp(oldname, newname)))) { + // there isn't an entry and there are no duplicates in the target dir or + // there isn't an entry and the target dir is the same as the source dir and + // the source and target name are the same, case-insensitively + + // erase old directory entry + if ((result = erase_dir_entry(vol, file)) != B_OK) { + dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname, strerror(result)); + goto bi1; + } + + dirty = true; + + // create the new directory entry + if ((result = create_dir_entry(vol, ndir, file, newname, &ns, &ne)) != B_OK) { + dprintf("dosfs_rename: error creating directory entry for %s\n", newname); + goto bi1; + } + } else if (result == ENOENT && dups_exist) { + // the entry doesn't exist but a non-case entry does, so we can't do it + result = EEXIST; + goto bi1; + } else { + goto bi1; + } + + // shrink the directory (an error here is not disastrous) + compact_directory(vol, odir); + + dirty = true; + + // update vnode information + file->dir_vnid = ndir->vnid; + file->sindex = ns; + file->eindex = ne; + + // update vcache + vcache_set_entry(vol, file->vnid, + (file->st_size) ? + GENERATE_DIR_CLUSTER_VNID(file->dir_vnid, file->cluster) : + GENERATE_DIR_INDEX_VNID(file->dir_vnid, file->sindex)); + + // XXX: only write changes in the directory entry if needed + // (i.e. old entry, not new) + write_vnode_entry(vol, file); + + if (file->mode & FAT_SUBDIR) { + // update '..' directory entry if needed + // this should most properly be in write_vnode, but it is safe + // to keep it here since this is the only way the cluster of + // the parent can change. + struct diri diri; + uint8 *buffer; + if ((buffer = diri_init(vol, file->cluster, 1, &diri)) == NULL) { + dprintf("error opening directory :(\n"); + result = EIO; + goto bi2; + } + if (memcmp(buffer, ".. ", 11)) { + dprintf("invalid directory :(\n"); + result = EIO; + goto bi2; + } + if (ndir->vnid == vol->root_vnode.vnid) { + // root directory always has cluster = 0 + buffer[0x1a] = buffer[0x1b] = 0; + } else { + buffer[0x1a] = ndir->cluster & 0xff; + buffer[0x1b] = (ndir->cluster >> 8) & 0xff; + if (vol->fat_bits == 32) { + buffer[0x14] = (ndir->cluster >> 16) & 0xff; + buffer[0x15] = (ndir->cluster >> 24) & 0xff; + } + } + diri_mark_dirty(&diri); + diri_free(&diri); + } + +#if TRACK_FILENAME + if (file->filename) free(file->filename); + file->filename = malloc(strlen(newname) + 1); + if (file->filename) strcpy(file->filename, newname); +#endif + + notify_listener(B_ENTRY_MOVED, vol->id, odir->vnid, ndir->vnid, file->vnid, newname); + + // update MIME information + if(!(file->mode & FAT_SUBDIR)) { + set_mime_type(file, newname); + notify_listener(B_ATTR_CHANGED, vol->id, 0, 0, file->vnid, "BEOS:TYPE"); + } + + result = 0; + +bi2: + if (result != B_OK) put_vnode(vol->id, file2->vnid); +bi1: + put_vnode(vol->id, file->vnid); +bi: + if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && dirty) + _dosfs_sync(vol); + UNLOCK_VOL(vol); + if (result != B_OK) DPRINTF(0, ("dosfs_rename (%s)\n", strerror(result))); + return result; +} + +int dosfs_remove_vnode(void *_vol, void *_node, char reenter) +{ + nspace *vol = (nspace *)_vol; + vnode *node = (vnode *)_node; + + if (!reenter) { LOCK_VOL(vol); } + + if (check_nspace_magic(vol, "dosfs_remove_vnode") || + check_vnode_magic(node, "dosfs_remove_vnode")) { + if (!reenter) UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("dosfs_remove_vnode (%Lx)\n", node->vnid)); + + if (vol->flags & B_FS_IS_READONLY) { + dprintf("dosfs_remove_vnode: read-only volume\n"); + if (!reenter) UNLOCK_VOL(vol); + return EROFS; + } + + // clear the fat chain + ASSERT((node->cluster == 0) || IS_DATA_CLUSTER(node->cluster)); + /* XXX: the following assertion was tripped */ + ASSERT((node->cluster != 0) || (node->st_size == 0)); + if (node->cluster != 0) + clear_fat_chain(vol, node->cluster); + + /* remove vnode id from the cache */ + if (find_vnid_in_vcache(vol, node->vnid) == B_OK) + remove_from_vcache(vol, node->vnid); + + /* and from the dlist as well */ + if (node->mode & FAT_SUBDIR) + dlist_remove(vol, node->vnid); + + node->magic = ~VNODE_MAGIC; // munge magic number to be safe + free(node); + + if (!reenter) { + if (vol->fs_flags & FS_FLAGS_OP_SYNC) { + // sync the entire filesystem, + // but only if we're not reentrant. Presumably the + // function that called this will sync. + _dosfs_sync(vol); + } + UNLOCK_VOL(vol); + } + + return 0; +} + +// get rid of node or directory +static int do_unlink(void *_vol, void *_dir, const char *name, bool is_file) +{ + int result = EINVAL; + nspace *vol = (nspace *)_vol; + vnode *dir = (vnode *)_dir, *file; + vnode_id vnid; + + if (!strcmp(name, ".")) + return EPERM; + + if (!strcmp(name, "..")) + return EPERM; + + LOCK_VOL(vol); + + if (check_nspace_magic(vol, "do_unlink") || + check_vnode_magic(dir, "do_unlink")) { + UNLOCK_VOL(vol); + return EINVAL; + } + + DPRINTF(0, ("do_unlink %Lx/%s\n", dir->vnid, name)); + + if (vol->flags & B_FS_IS_READONLY) { + dprintf("do_unlink: read-only volume\n"); + result = EROFS; + goto bi; + } + + // locate the file + if ((result = findfile_case(vol,dir,name,&vnid,&file)) != B_OK) { + DPRINTF(0, ("do_unlink: can't find file %s in directory %Lx\n", name, dir->vnid)); + result = ENOENT; + goto bi; + } + + if (check_vnode_magic(file, "do_unlink")) { + result = EINVAL; + goto bi1; + } + + if (file->disk_image) { + DPRINTF(0, ("do_unlink: can't unlink disk image or disk image directory\n")); + result = EPERM; + goto bi1; + } + + // don't need to check file permissions because it will be done for us + // also don't need to lock the file (see dosfs_rename for reasons why) + if (is_file) { + if (file->mode & FAT_SUBDIR) { + result = EISDIR; + goto bi1; + } + } else { + if ((file->mode & FAT_SUBDIR) == 0) { + result = ENOTDIR; + goto bi1; + } + + if (file->vnid == vol->root_vnode.vnid) { + // this actually isn't a problem since the root vnode + // will always be busy while the volume mounted + dprintf("dosfs_rmdir: don't call this on the root directory\n"); + result = EPERM; + goto bi1; + } + + if ((result = check_dir_empty(vol, file)) < 0) { + if (result == ENOTEMPTY) DPRINTF(0, ("dosfs_rmdir called on non-empty directory\n")); + goto bi1; + } + } + + // erase the entry in the parent directory + if ((result = erase_dir_entry(vol, file)) != B_OK) + goto bi1; + + // shrink the parent directory (errors here are not disastrous) + compact_directory(vol, dir); + + notify_listener(B_ENTRY_REMOVED, vol->id, dir->vnid, 0, file->vnid, NULL); + + /* Set the loc to a unique value. This effectively removes it from the + * vcache without releasing its vnid for reuse. It also nicely reserves + * the vnid from use by other nodes. This is okay because the vnode is + * locked in memory after this point and loc will not be referenced from + * here on. + */ + vcache_set_entry(vol, file->vnid, generate_unique_vnid(vol)); + + // fsil doesn't call dosfs_write_vnode for us, so we have to free the + // vnode manually here. + remove_vnode(vol->id, file->vnid); + + result = 0; + + if (vol->fs_flags & FS_FLAGS_OP_SYNC) + _dosfs_sync(vol); + +bi1:put_vnode(vol->id, vnid); // get 1 free +bi: UNLOCK_VOL(vol); + + if (result != B_OK) DPRINTF(0, ("do_unlink (%s)\n", strerror(result))); + + return result; +} + +int dosfs_unlink(void *vol, void *dir, const char *name) +{ + DPRINTF(1, ("dosfs_unlink called\n")); + + return do_unlink(vol,dir,name,true); +} + +int dosfs_rmdir(void *vol, void *dir, const char *name) +{ + DPRINTF(1, ("dosfs_rmdir called\n")); + + return do_unlink(vol,dir,name,false); +} diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/file.h b/src/tests/add-ons/kernel/file_systems/dos/r5/file.h new file mode 100644 index 0000000000..44d278f94d --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/file.h @@ -0,0 +1,30 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_FILE_H_ +#define _DOSFS_FILE_H_ + +status_t write_vnode_entry(nspace *vol, vnode *node); + +int dosfs_write_vnode(void *_vol, void *_node, char r); +int dosfs_rstat(void *_vol, void *_node, struct stat *st); +int dosfs_open(void *_vol, void *_node, int omode, void **cookie); +int dosfs_read(void *_vol, void *_node, void *cookie, off_t pos, + void *buf, size_t *len); +int dosfs_free_cookie(void *vol, void *node, void *cookie); +int dosfs_close(void *vol, void *node, void *cookie); + +int dosfs_remove_vnode(void *vol, void *node, char r); +int dosfs_create(void *vol, void *dir, const char *name, + int perms, int omode, vnode_id *vnid, void **cookie); +int dosfs_mkdir(void *vol, void *dir, const char *name, int perms); +int dosfs_rename(void *vol, void *olddir, const char *oldname, + void *newdir, const char *newname); +int dosfs_unlink(void *vol, void *dir, const char *name); +int dosfs_rmdir(void *vol, void *dir, const char *name); +int dosfs_wstat(void *vol, void *node, struct stat *st, long mask); +int dosfs_write(void *vol, void *node, void *cookie, off_t pos, + const void *buf, size_t *len); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/fsproto.h b/src/tests/add-ons/kernel/file_systems/dos/r5/fsproto.h new file mode 100644 index 0000000000..afa16d61b3 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/fsproto.h @@ -0,0 +1,245 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _FSPROTO_H +#define _FSPROTO_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef dev_t nspace_id; +typedef ino_t vnode_id; + +/* + * PUBLIC PART OF THE FILE SYSTEM PROTOCOL + */ + +#define WSTAT_MODE 0x0001 +#define WSTAT_UID 0x0002 +#define WSTAT_GID 0x0004 +#define WSTAT_SIZE 0x0008 +#define WSTAT_ATIME 0x0010 +#define WSTAT_MTIME 0x0020 +#define WSTAT_CRTIME 0x0040 + +#define WFSSTAT_NAME 0x0001 + +#define B_ENTRY_CREATED 1 +#define B_ENTRY_REMOVED 2 +#define B_ENTRY_MOVED 3 +#define B_STAT_CHANGED 4 +#define B_ATTR_CHANGED 5 +#define B_DEVICE_MOUNTED 6 +#define B_DEVICE_UNMOUNTED 7 + +#define B_STOP_WATCHING 0x0000 +#define B_WATCH_NAME 0x0001 +#define B_WATCH_STAT 0x0002 +#define B_WATCH_ATTR 0x0004 +#define B_WATCH_DIRECTORY 0x0008 + +#define SELECT_READ 1 +#define SELECT_WRITE 2 +#define SELECT_EXCEPTION 3 + +#define B_CUR_FS_API_VERSION 2 + +struct attr_info; +struct index_info; + +typedef int op_read_vnode(void *ns, vnode_id vnid, char r, void **node); +typedef int op_write_vnode(void *ns, void *node, char r); +typedef int op_remove_vnode(void *ns, void *node, char r); +typedef int op_secure_vnode(void *ns, void *node); + +typedef int op_walk(void *ns, void *base, const char *file, char **newpath, + vnode_id *vnid); + +typedef int op_access(void *ns, void *node, int mode); + +typedef int op_create(void *ns, void *dir, const char *name, + int omode, int perms, vnode_id *vnid, void **cookie); +typedef int op_mkdir(void *ns, void *dir, const char *name, int perms); +typedef int op_symlink(void *ns, void *dir, const char *name, + const char *path); +typedef int op_link(void *ns, void *dir, const char *name, void *node); + +typedef int op_rename(void *ns, void *olddir, const char *oldname, + void *newdir, const char *newname); +typedef int op_unlink(void *ns, void *dir, const char *name); +typedef int op_rmdir(void *ns, void *dir, const char *name); + +typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize); + +typedef int op_opendir(void *ns, void *node, void **cookie); +typedef int op_closedir(void *ns, void *node, void *cookie); +typedef int op_rewinddir(void *ns, void *node, void *cookie); +typedef int op_readdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef int op_open(void *ns, void *node, int omode, void **cookie); +typedef int op_close(void *ns, void *node, void *cookie); +typedef int op_free_cookie(void *ns, void *node, void *cookie); +typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf, + size_t *len); +typedef int op_write(void *ns, void *node, void *cookie, off_t pos, + const void *buf, size_t *len); +typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, + size_t len); +typedef int op_setflags(void *ns, void *node, void *cookie, int flags); + +typedef int op_rstat(void *ns, void *node, struct stat *); +typedef int op_wstat(void *ns, void *node, struct stat *, long mask); +typedef int op_fsync(void *ns, void *node); + +typedef int op_select(void *ns, void *node, void *cookie, uint8 event, + uint32 ref, selectsync *sync); +typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event, + selectsync *sync); + +typedef int op_initialize(const char *devname, void *parms, size_t len); +typedef int op_mount(nspace_id nsid, const char *devname, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid); +typedef int op_unmount(void *ns); +typedef int op_sync(void *ns); +typedef int op_rfsstat(void *ns, struct fs_info *); +typedef int op_wfsstat(void *ns, struct fs_info *, long mask); + + +typedef int op_open_attrdir(void *ns, void *node, void **cookie); +typedef int op_close_attrdir(void *ns, void *node, void *cookie); +typedef int op_rewind_attrdir(void *ns, void *node, void *cookie); +typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_remove_attr(void *ns, void *node, const char *name); +typedef int op_rename_attr(void *ns, void *node, const char *oldname, + const char *newname); +typedef int op_stat_attr(void *ns, void *node, const char *name, + struct attr_info *buf); + +typedef int op_write_attr(void *ns, void *node, const char *name, int type, + const void *buf, size_t *len, off_t pos); +typedef int op_read_attr(void *ns, void *node, const char *name, int type, + void *buf, size_t *len, off_t pos); + +typedef int op_open_indexdir(void *ns, void **cookie); +typedef int op_close_indexdir(void *ns, void *cookie); +typedef int op_rewind_indexdir(void *ns, void *cookie); +typedef int op_read_indexdir(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_create_index(void *ns, const char *name, int type, int flags); +typedef int op_remove_index(void *ns, const char *name); +typedef int op_rename_index(void *ns, const char *oldname, + const char *newname); +typedef int op_stat_index(void *ns, const char *name, struct index_info *buf); + +typedef int op_open_query(void *ns, const char *query, ulong flags, + port_id port, long token, void **cookie); +typedef int op_close_query(void *ns, void *cookie); +typedef int op_read_query(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef struct vnode_ops { + op_read_vnode (*read_vnode); + op_write_vnode (*write_vnode); + op_remove_vnode (*remove_vnode); + op_secure_vnode (*secure_vnode); + op_walk (*walk); + op_access (*access); + op_create (*create); + op_mkdir (*mkdir); + op_symlink (*symlink); + op_link (*link); + op_rename (*rename); + op_unlink (*unlink); + op_rmdir (*rmdir); + op_readlink (*readlink); + op_opendir (*opendir); + op_closedir (*closedir); + op_free_cookie (*free_dircookie); + op_rewinddir (*rewinddir); + op_readdir (*readdir); + op_open (*open); + op_close (*close); + op_free_cookie (*free_cookie); + op_read (*read); + op_write (*write); + op_readv (*readv); + op_writev (*writev); + op_ioctl (*ioctl); + op_setflags (*setflags); + op_rstat (*rstat); + op_wstat (*wstat); + op_fsync (*fsync); + op_initialize (*initialize); + op_mount (*mount); + op_unmount (*unmount); + op_sync (*sync); + op_rfsstat (*rfsstat); + op_wfsstat (*wfsstat); + op_select (*select); + op_deselect (*deselect); + op_open_indexdir (*open_indexdir); + op_close_indexdir (*close_indexdir); + op_free_cookie (*free_indexdircookie); + op_rewind_indexdir (*rewind_indexdir); + op_read_indexdir (*read_indexdir); + op_create_index (*create_index); + op_remove_index (*remove_index); + op_rename_index (*rename_index); + op_stat_index (*stat_index); + op_open_attrdir (*open_attrdir); + op_close_attrdir (*close_attrdir); + op_free_cookie (*free_attrdircookie); + op_rewind_attrdir (*rewind_attrdir); + op_read_attrdir (*read_attrdir); + op_write_attr (*write_attr); + op_read_attr (*read_attr); + op_remove_attr (*remove_attr); + op_rename_attr (*rename_attr); + op_stat_attr (*stat_attr); + op_open_query (*open_query); + op_close_query (*close_query); + op_free_cookie (*free_querycookie); + op_read_query (*read_query); +} vnode_ops; + +extern _IMPEXP_KERNEL int new_path(const char *path, char **copy); +extern _IMPEXP_KERNEL void free_path(char *p); + +extern _IMPEXP_KERNEL int notify_listener(int op, nspace_id nsid, + vnode_id vnida, vnode_id vnidb, + vnode_id vnidc, const char *name); +extern _IMPEXP_KERNEL int send_notification(port_id port, long token, + ulong what, long op, nspace_id nsida, + nspace_id nsidb, vnode_id vnida, + vnode_id vnidb, vnode_id vnidc, + const char *name); +extern _IMPEXP_KERNEL int get_vnode(nspace_id nsid, vnode_id vnid, void **data); +extern _IMPEXP_KERNEL int put_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int new_vnode(nspace_id nsid, vnode_id vnid, void *data); +extern _IMPEXP_KERNEL int remove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int unremove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int is_vnode_removed(nspace_id nsid, vnode_id vnid); + + +extern _EXPORT vnode_ops fs_entry; +extern _EXPORT int32 api_version; + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/iter.c b/src/tests/add-ons/kernel/file_systems/dos/r5/iter.c new file mode 100644 index 0000000000..6563ce9bd8 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/iter.c @@ -0,0 +1,310 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#include + +#include +#include +#include + +#include "iter.h" +#include "dosfs.h" +#include "fat.h" +#include "util.h" + +#define DPRINTF(a,b) if (debug_iter > (a)) dprintf b + +CHECK_MAGIC(diri,struct diri,DIRI_MAGIC) + +static int _validate_cs_(nspace *vol, uint32 cluster, uint32 sector) +{ + if (sector < 0) return -1; + + if ((vol->fat_bits != 32) && IS_FIXED_ROOT(cluster)) { // fat12 or fat16 root + if (sector >= vol->root_sectors) + return -1; + return 0; + } + + if (sector >= vol->sectors_per_cluster) return -1; + + if (!IS_DATA_CLUSTER(cluster)) return -1; + + return 0; +} + +static off_t _csi_to_block_(struct csi *csi) +{ + // presumes the caller has already called _validate_cs_ on the argument + ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0); + if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) + return EINVAL; + + if (IS_FIXED_ROOT(csi->cluster)) + return csi->vol->root_start + csi->sector; + + return csi->vol->data_start + + (off_t)(csi->cluster - 2)* csi->vol->sectors_per_cluster + + csi->sector; +} + +int init_csi(nspace *vol, uint32 cluster, uint32 sector, struct csi *csi) +{ + int ret; + if ((ret = _validate_cs_(vol,cluster,sector)) != 0) + return ret; + + csi->vol = vol; csi->cluster = cluster; csi->sector = sector; + + return 0; +} + +int iter_csi(struct csi *csi, int sectors) +{ + if (csi->sector == 0xffff) // check if already at end of chain + return -1; + + if (sectors < 0) + return EINVAL; + + if (sectors == 0) + return 0; + + if (IS_FIXED_ROOT(csi->cluster)) { + csi->sector += sectors; + if (csi->sector < csi->vol->root_sectors) + return 0; + } else { + csi->sector += sectors; + if (csi->sector < csi->vol->sectors_per_cluster) + return 0; + csi->cluster = get_nth_fat_entry(csi->vol, csi->cluster, csi->sector / csi->vol->sectors_per_cluster); + + if ((int32)csi->cluster < 0) { + csi->sector = 0xffff; + return csi->cluster; + } + + if (vIS_DATA_CLUSTER(csi->vol,csi->cluster)) { + csi->sector %= csi->vol->sectors_per_cluster; + return 0; + } + } + + csi->sector = 0xffff; + + return -1; +} + +uint8 *csi_get_block(struct csi *csi) +{ + if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) + return NULL; + + return get_block(csi->vol->fd, _csi_to_block_(csi), csi->vol->bytes_per_sector); +} + +status_t csi_release_block(struct csi *csi) +{ + status_t err; + + if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) + return EINVAL; + + err = release_block(csi->vol->fd, _csi_to_block_(csi)); + ASSERT(err == B_OK); + return err; +} + +status_t csi_mark_block_dirty(struct csi *csi) +{ + status_t err; + + ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0); + if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) + return EINVAL; + + err = mark_blocks_dirty(csi->vol->fd, _csi_to_block_(csi), 1); + ASSERT(err == B_OK); + return err; +} + +/* XXX: not the most efficient implementation, but it gets the job done */ +status_t csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len) +{ + struct csi old_csi; + uint32 sectors; + off_t block; + status_t err; + + ASSERT(len >= csi->vol->bytes_per_sector); + + if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) + return EINVAL; + + sectors = 1; + block = _csi_to_block_(csi); + + while (1) { + old_csi = *csi; + err = iter_csi(csi, 1); + if (len < (sectors + 1) * csi->vol->bytes_per_sector) + break; + if ((err < B_OK) || (block + sectors != _csi_to_block_(csi))) + break; + sectors++; + } + + err = cached_read(csi->vol->fd, block, buffer, sectors, csi->vol->bytes_per_sector); + if (err < B_OK) + return err; + + *csi = old_csi; + + return sectors * csi->vol->bytes_per_sector; +} + +status_t csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len) +{ + struct csi old_csi; + uint32 sectors; + off_t block; + status_t err; + + ASSERT(len >= csi->vol->bytes_per_sector); + + ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0); + if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) + return EINVAL; + + sectors = 1; + block = _csi_to_block_(csi); + + while (1) { + old_csi = *csi; + err = iter_csi(csi, 1); + if (len < (sectors + 1) * csi->vol->bytes_per_sector) + break; + if ((err < B_OK) || (block + sectors != _csi_to_block_(csi))) + break; + sectors++; + } + + err = cached_write(csi->vol->fd, block, buffer, sectors, csi->vol->bytes_per_sector); + if (err < B_OK) + return err; + + /* return the last state of the iterator because that's what dosfs_write + * expects. this lets it meaningfully cache the state even when it's + * writing to the end of the file. */ + *csi = old_csi; + + return sectors * csi->vol->bytes_per_sector; +} + +status_t csi_write_block(struct csi *csi, uint8 *buffer) +{ + ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0); + if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0) + return EINVAL; + + return cached_write(csi->vol->fd, _csi_to_block_(csi), buffer, 1, csi->vol->bytes_per_sector); +} + +static void _diri_release_current_block_(struct diri *diri) +{ + ASSERT(diri->current_block); + if (diri->current_block == NULL) + return; + csi_release_block(&(diri->csi)); + diri->current_block = NULL; +} + +uint8 *diri_init(nspace *vol, uint32 cluster, uint32 index, struct diri *diri) +{ + diri->magic = DIRI_MAGIC; + diri->current_block = NULL; + + if (cluster >= vol->total_clusters + 2) + return NULL; + + if (init_csi(vol,cluster,0,&(diri->csi)) != 0) + return NULL; + + diri->starting_cluster = cluster; + diri->current_index = index; + if (index >= vol->bytes_per_sector / 0x20) { + if (iter_csi(&(diri->csi), diri->current_index / (vol->bytes_per_sector / 0x20)) != 0) + return NULL; + } + + // get current sector + diri->current_block = csi_get_block(&(diri->csi)); + + if (diri->current_block == NULL) + return NULL; + + return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20; +} + +int diri_free(struct diri *diri) +{ + if (check_diri_magic(diri, "diri_free")) return EINVAL; + + diri->magic = ~DIRI_MAGIC; // trash magic number + + if (diri->current_block) + _diri_release_current_block_(diri); + + return 0; +} + +uint8 *diri_current_entry(struct diri *diri) +{ + if (check_diri_magic(diri, "diri_current_entry")) return NULL; + + if (diri->current_block == NULL) + return NULL; + + return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20; +} + +uint8 *diri_next_entry(struct diri *diri) +{ + if (check_diri_magic(diri, "diri_next_entry")) return NULL; + + if (diri->current_block == NULL) + return NULL; + + if ((++diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20)) == 0) { + _diri_release_current_block_(diri); + if (iter_csi(&(diri->csi), 1) != 0) + return NULL; + diri->current_block = csi_get_block(&(diri->csi)); + if (diri->current_block == NULL) + return NULL; + } + + return diri->current_block + (diri->current_index % (diri->csi.vol->bytes_per_sector / 0x20))*0x20; +} + +uint8 *diri_rewind(struct diri *diri) +{ + if (check_diri_magic(diri, "diri_rewind")) return NULL; + + if (diri->current_index > (diri->csi.vol->bytes_per_sector / 0x20 - 1)) { + if (diri->current_block) + _diri_release_current_block_(diri); + if (init_csi(diri->csi.vol, diri->starting_cluster, 0, &(diri->csi)) != 0) + return NULL; + diri->current_block = csi_get_block(&(diri->csi)); + } + diri->current_index = 0; + return diri->current_block; +} + +void diri_mark_dirty(struct diri *diri) +{ + csi_mark_block_dirty(&(diri->csi)); +} diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/iter.h b/src/tests/add-ons/kernel/file_systems/dos/r5/iter.h new file mode 100644 index 0000000000..ec6dafb0de --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/iter.h @@ -0,0 +1,47 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_ITER_H_ +#define _DOSFS_ITER_H_ + +struct _nspace; + +/* csi keeps track of current cluster and sector info */ +struct csi +{ + struct _nspace *vol; + uint32 cluster; + uint32 sector; +}; + +int init_csi(struct _nspace *vol, uint32 cluster, uint32 sector, struct csi *csi); +int iter_csi(struct csi *csi, int sectors); +uint8 *csi_get_block(struct csi *csi); +status_t csi_release_block(struct csi *csi); +status_t csi_mark_block_dirty(struct csi *csi); +status_t csi_read_blocks(struct csi *csi, uint8 *buffer, ssize_t len); +status_t csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len); +status_t csi_write_block(struct csi *csi, uint8 *buffer); + +/* directory entry iterator */ +#define DIRI_MAGIC '!duM' +struct diri +{ + uint32 magic; + struct csi csi; + uint32 starting_cluster; + uint32 current_index; + uint8 *current_block; +}; + +uint8 *diri_init(struct _nspace *vol, uint32 cluster, uint32 index, struct diri *diri); +int diri_free(struct diri *diri); +uint8 *diri_current_entry(struct diri *diri); +uint8 *diri_next_entry(struct diri *diri); +uint8 *diri_rewind(struct diri *diri); +void diri_mark_dirty(struct diri *diri); + +int check_diri_magic(struct diri *t, char *funcname); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/kalloc.h b/src/tests/add-ons/kernel/file_systems/dos/r5/kalloc.h new file mode 100644 index 0000000000..43c5bdfdef --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/kalloc.h @@ -0,0 +1,23 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _ALLOC_H +#define _ALLOC_H + +#include + +#if KMALLOC_TRACKING +extern bool kmalloc_tracking_enabled; +#endif + +extern void init_malloc(void); +extern void init_smalloc(void); + +extern void * smalloc(unsigned int nbytes); +extern void sfree(void *ptr); +extern void * scalloc(unsigned int nobj, unsigned int size); +extern void * srealloc(void *p, unsigned int newsize); + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/lock.h b/src/tests/add-ons/kernel/file_systems/dos/r5/lock.h new file mode 100644 index 0000000000..4014b752d4 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/lock.h @@ -0,0 +1,46 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _LOCK_H +#define _LOCK_H + +#include + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct lock lock; +typedef struct mlock mlock; + +struct lock { + sem_id s; + long c; +}; + +struct mlock { + sem_id s; +}; + +extern _IMPEXP_KERNEL int new_lock(lock *l, const char *name); +extern _IMPEXP_KERNEL int free_lock(lock *l); + +#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s); +#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s); + +extern _IMPEXP_KERNEL int new_mlock(mlock *l, long c, const char *name); +extern _IMPEXP_KERNEL int free_mlock(mlock *l); + +#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0) +#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0) + + +#ifdef __cplusplus + } // extern "C" +#endif + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.c b/src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.c new file mode 100644 index 0000000000..7bb66e72ac --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.c @@ -0,0 +1,105 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + extended: 2001-12-11 by Marcus Overhagen +*/ + +struct ext_mime { + char *extension; + char *mime; +}; + +struct ext_mime mimes[] = { + { "gz", "application/x-gzip" }, + { "hqx", "application/x-binhex40" }, + { "lha", "application/x-lharc" }, + { "lzh", "application/x-lharc" }, + { "pcl", "application/x-pcl" }, + { "pdf", "application/pdf" }, + { "ps", "application/postscript" }, + { "sit", "application/x-stuff-it" }, + { "tar", "application/x-tar" }, + { "tgz", "application/x-gzip" }, + { "uue", "application/x-uuencode" }, + { "z", "application/x-compress" }, + { "zip", "application/zip" }, + { "zoo", "application/x-zoo" }, + { "pkg", "application/x-scode-UPkg" }, + { "vdwn", "application/x-scode-UPkg" }, + { "proj", "application/x-mw-project" }, + { "swf", "application/x-shockwave-flash" }, + { "clp", "application/x-codeliege-project" }, + + { "aif", "audio/x-aiff" }, + { "aifc", "audio/x-aifc" }, + { "aiff", "audio/x-aiff" }, + { "au", "audio/basic" }, + { "mid", "audio/x-midi" }, + { "midi", "audio/x-midi" }, + { "mod", "audio/mod" }, + { "ra", "audio/x-real-audio" }, + { "wav", "audio/x-wav" }, + { "mp2", "audio/x-mpeg" }, + { "mp3", "audio/x-mpeg" }, + { "ogg", "audio/x-vorbis" }, + { "mpc", "audio/x-mpc" }, + { "asf", "application/x-asf" }, + { "riff", "application/x-riff" }, + { "wma", "audio/x-ms-wma" }, + + { "bmp", "image/x-bmp" }, + { "fax", "image/g3fax" }, + { "gif", "image/gif" }, + { "iff", "image/x-iff" }, + { "jpg", "image/jpeg" }, + { "jpeg", "image/jpeg" }, + { "pbm", "image/x-portable-bitmap" }, + { "pcx", "image/x-pcx" }, + { "pgm", "image/x-portable-graymap" }, + { "png", "image/png" }, + { "ppm", "image/x-portable-pixmap" }, + { "rgb", "image/x-rgb" }, + { "tga", "image/x-targa" }, + { "tif", "image/tiff" }, + { "tiff", "image/tiff" }, + { "xbm", "image/x-xbitmap" }, + + { "txt", "text/plain" }, + { "ini", "text/plain" }, + { "log", "text/plain" }, + { "bat", "text/plain" }, + { "doc", "text/plain" }, + { "cfg", "text/plain" }, + { "inf", "text/plain" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "rtf", "text/rtf" }, + { "c", "text/x-source-code" }, + { "cc", "text/x-source-code" }, + { "c++", "text/x-source-code" }, + { "h", "text/x-source-code" }, + { "h++", "text/x-source-code" }, + { "hh", "text/x-source-code" }, + { "hpp", "text/x-source-code" }, + { "pl", "text/x-source-code" }, + { "py", "text/x-source-code" }, + { "cxx", "text/x-source-code" }, + { "cpp", "text/x-source-code" }, + { "S", "text/x-source-code" }, + { "asm", "text/x-source-code" }, + { "bas", "text/x-source-code" }, + { "pas", "text/x-source-code" }, + { "java", "text/x-source-code" }, + + { "avi", "video/x-msvideo" }, + { "mov", "video/quicktime" }, + { "mpg", "video/mpeg" }, + { "mpeg", "video/mpeg" }, + { "ogm", "video/x-ogm" }, + { "wmv", "video/x-ms-wmv" }, + { "rm", "application/vnd.rn-realmedia" }, + { "rn", "application/vnd.rn-realmedia" }, + + { 0, 0 } +}; diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.h b/src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.h new file mode 100644 index 0000000000..1a888231d8 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/mime_table.h @@ -0,0 +1,16 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef MIME_TYPES_H +#define MIME_TYPES_H + +struct ext_mime { + char *extension; + char *mime; +}; + +extern struct ext_mime mimes[]; + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/rtc_info.h b/src/tests/add-ons/kernel/file_systems/dos/r5/rtc_info.h new file mode 100644 index 0000000000..024acbf2ae --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/rtc_info.h @@ -0,0 +1,23 @@ +/* ++++++++++ + FILE: rtc_info.h + NAME: mani + DATE: 2/1998 + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. ++++++ */ + +#ifndef _RTC_INFO_H +#define _RTC_INFO_H + +typedef struct { + uint32 time; + bool is_gmt; + int32 tz_minuteswest; + int32 tz_dsttime; +} rtc_info; + +#define RTC_SETTINGS_FILE "RTC_time_settings" + +extern _IMPEXP_KERNEL status_t get_rtc_info(rtc_info *); + +#endif /* _RTC_INFO_H */ diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/settings/dos b/src/tests/add-ons/kernel/file_systems/dos/r5/settings/dos new file mode 100644 index 0000000000..337f222fb2 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/settings/dos @@ -0,0 +1,17 @@ +# Sample settings file for the dosfs plugin +# +# This file should be moved to the directory +# /boot/home/config/settings/kernel/drivers/ +# +# lock device: +# true = (default) locks the device's door if: +# the filesystem is RW +# the device is removable +# false = no door locking +lock_device true + +# sync mode: +# 0 = (default) no sync after each operation +# 1 = sync after each operation on removable media only +# 2 = sync after each operation always +op_sync_mode 0 diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/util.c b/src/tests/add-ons/kernel/file_systems/dos/r5/util.c new file mode 100644 index 0000000000..86cf91e2e9 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/util.c @@ -0,0 +1,149 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#include +#include + +#include +#include +#include + +#include +#include + +#include "dosfs.h" +#include "fat.h" +#include "util.h" + +static int32 tzoffset = -1; /* in minutes */ + +int _assert_(char *a, int b, char *c) +{ + dprintf("tripped assertion in %s/%d (%s)\n", a, b, c); + kernel_debugger("tripped assertion"); + return 0; +} + +static void print_byte(uint8 c) +{ + dprintf("%c", ((c >= ' ') && (c <= '~')) ? c : '.'); +} + +void dump_bytes(uint8 *buffer, uint32 count) +{ + uint32 i, j, k; + for (i=0;i<0x10;i++) + dprintf(" %lX ", i); + dprintf("\n"); + for (i=0;i count) ? count - i : 0x10; + for (k=i;k= 100) result++; // correct for 2000 + if (IS_LEAP_YEAR(yr + 1970)) + if (mon < 2) result--; + return result; +} + +static int daze[] = { 0,0,31,59,90,120,151,181,212,243,273,304,334,0,0,0 }; + +time_t dos2time_t(uint32 t) +{ + time_t days; + + get_tzoffset(); + + //dprintf("%d/%d/%d %d:%2.2d:%2.2d\n", + // (t>>25)+1980,((t>>21)&15),((t>>16)&31), + // (t>>11)&31,(t>>5)&63,2*(t&31)); + + days = daze[(t>>21)&15] + ((t>>25)+10)*365 + leaps((t>>25)+10,((t>>21)&15)-1)+((t>>16)&31)-1; + + return (((days * 24) + ((t>>11)&31)) * 60 + ((t>>5)&63) + tzoffset) * 60 + 2*(t&31); +} + +uint32 time_t2dos(time_t s) +{ + uint32 t, d, y; + int days; + + get_tzoffset(); + + t = (s % 60) / 2; s /= 60; s -= tzoffset; + t += (s % 60) << 5; s /= 60; + t += (s % 24) << 11;s /= 24; + + s -= 10*365 + 2; // convert from 1970-based year to 1980-based year + + for (y=0;;y++) { + days = IS_LEAP_YEAR(1980+y) ? 366 : 365; + if (s < days) break; + s -= days; + } + + if (IS_LEAP_YEAR(1980+y)) { + if (s == 59) { + d = (1 << 5) + 28; /* 2/29, 0 based */ + goto bi; + } else if (s > 59) + s--; + } + + for (d=0;d<11;d++) + if (daze[d+2] > s) + break; + d = (d << 5) + (s - daze[d+1]); + +bi: + d += (1 << 5) + 1; // make date 1-based + + return t + (d << 16) + (y << 25); +} + +uint8 hash_msdos_name(const char *name) +{ + const uint8 *p = (const uint8 *)name; + int i; + uint8 c = 0; + for (i=0;i<11;i++) + c = (c << 7) + (c >> 1) + *(p++); + return c; +} diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/util.h b/src/tests/add-ons/kernel/file_systems/dos/r5/util.h new file mode 100644 index 0000000000..7ff13912f3 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/util.h @@ -0,0 +1,43 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_UTIL_H_ +#define _DOSFS_UTIL_H_ + +#include + +// debugging functions + +#ifndef DEBUG +#define ASSERT(c) ((void)0) +#else +int _assert_(char *,int,char *); +#define ASSERT(c) (!(c) ? _assert_(__FILE__,__LINE__,#c) : 0) +#endif + +void dump_bytes(uint8 *buffer, uint32 count); +void dump_directory(uint8 *buffer); + +// time +time_t dos2time_t(uint32 t); +uint32 time_t2dos(time_t s); + +uint8 hash_msdos_name(const char *name); + +#if 0 +#define read32(buffer,off) \ + (((uint8 *)buffer)[(off)] + (((uint8 *)buffer)[(off)+1] << 8) + \ + (((uint8 *)buffer)[(off)+2] << 16) + (((uint8 *)buffer)[(off)+3] << 24)) + +#define read16(buffer,off) \ + (((uint8 *)buffer)[(off)] + (((uint8 *)buffer)[(off)+1] << 8)) +#endif + +#define read32(buffer,off) \ + B_LENDIAN_TO_HOST_INT32(*(uint32 *)&buffer[off]) + +#define read16(buffer,off) \ + B_LENDIAN_TO_HOST_INT16(*(uint16 *)&buffer[off]) + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/vcache.c b/src/tests/add-ons/kernel/file_systems/dos/r5/vcache.c new file mode 100644 index 0000000000..8bb1ac0517 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/vcache.c @@ -0,0 +1,443 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* +The FAT file system has no good way of assigning unique persistent values to +nodes. The only obvious choice, storing the starting cluster number of the +file, is unusable because 0 byte files exist as directory entries only. +Further, even if it were usable, it would potentially require a full directory +tree traversal to locate an arbitrary node. We must resort to some ickiness +in order to make persistent vnode id's (at least across a given mount) work. + +There are three ways to encode a vnode id: + +1. Combine the starting cluster of the entry with the starting cluster of the + directory it appears in. This is used for files with data. +2. Combine the starting cluster of the directory the entry appears in with the + index of the entry in the directory. This is used for 0-byte files. +3. A unique number that doesn't match any possible values from encodings 1 or + 2. + +With the first encoding, the vnode id is invalidated (i.e. no longer describes +the file's location) when the file moves to a different directory or when +its starting cluster changes (this can occur if the file is truncated and data +is subsequently written to it). + +With the second encoding, the vnode id is invalidated when the file position +is moved within a directory (as a result of a renaming), when it's moved to a +different directory, or when data is written to it. + +The third encoding doesn't describe the file's location on disk, and so it is +invalid from the start. + +Since we can't change vnode id's once they are assigned, we have to create a +mapping table to translate vnode id's to locations. This file serves this +purpose. +*/ + +#define DPRINTF(a,b) if (debug_vcache > (a)) dprintf b + +#define LOCK_CACHE_R \ + acquire_sem(vol->vcache.vc_sem) + +#define LOCK_CACHE_W \ + acquire_sem_etc(vol->vcache.vc_sem, READERS, 0, 0) + +#define UNLOCK_CACHE_R \ + release_sem(vol->vcache.vc_sem) + +#define UNLOCK_CACHE_W \ + release_sem_etc(vol->vcache.vc_sem, READERS, 0) + +#include +#include + +#include +#include +#include + +#include "dosfs.h" +#include "vcache.h" + +#include "util.h" + +struct vcache_entry { + vnode_id vnid; /* originally reported vnid */ + vnode_id loc; /* where the file is now */ + struct vcache_entry *next_vnid; /* next entry in vnid hash table */ + struct vcache_entry *next_loc; /* next entry in location hash table */ +}; + +void dump_vcache(nspace *vol) +{ + uint32 i; + struct vcache_entry *c; + dprintf("vnid cache size %lx, cur vnid = %Lx\n" + "vnid loc\n", + vol->vcache.cache_size, vol->vcache.cur_vnid); + for (i=0;ivcache.cache_size;i++) + for (c = vol->vcache.by_vnid[i];c;c=c->next_vnid) + dprintf("%16Lx %16Lx\n", c->vnid, c->loc); +} + +#define hash(v) ((v) & (vol->vcache.cache_size-1)) + +status_t init_vcache(nspace *vol) +{ + char name[16]; + DPRINTF(0, ("init_vcache called\n")); + + vol->vcache.cur_vnid = ARTIFICIAL_VNID_BITS; +#if DEBUG + vol->vcache.cache_size = 1; +#else + vol->vcache.cache_size = 512; /* must be power of 2 */ +#endif + vol->vcache.by_vnid = calloc(sizeof(struct vache_entry *), vol->vcache.cache_size); + if (vol->vcache.by_vnid == NULL) { + dprintf("init_vcache: out of core\n"); + return ENOMEM; + } + vol->vcache.by_loc = calloc(sizeof(struct vache_entry *), vol->vcache.cache_size); + if (vol->vcache.by_loc == NULL) { + dprintf("init_vcache: out of core\n"); + free(vol->vcache.by_vnid); + vol->vcache.by_vnid = NULL; + return ENOMEM; + } + + sprintf(name, "fat cache %lx", vol->id); + if ((vol->vcache.vc_sem = create_sem(READERS, name)) < 0) { + free(vol->vcache.by_vnid); vol->vcache.by_vnid = NULL; + free(vol->vcache.by_loc); vol->vcache.by_loc = NULL; + return vol->vcache.vc_sem; + } + + DPRINTF(0, ("init_vcache: initialized vnid cache with %lx entries\n", vol->vcache.cache_size)); + + return 0; +} + +status_t uninit_vcache(nspace *vol) +{ + uint32 i, count = 0; + struct vcache_entry *c, *n; + DPRINTF(0, ("uninit_vcache called\n")); + + LOCK_CACHE_W; + + /* free entries */ + for (i=0;ivcache.cache_size;i++) { + c = vol->vcache.by_vnid[i]; + while (c) { + count++; + n = c->next_vnid; + free(c); + c = n; + } + } + + DPRINTF(0, ("%lx vcache entries removed\n", count)); + + free(vol->vcache.by_vnid); vol->vcache.by_vnid = NULL; + free(vol->vcache.by_loc); vol->vcache.by_loc = NULL; + + delete_sem(vol->vcache.vc_sem); + + return 0; +} + +vnode_id generate_unique_vnid(nspace *vol) +{ + DPRINTF(0, ("generate_unique_vnid\n")); + /* only one thread per volume will be in here at any given time anyway + * due to volume locking */ + return vol->vcache.cur_vnid++; +} + +static status_t _add_to_vcache_(nspace *vol, vnode_id vnid, vnode_id loc) +{ + int hash1 = hash(vnid), hash2 = hash(loc); + struct vcache_entry *e, *c, *p; + + DPRINTF(0, ("add_to_vcache %Lx/%Lx\n", vnid, loc)); + + ASSERT(vnid != loc); + + e = malloc(sizeof(struct vcache_entry)); + if (e == NULL) + return ENOMEM; + + e->vnid = vnid; e->loc = loc; e->next_vnid = NULL; e->next_loc = NULL; + + c = p = vol->vcache.by_vnid[hash1]; + while (c) { + if (vnid < c->vnid) + break; + ASSERT(vnid != c->vnid); ASSERT(loc != c->loc); + p = c; + c = c->next_vnid; + } + ASSERT(!c || (vnid != c->vnid)); + + e->next_vnid = c; + if (p == c) + vol->vcache.by_vnid[hash1] = e; + else + p->next_vnid = e; + + c = p = vol->vcache.by_loc[hash2]; + while (c) { + if (loc < c->loc) + break; + ASSERT(vnid != c->vnid); ASSERT(loc != c->loc); + p = c; + c = c->next_loc; + } + ASSERT(!c || (loc != c->loc)); + + e->next_loc = c; + if (p == c) + vol->vcache.by_loc[hash2] = e; + else + p->next_loc = e; + + return B_OK; +} + +static status_t _remove_from_vcache_(nspace *vol, vnode_id vnid) +{ + int hash1 = hash(vnid), hash2; + struct vcache_entry *c, *p, *e; + + DPRINTF(0, ("remove_from_vcache %Lx\n", vnid)); + + c = p = vol->vcache.by_vnid[hash1]; + while (c) { + if (vnid == c->vnid) + break; + ASSERT(c->vnid < vnid); + p = c; + c = c->next_vnid; + } + ASSERT(c); + if (!c) return ENOENT; + + if (p == c) + vol->vcache.by_vnid[hash1] = c->next_vnid; + else + p->next_vnid = c->next_vnid; + + e = c; + + hash2 = hash(c->loc); + c = p = vol->vcache.by_loc[hash2]; + + while (c) { + if (vnid == c->vnid) + break; + ASSERT(c->loc < e->loc); + p = c; + c = c->next_loc; + } + ASSERT(c); + if (!c) return ENOENT; + if (p == c) + vol->vcache.by_loc[hash2] = c->next_loc; + else + p->next_loc = c->next_loc; + + free(c); + + return 0; +} + +static struct vcache_entry *_find_vnid_in_vcache_(nspace *vol, vnode_id vnid) +{ + int hash1 = hash(vnid); + struct vcache_entry *c; + c = vol->vcache.by_vnid[hash1]; + while (c) { + if (c->vnid == vnid) + break; + if (c->vnid > vnid) + return NULL; + c = c->next_vnid; + } + + return c; +} + +static struct vcache_entry *_find_loc_in_vcache_(nspace *vol, vnode_id loc) +{ + int hash2 = hash(loc); + struct vcache_entry *c; + c = vol->vcache.by_loc[hash2]; + while (c) { + if (c->loc == loc) + break; + if (c->loc > loc) + return NULL; + c = c->next_loc; + } + + return c; +} + +status_t add_to_vcache(nspace *vol, vnode_id vnid, vnode_id loc) +{ + status_t result; + + LOCK_CACHE_W; + result = _add_to_vcache_(vol,vnid,loc); + UNLOCK_CACHE_W; + + if (result < 0) DPRINTF(0, ("add_to_vcache failed (%s)\n", strerror(result))); + return result; +} + +/* XXX: do this in a smarter fashion */ +static status_t _update_loc_in_vcache_(nspace *vol, vnode_id vnid, vnode_id loc) +{ + status_t result; + + result = _remove_from_vcache_(vol, vnid); + if (result == 0) + result = _add_to_vcache_(vol, vnid, loc); + + return result; +} + +status_t remove_from_vcache(nspace *vol, vnode_id vnid) +{ + status_t result; + + LOCK_CACHE_W; + result = _remove_from_vcache_(vol, vnid); + UNLOCK_CACHE_W; + + if (result < 0) DPRINTF(0, ("remove_from_vcache failed (%s)\n", strerror(result))); + return result; +} + +status_t vcache_vnid_to_loc(nspace *vol, vnode_id vnid, vnode_id *loc) +{ + struct vcache_entry *e; + + DPRINTF(1, ("vcache_vnid_to_loc %Lx %lx\n", vnid, (uint32)loc)); + + LOCK_CACHE_R; + e = _find_vnid_in_vcache_(vol, vnid); + if (loc && e) + *loc = e->loc; + UNLOCK_CACHE_R; + + return (e) ? B_OK : ENOENT; +} + +status_t vcache_loc_to_vnid(nspace *vol, vnode_id loc, vnode_id *vnid) +{ + struct vcache_entry *e; + + DPRINTF(1, ("vcache_loc_to_vnid %Lx %lx\n", loc, (uint32)vnid)); + + LOCK_CACHE_R; + e = _find_loc_in_vcache_(vol, loc); + if (vnid && e) + *vnid = e->vnid; + UNLOCK_CACHE_R; + + return (e) ? B_OK : ENOENT; +} + +status_t vcache_set_entry(nspace *vol, vnode_id vnid, vnode_id loc) +{ + struct vcache_entry *e; + status_t result = B_OK; + + DPRINTF(0, ("vcache_set_entry: %Lx -> %Lx\n", vnid, loc)); + + if (is_vnode_removed(vol->id, vnid) > 0) { + if (!IS_ARTIFICIAL_VNID(loc)) + return B_OK; + } else { + ASSERT(is_vnode_removed(vol->id, vnid) == 0); + } + + LOCK_CACHE_W; + + e = _find_vnid_in_vcache_(vol, vnid); + + if (e) { + if (e->vnid == loc) + result = _remove_from_vcache_(vol, vnid); + else + result = _update_loc_in_vcache_(vol, vnid, loc); + } else { + if (vnid != loc) + result = _add_to_vcache_(vol,vnid,loc); + } + + UNLOCK_CACHE_W; + + return result; +} + +#if DEBUG + +int debug_dfvnid(int argc, char **argv) +{ + int i; + nspace *vol; + + if (argc < 3) { + kprintf("dfvnid nspace vnid\n"); + return B_OK; + } + + vol = (nspace *)strtoul(argv[1], NULL, 0); + if (vol == NULL) + return B_OK; + + for (i=2;i loc %Lx @ %p\n", vnid, e->loc, e); + } else { + kprintf("vnid %Lx not found in vnid cache\n", vnid); + } + } + + return B_OK; +} + +int debug_dfloc(int argc, char **argv) +{ + int i; + nspace *vol; + + if (argc < 3) { + kprintf("dfloc nspace vnid\n"); + return B_OK; + } + + vol = (nspace *)strtoul(argv[1], NULL, 0); + if (vol == NULL) + return B_OK; + + for (i=2;i vnid %Lx @ %p\n", loc, e->vnid, e); + } else { + kprintf("loc %Lx not found in vnid cache\n", loc); + } + } + + return B_OK; +} + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/vcache.h b/src/tests/add-ons/kernel/file_systems/dos/r5/vcache.h new file mode 100644 index 0000000000..25c072ba5f --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/vcache.h @@ -0,0 +1,30 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _DOSFS_VCACHE_H_ +#define _DOSFS_VCACHE_H_ + +void dump_vcache(nspace *vol); + +status_t init_vcache(nspace *vol); +status_t uninit_vcache(nspace *vol); + +vnode_id generate_unique_vnid(nspace *vol); + +status_t add_to_vcache(nspace *vol, vnode_id vnid, vnode_id loc); +status_t remove_from_vcache(nspace *vol, vnode_id vnid); +status_t vcache_vnid_to_loc(nspace *vol, vnode_id vnid, vnode_id *loc); +status_t vcache_loc_to_vnid(nspace *vol, vnode_id loc, vnode_id *vnid); + +status_t vcache_set_entry(nspace *vol, vnode_id vnid, vnode_id loc); + +#define find_vnid_in_vcache(vol,vnid) vcache_vnid_to_loc(vol,vnid,NULL) +#define find_loc_in_vcache(vol,loc) vcache_loc_to_vnid(vol,loc,NULL) + +#if DEBUG + int debug_dfvnid(int argc, char **argv); + int debug_dfloc(int argc, char **argv); +#endif + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/dos/r5/version.c b/src/tests/add-ons/kernel/file_systems/dos/r5/version.c new file mode 100644 index 0000000000..ced947f767 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/dos/r5/version.c @@ -0,0 +1,6 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +const char *build_time = __TIME__; +const char *build_date = __DATE__;